1 /*
2  * Copyright (C) 2017 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 package com.android.internal.widget;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.content.Context;
25 import android.content.res.TypedArray;
26 import android.database.Observable;
27 import android.graphics.Canvas;
28 import android.graphics.Matrix;
29 import android.graphics.PointF;
30 import android.graphics.Rect;
31 import android.graphics.RectF;
32 import android.os.Build;
33 import android.os.Bundle;
34 import android.os.Parcel;
35 import android.os.Parcelable;
36 import android.os.SystemClock;
37 import android.os.Trace;
38 import android.util.AttributeSet;
39 import android.util.Log;
40 import android.util.SparseArray;
41 import android.util.TypedValue;
42 import android.view.AbsSavedState;
43 import android.view.Display;
44 import android.view.FocusFinder;
45 import android.view.InputDevice;
46 import android.view.MotionEvent;
47 import android.view.VelocityTracker;
48 import android.view.View;
49 import android.view.ViewConfiguration;
50 import android.view.ViewGroup;
51 import android.view.ViewParent;
52 import android.view.accessibility.AccessibilityEvent;
53 import android.view.accessibility.AccessibilityManager;
54 import android.view.accessibility.AccessibilityNodeInfo;
55 import android.view.animation.Interpolator;
56 import android.widget.EdgeEffect;
57 import android.widget.OverScroller;
58 
59 import com.android.internal.R;
60 import com.android.internal.annotations.VisibleForTesting;
61 import com.android.internal.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
62 
63 import java.lang.annotation.Retention;
64 import java.lang.annotation.RetentionPolicy;
65 import java.lang.ref.WeakReference;
66 import java.lang.reflect.Constructor;
67 import java.lang.reflect.InvocationTargetException;
68 import java.util.ArrayList;
69 import java.util.Collections;
70 import java.util.List;
71 
72 /**
73  * A flexible view for providing a limited window into a large data set.
74  *
75  * <h3>Glossary of terms:</h3>
76  *
77  * <ul>
78  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
79  *     that represent items in a data set.</li>
80  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
81  *     <li><em>Index:</em> The index of an attached child view as used in a call to
82  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
83  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
84  *     to a <em>position</em> within the adapter.</li>
85  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
86  *     position may be placed in a cache for later reuse to display the same type of data again
87  *     later. This can drastically improve performance by skipping initial layout inflation
88  *     or construction.</li>
89  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
90  *     state during layout. Scrap views may be reused without becoming fully detached
91  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
92  *     by the adapter if the view was considered <em>dirty</em>.</li>
93  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
94  *     being displayed.</li>
95  * </ul>
96  *
97  * <h4>Positions in RecyclerView:</h4>
98  * <p>
99  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
100  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
101  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
102  * It also helps with performance because all view bindings happen at the same time and unnecessary
103  * bindings are avoided.
104  * <p>
105  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
106  * <ul>
107  *     <li>layout position: Position of an item in the latest layout calculation. This is the
108  *     position from the LayoutManager's perspective.</li>
109  *     <li>adapter position: Position of an item in the adapter. This is the position from
110  *     the Adapter's perspective.</li>
111  * </ul>
112  * <p>
113  * These two positions are the same except the time between dispatching <code>adapter.notify*
114  * </code> events and calculating the updated layout.
115  * <p>
116  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
117  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
118  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
119  * last layout calculation. You can rely on these positions to be consistent with what user is
120  * currently seeing on the screen. For example, if you have a list of items on the screen and user
121  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
122  * is seeing.
123  * <p>
124  * The other set of position related methods are in the form of
125  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
126  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
127  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
128  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
129  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
130  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
131  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
132  * <code>null</code> results from these methods.
133  * <p>
134  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
135  * writing an {@link Adapter}, you probably want to use adapter positions.
136  */
137 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
138 
139     static final String TAG = "RecyclerView";
140 
141     static final boolean DEBUG = false;
142 
143     private static final int[]  NESTED_SCROLLING_ATTRS = { android.R.attr.nestedScrollingEnabled };
144 
145     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
146 
147     /**
148      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
149      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
150      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
151      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
152      * this criteria.
153      */
154     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
155             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
156     /**
157      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
158      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
159      * 0 when mode is unspecified.
160      */
161     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
162 
163     static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
164 
165     /**
166      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
167      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
168      */
169     private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
170 
171     /**
172      * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
173      * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
174      */
175     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
176 
177     /**
178      * on API 15-, a focused child can still be considered a focused child of RV even after
179      * it's being removed or its focusable flag is set to false. This is because when this focused
180      * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
181      * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
182      * to request focus on a new child, which will clear the focus on the old (detached) child as a
183      * side-effect.
184      */
185     private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
186 
187     static final boolean DISPATCH_TEMP_DETACH = false;
188     public static final int HORIZONTAL = 0;
189     public static final int VERTICAL = 1;
190 
191     public static final int NO_POSITION = -1;
192     public static final long NO_ID = -1;
193     public static final int INVALID_TYPE = -1;
194 
195     /**
196      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
197      * that the RecyclerView should use the standard touch slop for smooth,
198      * continuous scrolling.
199      */
200     public static final int TOUCH_SLOP_DEFAULT = 0;
201 
202     /**
203      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
204      * that the RecyclerView should use the standard touch slop for scrolling
205      * widgets that snap to a page or other coarse-grained barrier.
206      */
207     public static final int TOUCH_SLOP_PAGING = 1;
208 
209     static final int MAX_SCROLL_DURATION = 2000;
210 
211     /**
212      * RecyclerView is calculating a scroll.
213      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
214      * it. Try to avoid using EditText, focusable views or handle them with care.
215      */
216     static final String TRACE_SCROLL_TAG = "RV Scroll";
217 
218     /**
219      * OnLayout has been called by the View system.
220      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
221      * update themselves directly. This will cause a full re-layout but when it happens via the
222      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
223      */
224     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
225 
226     /**
227      * NotifyDataSetChanged or equal has been called.
228      * If this is taking a long time, try sending granular notify adapter changes instead of just
229      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
230      * might help.
231      */
232     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
233 
234     /**
235      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
236      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
237      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
238      * methods.
239      */
240     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
241 
242     /**
243      * RecyclerView is rebinding a View.
244      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
245      * doing extra operations in onBindViewHolder call.
246      */
247     static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
248 
249     /**
250      * RecyclerView is attempting to pre-populate off screen views.
251      */
252     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
253 
254     /**
255      * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
256      * RecyclerView.
257      */
258     static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
259 
260     /**
261      * RecyclerView is creating a new View.
262      * If too many of these present in Systrace:
263      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
264      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
265      * > Adapter#onFailedToRecycleView(ViewHolder)})
266      *
267      * - There might be too many item view types.
268      * > Try merging them
269      *
270      * - There might be too many itemChange animations and not enough space in RecyclerPool.
271      * >Try increasing your pool size and item cache size.
272      */
273     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
274     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
275             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
276 
277     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
278 
279     final Recycler mRecycler = new Recycler();
280 
281     private SavedState mPendingSavedState;
282 
283     /**
284      * Handles adapter updates
285      */
286     AdapterHelper mAdapterHelper;
287 
288     /**
289      * Handles abstraction between LayoutManager children and RecyclerView children
290      */
291     ChildHelper mChildHelper;
292 
293     /**
294      * Keeps data about views to be used for animations
295      */
296     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
297 
298     /**
299      * Prior to L, there is no way to query this variable which is why we override the setter and
300      * track it here.
301      */
302     boolean mClipToPadding;
303 
304     /**
305      * Note: this Runnable is only ever posted if:
306      * 1) We've been through first layout
307      * 2) We know we have a fixed size (mHasFixedSize)
308      * 3) We're attached
309      */
310     final Runnable mUpdateChildViewsRunnable = new Runnable() {
311         @Override
312         public void run() {
313             if (!mFirstLayoutComplete || isLayoutRequested()) {
314                 // a layout request will happen, we should not do layout here.
315                 return;
316             }
317             if (!mIsAttached) {
318                 requestLayout();
319                 // if we are not attached yet, mark us as requiring layout and skip
320                 return;
321             }
322             if (mLayoutFrozen) {
323                 mLayoutRequestEaten = true;
324                 return; //we'll process updates when ice age ends.
325             }
326             consumePendingUpdateOperations();
327         }
328     };
329 
330     final Rect mTempRect = new Rect();
331     private final Rect mTempRect2 = new Rect();
332     final RectF mTempRectF = new RectF();
333     Adapter mAdapter;
334     @VisibleForTesting LayoutManager mLayout;
335     RecyclerListener mRecyclerListener;
336     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
337     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
338             new ArrayList<>();
339     private OnItemTouchListener mActiveOnItemTouchListener;
340     boolean mIsAttached;
341     boolean mHasFixedSize;
342     @VisibleForTesting boolean mFirstLayoutComplete;
343 
344     // Counting lock to control whether we should ignore requestLayout calls from children or not.
345     private int mEatRequestLayout = 0;
346 
347     boolean mLayoutRequestEaten;
348     boolean mLayoutFrozen;
349     private boolean mIgnoreMotionEventTillDown;
350 
351     // binary OR of change events that were eaten during a layout or scroll.
352     private int mEatenAccessibilityChangeFlags;
353     boolean mAdapterUpdateDuringMeasure;
354 
355     private final AccessibilityManager mAccessibilityManager;
356     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
357 
358     /**
359      * Set to true when an adapter data set changed notification is received.
360      * In that case, we cannot run any animations since we don't know what happened until layout.
361      *
362      * Attached items are invalid until next layout, at which point layout will animate/replace
363      * items as necessary, building up content from the (effectively) new adapter from scratch.
364      *
365      * Cached items must be discarded when setting this to true, so that the cache may be freely
366      * used by prefetching until the next layout occurs.
367      *
368      * @see #setDataSetChangedAfterLayout()
369      */
370     boolean mDataSetHasChangedAfterLayout = false;
371 
372     /**
373      * This variable is incremented during a dispatchLayout and/or scroll.
374      * Some methods should not be called during these periods (e.g. adapter data change).
375      * Doing so will create hard to find bugs so we better check it and throw an exception.
376      *
377      * @see #assertInLayoutOrScroll(String)
378      * @see #assertNotInLayoutOrScroll(String)
379      */
380     private int mLayoutOrScrollCounter = 0;
381 
382     /**
383      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
384      * (for API compatibility).
385      * <p>
386      * It is a bad practice for a developer to update the data in a scroll callback since it is
387      * potentially called during a layout.
388      */
389     private int mDispatchScrollCounter = 0;
390 
391     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
392 
393     ItemAnimator mItemAnimator = new DefaultItemAnimator();
394 
395     private static final int INVALID_POINTER = -1;
396 
397     /**
398      * The RecyclerView is not currently scrolling.
399      * @see #getScrollState()
400      */
401     public static final int SCROLL_STATE_IDLE = 0;
402 
403     /**
404      * The RecyclerView is currently being dragged by outside input such as user touch input.
405      * @see #getScrollState()
406      */
407     public static final int SCROLL_STATE_DRAGGING = 1;
408 
409     /**
410      * The RecyclerView is currently animating to a final position while not under
411      * outside control.
412      * @see #getScrollState()
413      */
414     public static final int SCROLL_STATE_SETTLING = 2;
415 
416     static final long FOREVER_NS = Long.MAX_VALUE;
417 
418     // Touch/scrolling handling
419 
420     private int mScrollState = SCROLL_STATE_IDLE;
421     private int mScrollPointerId = INVALID_POINTER;
422     private VelocityTracker mVelocityTracker;
423     private int mInitialTouchX;
424     private int mInitialTouchY;
425     private int mLastTouchX;
426     private int mLastTouchY;
427     private int mTouchSlop;
428     private OnFlingListener mOnFlingListener;
429     private final int mMinFlingVelocity;
430     private final int mMaxFlingVelocity;
431     // This value is used when handling generic motion events.
432     private float mScrollFactor = Float.MIN_VALUE;
433     private boolean mPreserveFocusAfterLayout = true;
434 
435     final ViewFlinger mViewFlinger = new ViewFlinger();
436 
437     GapWorker mGapWorker;
438     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
439             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
440 
441     final State mState = new State();
442 
443     private OnScrollListener mScrollListener;
444     private List<OnScrollListener> mScrollListeners;
445 
446     // For use in item animations
447     boolean mItemsAddedOrRemoved = false;
448     boolean mItemsChanged = false;
449     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
450             new ItemAnimatorRestoreListener();
451     boolean mPostedAnimatorRunner = false;
452     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
453     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
454 
455     // simple array to keep min and max child position during a layout calculation
456     // preserved not to create a new one in each layout pass
457     private final int[] mMinMaxLayoutPositions = new int[2];
458 
459     private final int[] mScrollOffset = new int[2];
460     private final int[] mScrollConsumed = new int[2];
461     private final int[] mNestedOffsets = new int[2];
462 
463     /**
464      * These are views that had their a11y importance changed during a layout. We defer these events
465      * until the end of the layout because a11y service may make sync calls back to the RV while
466      * the View's state is undefined.
467      */
468     @VisibleForTesting
469     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
470 
471     private Runnable mItemAnimatorRunner = new Runnable() {
472         @Override
473         public void run() {
474             if (mItemAnimator != null) {
475                 mItemAnimator.runPendingAnimations();
476             }
477             mPostedAnimatorRunner = false;
478         }
479     };
480 
481     static final Interpolator sQuinticInterpolator = new Interpolator() {
482         @Override
483         public float getInterpolation(float t) {
484             t -= 1.0f;
485             return t * t * t * t * t + 1.0f;
486         }
487     };
488 
489     /**
490      * The callback to convert view info diffs into animations.
491      */
492     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
493             new ViewInfoStore.ProcessCallback() {
494         @Override
495         public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
496                 @Nullable ItemHolderInfo postInfo) {
497             mRecycler.unscrapView(viewHolder);
498             animateDisappearance(viewHolder, info, postInfo);
499         }
500         @Override
501         public void processAppeared(ViewHolder viewHolder,
502                 ItemHolderInfo preInfo, ItemHolderInfo info) {
503             animateAppearance(viewHolder, preInfo, info);
504         }
505 
506         @Override
507         public void processPersistent(ViewHolder viewHolder,
508                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
509             viewHolder.setIsRecyclable(false);
510             if (mDataSetHasChangedAfterLayout) {
511                 // since it was rebound, use change instead as we'll be mapping them from
512                 // stable ids. If stable ids were false, we would not be running any
513                 // animations
514                 if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
515                     postAnimationRunner();
516                 }
517             } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
518                 postAnimationRunner();
519             }
520         }
521         @Override
522         public void unused(ViewHolder viewHolder) {
523             mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
524         }
525     };
526 
RecyclerView(Context context)527     public RecyclerView(Context context) {
528         this(context, null);
529     }
530 
RecyclerView(Context context, @Nullable AttributeSet attrs)531     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
532         this(context, attrs, 0);
533     }
534 
RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle)535     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
536         super(context, attrs, defStyle);
537         if (attrs != null) {
538             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
539             mClipToPadding = a.getBoolean(0, true);
540             a.recycle();
541         } else {
542             mClipToPadding = true;
543         }
544         setScrollContainer(true);
545         setFocusableInTouchMode(true);
546 
547         final ViewConfiguration vc = ViewConfiguration.get(context);
548         mTouchSlop = vc.getScaledTouchSlop();
549         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
550         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
551         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
552 
553         mItemAnimator.setListener(mItemAnimatorListener);
554         initAdapterManager();
555         initChildrenHelper();
556         // If not explicitly specified this view is important for accessibility.
557         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
558             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
559         }
560         mAccessibilityManager = (AccessibilityManager) getContext()
561                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
562         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
563         // Create the layoutManager if specified.
564 
565         boolean nestedScrollingEnabled = true;
566 
567         if (attrs != null) {
568             int defStyleRes = 0;
569             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
570                     defStyle, defStyleRes);
571             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
572             int descendantFocusability = a.getInt(
573                     R.styleable.RecyclerView_descendantFocusability, -1);
574             if (descendantFocusability == -1) {
575                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
576             }
577             a.recycle();
578             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
579 
580             if (Build.VERSION.SDK_INT >= 21) {
581                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
582                         defStyle, defStyleRes);
583                 nestedScrollingEnabled = a.getBoolean(0, true);
584                 a.recycle();
585             }
586         } else {
587             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
588         }
589 
590         // Re-set whether nested scrolling is enabled so that it is set on all API levels
591         setNestedScrollingEnabled(nestedScrollingEnabled);
592     }
593 
594     /**
595      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
596      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
597      */
getCompatAccessibilityDelegate()598     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
599         return mAccessibilityDelegate;
600     }
601 
602     /**
603      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
604      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
605      */
setAccessibilityDelegateCompat( RecyclerViewAccessibilityDelegate accessibilityDelegate)606     public void setAccessibilityDelegateCompat(
607             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
608         mAccessibilityDelegate = accessibilityDelegate;
609         setAccessibilityDelegate(mAccessibilityDelegate);
610     }
611 
612     /**
613      * Instantiate and set a LayoutManager, if specified in the attributes.
614      */
createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes)615     private void createLayoutManager(Context context, String className, AttributeSet attrs,
616             int defStyleAttr, int defStyleRes) {
617         if (className != null) {
618             className = className.trim();
619             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
620                 className = getFullClassName(context, className);
621                 try {
622                     ClassLoader classLoader;
623                     if (isInEditMode()) {
624                         // Stupid layoutlib cannot handle simple class loaders.
625                         classLoader = this.getClass().getClassLoader();
626                     } else {
627                         classLoader = context.getClassLoader();
628                     }
629                     Class<? extends LayoutManager> layoutManagerClass =
630                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
631                     Constructor<? extends LayoutManager> constructor;
632                     Object[] constructorArgs = null;
633                     try {
634                         constructor = layoutManagerClass
635                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
636                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
637                     } catch (NoSuchMethodException e) {
638                         try {
639                             constructor = layoutManagerClass.getConstructor();
640                         } catch (NoSuchMethodException e1) {
641                             e1.initCause(e);
642                             throw new IllegalStateException(attrs.getPositionDescription()
643                                     + ": Error creating LayoutManager " + className, e1);
644                         }
645                     }
646                     constructor.setAccessible(true);
647                     setLayoutManager(constructor.newInstance(constructorArgs));
648                 } catch (ClassNotFoundException e) {
649                     throw new IllegalStateException(attrs.getPositionDescription()
650                             + ": Unable to find LayoutManager " + className, e);
651                 } catch (InvocationTargetException e) {
652                     throw new IllegalStateException(attrs.getPositionDescription()
653                             + ": Could not instantiate the LayoutManager: " + className, e);
654                 } catch (InstantiationException e) {
655                     throw new IllegalStateException(attrs.getPositionDescription()
656                             + ": Could not instantiate the LayoutManager: " + className, e);
657                 } catch (IllegalAccessException e) {
658                     throw new IllegalStateException(attrs.getPositionDescription()
659                             + ": Cannot access non-public constructor " + className, e);
660                 } catch (ClassCastException e) {
661                     throw new IllegalStateException(attrs.getPositionDescription()
662                             + ": Class is not a LayoutManager " + className, e);
663                 }
664             }
665         }
666     }
667 
getFullClassName(Context context, String className)668     private String getFullClassName(Context context, String className) {
669         if (className.charAt(0) == '.') {
670             return context.getPackageName() + className;
671         }
672         if (className.contains(".")) {
673             return className;
674         }
675         return RecyclerView.class.getPackage().getName() + '.' + className;
676     }
677 
initChildrenHelper()678     private void initChildrenHelper() {
679         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
680             @Override
681             public int getChildCount() {
682                 return RecyclerView.this.getChildCount();
683             }
684 
685             @Override
686             public void addView(View child, int index) {
687                 RecyclerView.this.addView(child, index);
688                 dispatchChildAttached(child);
689             }
690 
691             @Override
692             public int indexOfChild(View view) {
693                 return RecyclerView.this.indexOfChild(view);
694             }
695 
696             @Override
697             public void removeViewAt(int index) {
698                 final View child = RecyclerView.this.getChildAt(index);
699                 if (child != null) {
700                     dispatchChildDetached(child);
701                 }
702                 RecyclerView.this.removeViewAt(index);
703             }
704 
705             @Override
706             public View getChildAt(int offset) {
707                 return RecyclerView.this.getChildAt(offset);
708             }
709 
710             @Override
711             public void removeAllViews() {
712                 final int count = getChildCount();
713                 for (int i = 0; i < count; i++) {
714                     dispatchChildDetached(getChildAt(i));
715                 }
716                 RecyclerView.this.removeAllViews();
717             }
718 
719             @Override
720             public ViewHolder getChildViewHolder(View view) {
721                 return getChildViewHolderInt(view);
722             }
723 
724             @Override
725             public void attachViewToParent(View child, int index,
726                     ViewGroup.LayoutParams layoutParams) {
727                 final ViewHolder vh = getChildViewHolderInt(child);
728                 if (vh != null) {
729                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
730                         throw new IllegalArgumentException("Called attach on a child which is not"
731                                 + " detached: " + vh);
732                     }
733                     if (DEBUG) {
734                         Log.d(TAG, "reAttach " + vh);
735                     }
736                     vh.clearTmpDetachFlag();
737                 }
738                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
739             }
740 
741             @Override
742             public void detachViewFromParent(int offset) {
743                 final View view = getChildAt(offset);
744                 if (view != null) {
745                     final ViewHolder vh = getChildViewHolderInt(view);
746                     if (vh != null) {
747                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
748                             throw new IllegalArgumentException("called detach on an already"
749                                     + " detached child " + vh);
750                         }
751                         if (DEBUG) {
752                             Log.d(TAG, "tmpDetach " + vh);
753                         }
754                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
755                     }
756                 }
757                 RecyclerView.this.detachViewFromParent(offset);
758             }
759 
760             @Override
761             public void onEnteredHiddenState(View child) {
762                 final ViewHolder vh = getChildViewHolderInt(child);
763                 if (vh != null) {
764                     vh.onEnteredHiddenState(RecyclerView.this);
765                 }
766             }
767 
768             @Override
769             public void onLeftHiddenState(View child) {
770                 final ViewHolder vh = getChildViewHolderInt(child);
771                 if (vh != null) {
772                     vh.onLeftHiddenState(RecyclerView.this);
773                 }
774             }
775         });
776     }
777 
initAdapterManager()778     void initAdapterManager() {
779         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
780             @Override
781             public ViewHolder findViewHolder(int position) {
782                 final ViewHolder vh = findViewHolderForPosition(position, true);
783                 if (vh == null) {
784                     return null;
785                 }
786                 // ensure it is not hidden because for adapter helper, the only thing matter is that
787                 // LM thinks view is a child.
788                 if (mChildHelper.isHidden(vh.itemView)) {
789                     if (DEBUG) {
790                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
791                     }
792                     return null;
793                 }
794                 return vh;
795             }
796 
797             @Override
798             public void offsetPositionsForRemovingInvisible(int start, int count) {
799                 offsetPositionRecordsForRemove(start, count, true);
800                 mItemsAddedOrRemoved = true;
801                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
802             }
803 
804             @Override
805             public void offsetPositionsForRemovingLaidOutOrNewView(
806                     int positionStart, int itemCount) {
807                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
808                 mItemsAddedOrRemoved = true;
809             }
810 
811             @Override
812             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
813                 viewRangeUpdate(positionStart, itemCount, payload);
814                 mItemsChanged = true;
815             }
816 
817             @Override
818             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
819                 dispatchUpdate(op);
820             }
821 
822             void dispatchUpdate(AdapterHelper.UpdateOp op) {
823                 switch (op.cmd) {
824                     case AdapterHelper.UpdateOp.ADD:
825                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
826                         break;
827                     case AdapterHelper.UpdateOp.REMOVE:
828                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
829                         break;
830                     case AdapterHelper.UpdateOp.UPDATE:
831                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
832                                 op.payload);
833                         break;
834                     case AdapterHelper.UpdateOp.MOVE:
835                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
836                         break;
837                 }
838             }
839 
840             @Override
841             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
842                 dispatchUpdate(op);
843             }
844 
845             @Override
846             public void offsetPositionsForAdd(int positionStart, int itemCount) {
847                 offsetPositionRecordsForInsert(positionStart, itemCount);
848                 mItemsAddedOrRemoved = true;
849             }
850 
851             @Override
852             public void offsetPositionsForMove(int from, int to) {
853                 offsetPositionRecordsForMove(from, to);
854                 // should we create mItemsMoved ?
855                 mItemsAddedOrRemoved = true;
856             }
857         });
858     }
859 
860     /**
861      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
862      * size is not affected by the adapter contents. RecyclerView can still change its size based
863      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
864      * size of its children or contents of its adapter (except the number of items in the adapter).
865      * <p>
866      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
867      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
868      *
869      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
870      */
setHasFixedSize(boolean hasFixedSize)871     public void setHasFixedSize(boolean hasFixedSize) {
872         mHasFixedSize = hasFixedSize;
873     }
874 
875     /**
876      * @return true if the app has specified that changes in adapter content cannot change
877      * the size of the RecyclerView itself.
878      */
hasFixedSize()879     public boolean hasFixedSize() {
880         return mHasFixedSize;
881     }
882 
883     @Override
setClipToPadding(boolean clipToPadding)884     public void setClipToPadding(boolean clipToPadding) {
885         if (clipToPadding != mClipToPadding) {
886             invalidateGlows();
887         }
888         mClipToPadding = clipToPadding;
889         super.setClipToPadding(clipToPadding);
890         if (mFirstLayoutComplete) {
891             requestLayout();
892         }
893     }
894 
895     /**
896      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
897      * not clip) any EdgeEffect to the padded region, if padding is present.
898      * <p>
899      * By default, children are clipped to the padding of their parent
900      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
901      *
902      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
903      *         clip) any EdgeEffect to the padded region, false otherwise.
904      *
905      * @attr name android:clipToPadding
906      */
907     @Override
getClipToPadding()908     public boolean getClipToPadding() {
909         return mClipToPadding;
910     }
911 
912     /**
913      * Configure the scrolling touch slop for a specific use case.
914      *
915      * Set up the RecyclerView's scrolling motion threshold based on common usages.
916      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
917      *
918      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
919      *                     the intended usage of this RecyclerView
920      */
setScrollingTouchSlop(int slopConstant)921     public void setScrollingTouchSlop(int slopConstant) {
922         final ViewConfiguration vc = ViewConfiguration.get(getContext());
923         switch (slopConstant) {
924             default:
925                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
926                         + slopConstant + "; using default value");
927                 // fall-through
928             case TOUCH_SLOP_DEFAULT:
929                 mTouchSlop = vc.getScaledTouchSlop();
930                 break;
931 
932             case TOUCH_SLOP_PAGING:
933                 mTouchSlop = vc.getScaledPagingTouchSlop();
934                 break;
935         }
936     }
937 
938     /**
939      * Swaps the current adapter with the provided one. It is similar to
940      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
941      * {@link ViewHolder} and does not clear the RecycledViewPool.
942      * <p>
943      * Note that it still calls onAdapterChanged callbacks.
944      *
945      * @param adapter The new adapter to set, or null to set no adapter.
946      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
947      *                                      Views. If adapters have stable ids and/or you want to
948      *                                      animate the disappearing views, you may prefer to set
949      *                                      this to false.
950      * @see #setAdapter(Adapter)
951      */
swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews)952     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
953         // bail out if layout is frozen
954         setLayoutFrozen(false);
955         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
956         setDataSetChangedAfterLayout();
957         requestLayout();
958     }
959     /**
960      * Set a new adapter to provide child views on demand.
961      * <p>
962      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
963      * only one adapter, it will be cleared.
964      *
965      * @param adapter The new adapter to set, or null to set no adapter.
966      * @see #swapAdapter(Adapter, boolean)
967      */
setAdapter(Adapter adapter)968     public void setAdapter(Adapter adapter) {
969         // bail out if layout is frozen
970         setLayoutFrozen(false);
971         setAdapterInternal(adapter, false, true);
972         requestLayout();
973     }
974 
975     /**
976      * Removes and recycles all views - both those currently attached, and those in the Recycler.
977      */
removeAndRecycleViews()978     void removeAndRecycleViews() {
979         // end all running animations
980         if (mItemAnimator != null) {
981             mItemAnimator.endAnimations();
982         }
983         // Since animations are ended, mLayout.children should be equal to
984         // recyclerView.children. This may not be true if item animator's end does not work as
985         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
986         // count.
987         if (mLayout != null) {
988             mLayout.removeAndRecycleAllViews(mRecycler);
989             mLayout.removeAndRecycleScrapInt(mRecycler);
990         }
991         // we should clear it here before adapters are swapped to ensure correct callbacks.
992         mRecycler.clear();
993     }
994 
995     /**
996      * Replaces the current adapter with the new one and triggers listeners.
997      * @param adapter The new adapter
998      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
999      *                               item types with the current adapter (helps us avoid cache
1000      *                               invalidation).
1001      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
1002      *                               compatibleWithPrevious is false, this parameter is ignored.
1003      */
setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews)1004     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
1005             boolean removeAndRecycleViews) {
1006         if (mAdapter != null) {
1007             mAdapter.unregisterAdapterDataObserver(mObserver);
1008             mAdapter.onDetachedFromRecyclerView(this);
1009         }
1010         if (!compatibleWithPrevious || removeAndRecycleViews) {
1011             removeAndRecycleViews();
1012         }
1013         mAdapterHelper.reset();
1014         final Adapter oldAdapter = mAdapter;
1015         mAdapter = adapter;
1016         if (adapter != null) {
1017             adapter.registerAdapterDataObserver(mObserver);
1018             adapter.onAttachedToRecyclerView(this);
1019         }
1020         if (mLayout != null) {
1021             mLayout.onAdapterChanged(oldAdapter, mAdapter);
1022         }
1023         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
1024         mState.mStructureChanged = true;
1025         markKnownViewsInvalid();
1026     }
1027 
1028     /**
1029      * Retrieves the previously set adapter or null if no adapter is set.
1030      *
1031      * @return The previously set adapter
1032      * @see #setAdapter(Adapter)
1033      */
getAdapter()1034     public Adapter getAdapter() {
1035         return mAdapter;
1036     }
1037 
1038     /**
1039      * Register a listener that will be notified whenever a child view is recycled.
1040      *
1041      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1042      * that a child view is no longer needed. If an application associates expensive
1043      * or heavyweight data with item views, this may be a good place to release
1044      * or free those resources.</p>
1045      *
1046      * @param listener Listener to register, or null to clear
1047      */
setRecyclerListener(RecyclerListener listener)1048     public void setRecyclerListener(RecyclerListener listener) {
1049         mRecyclerListener = listener;
1050     }
1051 
1052     /**
1053      * <p>Return the offset of the RecyclerView's text baseline from the its top
1054      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
1055      * this method returns -1.</p>
1056      *
1057      * @return the offset of the baseline within the RecyclerView's bounds or -1
1058      *         if baseline alignment is not supported
1059      */
1060     @Override
getBaseline()1061     public int getBaseline() {
1062         if (mLayout != null) {
1063             return mLayout.getBaseline();
1064         } else {
1065             return super.getBaseline();
1066         }
1067     }
1068 
1069     /**
1070      * Register a listener that will be notified whenever a child view is attached to or detached
1071      * from RecyclerView.
1072      *
1073      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
1074      * that a child view is no longer needed. If an application associates expensive
1075      * or heavyweight data with item views, this may be a good place to release
1076      * or free those resources.</p>
1077      *
1078      * @param listener Listener to register
1079      */
addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1080     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1081         if (mOnChildAttachStateListeners == null) {
1082             mOnChildAttachStateListeners = new ArrayList<>();
1083         }
1084         mOnChildAttachStateListeners.add(listener);
1085     }
1086 
1087     /**
1088      * Removes the provided listener from child attached state listeners list.
1089      *
1090      * @param listener Listener to unregister
1091      */
removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener)1092     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
1093         if (mOnChildAttachStateListeners == null) {
1094             return;
1095         }
1096         mOnChildAttachStateListeners.remove(listener);
1097     }
1098 
1099     /**
1100      * Removes all listeners that were added via
1101      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
1102      */
clearOnChildAttachStateChangeListeners()1103     public void clearOnChildAttachStateChangeListeners() {
1104         if (mOnChildAttachStateListeners != null) {
1105             mOnChildAttachStateListeners.clear();
1106         }
1107     }
1108 
1109     /**
1110      * Set the {@link LayoutManager} that this RecyclerView will use.
1111      *
1112      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
1113      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
1114      * layout arrangements for child views. These arrangements are controlled by the
1115      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
1116      *
1117      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
1118      *
1119      * @param layout LayoutManager to use
1120      */
setLayoutManager(LayoutManager layout)1121     public void setLayoutManager(LayoutManager layout) {
1122         if (layout == mLayout) {
1123             return;
1124         }
1125         stopScroll();
1126         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
1127         // chance that LayoutManagers will re-use views.
1128         if (mLayout != null) {
1129             // end all running animations
1130             if (mItemAnimator != null) {
1131                 mItemAnimator.endAnimations();
1132             }
1133             mLayout.removeAndRecycleAllViews(mRecycler);
1134             mLayout.removeAndRecycleScrapInt(mRecycler);
1135             mRecycler.clear();
1136 
1137             if (mIsAttached) {
1138                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
1139             }
1140             mLayout.setRecyclerView(null);
1141             mLayout = null;
1142         } else {
1143             mRecycler.clear();
1144         }
1145         // this is just a defensive measure for faulty item animators.
1146         mChildHelper.removeAllViewsUnfiltered();
1147         mLayout = layout;
1148         if (layout != null) {
1149             if (layout.mRecyclerView != null) {
1150                 throw new IllegalArgumentException("LayoutManager " + layout
1151                         + " is already attached to a RecyclerView: " + layout.mRecyclerView);
1152             }
1153             mLayout.setRecyclerView(this);
1154             if (mIsAttached) {
1155                 mLayout.dispatchAttachedToWindow(this);
1156             }
1157         }
1158         mRecycler.updateViewCacheSize();
1159         requestLayout();
1160     }
1161 
1162     /**
1163      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
1164      * <p>
1165      * If the {@link OnFlingListener} is set then it will receive
1166      * calls to {@link #fling(int,int)} and will be able to intercept them.
1167      *
1168      * @param onFlingListener The {@link OnFlingListener} instance.
1169      */
setOnFlingListener(@ullable OnFlingListener onFlingListener)1170     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
1171         mOnFlingListener = onFlingListener;
1172     }
1173 
1174     /**
1175      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
1176      *
1177      * @return The {@link OnFlingListener} instance currently set (can be null).
1178      */
1179     @Nullable
getOnFlingListener()1180     public OnFlingListener getOnFlingListener() {
1181         return mOnFlingListener;
1182     }
1183 
1184     @Override
onSaveInstanceState()1185     protected Parcelable onSaveInstanceState() {
1186         SavedState state = new SavedState(super.onSaveInstanceState());
1187         if (mPendingSavedState != null) {
1188             state.copyFrom(mPendingSavedState);
1189         } else if (mLayout != null) {
1190             state.mLayoutState = mLayout.onSaveInstanceState();
1191         } else {
1192             state.mLayoutState = null;
1193         }
1194 
1195         return state;
1196     }
1197 
1198     @Override
onRestoreInstanceState(Parcelable state)1199     protected void onRestoreInstanceState(Parcelable state) {
1200         if (!(state instanceof SavedState)) {
1201             super.onRestoreInstanceState(state);
1202             return;
1203         }
1204 
1205         mPendingSavedState = (SavedState) state;
1206         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
1207         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
1208             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
1209         }
1210     }
1211 
1212     /**
1213      * Override to prevent freezing of any views created by the adapter.
1214      */
1215     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)1216     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1217         dispatchFreezeSelfOnly(container);
1218     }
1219 
1220     /**
1221      * Override to prevent thawing of any views created by the adapter.
1222      */
1223     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)1224     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1225         dispatchThawSelfOnly(container);
1226     }
1227 
1228     /**
1229      * Adds a view to the animatingViews list.
1230      * mAnimatingViews holds the child views that are currently being kept around
1231      * purely for the purpose of being animated out of view. They are drawn as a regular
1232      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
1233      * as they are managed separately from the regular child views.
1234      * @param viewHolder The ViewHolder to be removed
1235      */
addAnimatingView(ViewHolder viewHolder)1236     private void addAnimatingView(ViewHolder viewHolder) {
1237         final View view = viewHolder.itemView;
1238         final boolean alreadyParented = view.getParent() == this;
1239         mRecycler.unscrapView(getChildViewHolder(view));
1240         if (viewHolder.isTmpDetached()) {
1241             // re-attach
1242             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
1243         } else if (!alreadyParented) {
1244             mChildHelper.addView(view, true);
1245         } else {
1246             mChildHelper.hide(view);
1247         }
1248     }
1249 
1250     /**
1251      * Removes a view from the animatingViews list.
1252      * @param view The view to be removed
1253      * @see #addAnimatingView(RecyclerView.ViewHolder)
1254      * @return true if an animating view is removed
1255      */
removeAnimatingView(View view)1256     boolean removeAnimatingView(View view) {
1257         eatRequestLayout();
1258         final boolean removed = mChildHelper.removeViewIfHidden(view);
1259         if (removed) {
1260             final ViewHolder viewHolder = getChildViewHolderInt(view);
1261             mRecycler.unscrapView(viewHolder);
1262             mRecycler.recycleViewHolderInternal(viewHolder);
1263             if (DEBUG) {
1264                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
1265             }
1266         }
1267         // only clear request eaten flag if we removed the view.
1268         resumeRequestLayout(!removed);
1269         return removed;
1270     }
1271 
1272     /**
1273      * Return the {@link LayoutManager} currently responsible for
1274      * layout policy for this RecyclerView.
1275      *
1276      * @return The currently bound LayoutManager
1277      */
getLayoutManager()1278     public LayoutManager getLayoutManager() {
1279         return mLayout;
1280     }
1281 
1282     /**
1283      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
1284      * if no pool is set for this view a new one will be created. See
1285      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
1286      *
1287      * @return The pool used to store recycled item views for reuse.
1288      * @see #setRecycledViewPool(RecycledViewPool)
1289      */
getRecycledViewPool()1290     public RecycledViewPool getRecycledViewPool() {
1291         return mRecycler.getRecycledViewPool();
1292     }
1293 
1294     /**
1295      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
1296      * This can be useful if you have multiple RecyclerViews with adapters that use the same
1297      * view types, for example if you have several data sets with the same kinds of item views
1298      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
1299      *
1300      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
1301      */
setRecycledViewPool(RecycledViewPool pool)1302     public void setRecycledViewPool(RecycledViewPool pool) {
1303         mRecycler.setRecycledViewPool(pool);
1304     }
1305 
1306     /**
1307      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
1308      *
1309      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
1310      *
1311      * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
1312      */
setViewCacheExtension(ViewCacheExtension extension)1313     public void setViewCacheExtension(ViewCacheExtension extension) {
1314         mRecycler.setViewCacheExtension(extension);
1315     }
1316 
1317     /**
1318      * Set the number of offscreen views to retain before adding them to the potentially shared
1319      * {@link #getRecycledViewPool() recycled view pool}.
1320      *
1321      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
1322      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
1323      * to rebind them.</p>
1324      *
1325      * @param size Number of views to cache offscreen before returning them to the general
1326      *             recycled view pool
1327      */
setItemViewCacheSize(int size)1328     public void setItemViewCacheSize(int size) {
1329         mRecycler.setViewCacheSize(size);
1330     }
1331 
1332     /**
1333      * Return the current scrolling state of the RecyclerView.
1334      *
1335      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
1336      * {@link #SCROLL_STATE_SETTLING}
1337      */
getScrollState()1338     public int getScrollState() {
1339         return mScrollState;
1340     }
1341 
setScrollState(int state)1342     void setScrollState(int state) {
1343         if (state == mScrollState) {
1344             return;
1345         }
1346         if (DEBUG) {
1347             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
1348                     new Exception());
1349         }
1350         mScrollState = state;
1351         if (state != SCROLL_STATE_SETTLING) {
1352             stopScrollersInternal();
1353         }
1354         dispatchOnScrollStateChanged(state);
1355     }
1356 
1357     /**
1358      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1359      * affect both measurement and drawing of individual item views.
1360      *
1361      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1362      * be run/queried/drawn first for their effects on item views. Padding added to views
1363      * will be nested; a padding added by an earlier decoration will mean further
1364      * item decorations in the list will be asked to draw/pad within the previous decoration's
1365      * given area.</p>
1366      *
1367      * @param decor Decoration to add
1368      * @param index Position in the decoration chain to insert this decoration at. If this value
1369      *              is negative the decoration will be added at the end.
1370      */
addItemDecoration(ItemDecoration decor, int index)1371     public void addItemDecoration(ItemDecoration decor, int index) {
1372         if (mLayout != null) {
1373             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
1374                     + " layout");
1375         }
1376         if (mItemDecorations.isEmpty()) {
1377             setWillNotDraw(false);
1378         }
1379         if (index < 0) {
1380             mItemDecorations.add(decor);
1381         } else {
1382             mItemDecorations.add(index, decor);
1383         }
1384         markItemDecorInsetsDirty();
1385         requestLayout();
1386     }
1387 
1388     /**
1389      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
1390      * affect both measurement and drawing of individual item views.
1391      *
1392      * <p>Item decorations are ordered. Decorations placed earlier in the list will
1393      * be run/queried/drawn first for their effects on item views. Padding added to views
1394      * will be nested; a padding added by an earlier decoration will mean further
1395      * item decorations in the list will be asked to draw/pad within the previous decoration's
1396      * given area.</p>
1397      *
1398      * @param decor Decoration to add
1399      */
addItemDecoration(ItemDecoration decor)1400     public void addItemDecoration(ItemDecoration decor) {
1401         addItemDecoration(decor, -1);
1402     }
1403 
1404     /**
1405      * Remove an {@link ItemDecoration} from this RecyclerView.
1406      *
1407      * <p>The given decoration will no longer impact the measurement and drawing of
1408      * item views.</p>
1409      *
1410      * @param decor Decoration to remove
1411      * @see #addItemDecoration(ItemDecoration)
1412      */
removeItemDecoration(ItemDecoration decor)1413     public void removeItemDecoration(ItemDecoration decor) {
1414         if (mLayout != null) {
1415             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
1416                     + " layout");
1417         }
1418         mItemDecorations.remove(decor);
1419         if (mItemDecorations.isEmpty()) {
1420             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
1421         }
1422         markItemDecorInsetsDirty();
1423         requestLayout();
1424     }
1425 
1426     /**
1427      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
1428      * <p>
1429      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
1430      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
1431      * true if childDrawingOrderCallback is not null, false otherwise.
1432      * <p>
1433      * Note that child drawing order may be overridden by View's elevation.
1434      *
1435      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
1436      *                                  system.
1437      */
setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback)1438     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
1439         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
1440             return;
1441         }
1442         mChildDrawingOrderCallback = childDrawingOrderCallback;
1443         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
1444     }
1445 
1446     /**
1447      * Set a listener that will be notified of any changes in scroll state or position.
1448      *
1449      * @param listener Listener to set or null to clear
1450      *
1451      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
1452      *             {@link #removeOnScrollListener(OnScrollListener)}
1453      */
1454     @Deprecated
setOnScrollListener(OnScrollListener listener)1455     public void setOnScrollListener(OnScrollListener listener) {
1456         mScrollListener = listener;
1457     }
1458 
1459     /**
1460      * Add a listener that will be notified of any changes in scroll state or position.
1461      *
1462      * <p>Components that add a listener should take care to remove it when finished.
1463      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
1464      * to remove all attached listeners.</p>
1465      *
1466      * @param listener listener to set or null to clear
1467      */
addOnScrollListener(OnScrollListener listener)1468     public void addOnScrollListener(OnScrollListener listener) {
1469         if (mScrollListeners == null) {
1470             mScrollListeners = new ArrayList<>();
1471         }
1472         mScrollListeners.add(listener);
1473     }
1474 
1475     /**
1476      * Remove a listener that was notified of any changes in scroll state or position.
1477      *
1478      * @param listener listener to set or null to clear
1479      */
removeOnScrollListener(OnScrollListener listener)1480     public void removeOnScrollListener(OnScrollListener listener) {
1481         if (mScrollListeners != null) {
1482             mScrollListeners.remove(listener);
1483         }
1484     }
1485 
1486     /**
1487      * Remove all secondary listener that were notified of any changes in scroll state or position.
1488      */
clearOnScrollListeners()1489     public void clearOnScrollListeners() {
1490         if (mScrollListeners != null) {
1491             mScrollListeners.clear();
1492         }
1493     }
1494 
1495     /**
1496      * Convenience method to scroll to a certain position.
1497      *
1498      * RecyclerView does not implement scrolling logic, rather forwards the call to
1499      * {@link com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
1500      * @param position Scroll to this adapter position
1501      * @see com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)
1502      */
scrollToPosition(int position)1503     public void scrollToPosition(int position) {
1504         if (mLayoutFrozen) {
1505             return;
1506         }
1507         stopScroll();
1508         if (mLayout == null) {
1509             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
1510                     + "Call setLayoutManager with a non-null argument.");
1511             return;
1512         }
1513         mLayout.scrollToPosition(position);
1514         awakenScrollBars();
1515     }
1516 
jumpToPositionForSmoothScroller(int position)1517     void jumpToPositionForSmoothScroller(int position) {
1518         if (mLayout == null) {
1519             return;
1520         }
1521         mLayout.scrollToPosition(position);
1522         awakenScrollBars();
1523     }
1524 
1525     /**
1526      * Starts a smooth scroll to an adapter position.
1527      * <p>
1528      * To support smooth scrolling, you must override
1529      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
1530      * {@link SmoothScroller}.
1531      * <p>
1532      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
1533      * provide a custom smooth scroll logic, override
1534      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
1535      * LayoutManager.
1536      *
1537      * @param position The adapter position to scroll to
1538      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
1539      */
smoothScrollToPosition(int position)1540     public void smoothScrollToPosition(int position) {
1541         if (mLayoutFrozen) {
1542             return;
1543         }
1544         if (mLayout == null) {
1545             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1546                     + "Call setLayoutManager with a non-null argument.");
1547             return;
1548         }
1549         mLayout.smoothScrollToPosition(this, mState, position);
1550     }
1551 
1552     @Override
scrollTo(int x, int y)1553     public void scrollTo(int x, int y) {
1554         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
1555                 + "Use scrollToPosition instead");
1556     }
1557 
1558     @Override
scrollBy(int x, int y)1559     public void scrollBy(int x, int y) {
1560         if (mLayout == null) {
1561             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
1562                     + "Call setLayoutManager with a non-null argument.");
1563             return;
1564         }
1565         if (mLayoutFrozen) {
1566             return;
1567         }
1568         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
1569         final boolean canScrollVertical = mLayout.canScrollVertically();
1570         if (canScrollHorizontal || canScrollVertical) {
1571             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
1572         }
1573     }
1574 
1575     /**
1576      * Helper method reflect data changes to the state.
1577      * <p>
1578      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
1579      * but data actually changed.
1580      * <p>
1581      * This method consumes all deferred changes to avoid that case.
1582      */
consumePendingUpdateOperations()1583     void consumePendingUpdateOperations() {
1584         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
1585             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1586             dispatchLayout();
1587             Trace.endSection();
1588             return;
1589         }
1590         if (!mAdapterHelper.hasPendingUpdates()) {
1591             return;
1592         }
1593 
1594         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
1595         // of the visible items is affected and if not, just ignore the change.
1596         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
1597                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
1598                         | AdapterHelper.UpdateOp.MOVE)) {
1599             Trace.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
1600             eatRequestLayout();
1601             onEnterLayoutOrScroll();
1602             mAdapterHelper.preProcess();
1603             if (!mLayoutRequestEaten) {
1604                 if (hasUpdatedView()) {
1605                     dispatchLayout();
1606                 } else {
1607                     // no need to layout, clean state
1608                     mAdapterHelper.consumePostponedUpdates();
1609                 }
1610             }
1611             resumeRequestLayout(true);
1612             onExitLayoutOrScroll();
1613             Trace.endSection();
1614         } else if (mAdapterHelper.hasPendingUpdates()) {
1615             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
1616             dispatchLayout();
1617             Trace.endSection();
1618         }
1619     }
1620 
1621     /**
1622      * @return True if an existing view holder needs to be updated
1623      */
hasUpdatedView()1624     private boolean hasUpdatedView() {
1625         final int childCount = mChildHelper.getChildCount();
1626         for (int i = 0; i < childCount; i++) {
1627             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
1628             if (holder == null || holder.shouldIgnore()) {
1629                 continue;
1630             }
1631             if (holder.isUpdated()) {
1632                 return true;
1633             }
1634         }
1635         return false;
1636     }
1637 
1638     /**
1639      * Does not perform bounds checking. Used by internal methods that have already validated input.
1640      * <p>
1641      * It also reports any unused scroll request to the related EdgeEffect.
1642      *
1643      * @param x The amount of horizontal scroll request
1644      * @param y The amount of vertical scroll request
1645      * @param ev The originating MotionEvent, or null if not from a touch event.
1646      *
1647      * @return Whether any scroll was consumed in either direction.
1648      */
scrollByInternal(int x, int y, MotionEvent ev)1649     boolean scrollByInternal(int x, int y, MotionEvent ev) {
1650         int unconsumedX = 0, unconsumedY = 0;
1651         int consumedX = 0, consumedY = 0;
1652 
1653         consumePendingUpdateOperations();
1654         if (mAdapter != null) {
1655             eatRequestLayout();
1656             onEnterLayoutOrScroll();
1657             Trace.beginSection(TRACE_SCROLL_TAG);
1658             if (x != 0) {
1659                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
1660                 unconsumedX = x - consumedX;
1661             }
1662             if (y != 0) {
1663                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
1664                 unconsumedY = y - consumedY;
1665             }
1666             Trace.endSection();
1667             repositionShadowingViews();
1668             onExitLayoutOrScroll();
1669             resumeRequestLayout(false);
1670         }
1671         if (!mItemDecorations.isEmpty()) {
1672             invalidate();
1673         }
1674 
1675         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
1676             // Update the last touch co-ords, taking any scroll offset into account
1677             mLastTouchX -= mScrollOffset[0];
1678             mLastTouchY -= mScrollOffset[1];
1679             if (ev != null) {
1680                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
1681             }
1682             mNestedOffsets[0] += mScrollOffset[0];
1683             mNestedOffsets[1] += mScrollOffset[1];
1684         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
1685             if (ev != null) {
1686                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
1687             }
1688             considerReleasingGlowsOnScroll(x, y);
1689         }
1690         if (consumedX != 0 || consumedY != 0) {
1691             dispatchOnScrolled(consumedX, consumedY);
1692         }
1693         if (!awakenScrollBars()) {
1694             invalidate();
1695         }
1696         return consumedX != 0 || consumedY != 0;
1697     }
1698 
1699     /**
1700      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
1701      * range. This value is used to compute the length of the thumb within the scrollbar's track.
1702      * </p>
1703      *
1704      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1705      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
1706      *
1707      * <p>Default implementation returns 0.</p>
1708      *
1709      * <p>If you want to support scroll bars, override
1710      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
1711      * LayoutManager. </p>
1712      *
1713      * @return The horizontal offset of the scrollbar's thumb
1714      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
1715      * (RecyclerView.State)
1716      */
1717     @Override
computeHorizontalScrollOffset()1718     public int computeHorizontalScrollOffset() {
1719         if (mLayout == null) {
1720             return 0;
1721         }
1722         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
1723     }
1724 
1725     /**
1726      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
1727      * horizontal range. This value is used to compute the length of the thumb within the
1728      * scrollbar's track.</p>
1729      *
1730      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1731      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
1732      *
1733      * <p>Default implementation returns 0.</p>
1734      *
1735      * <p>If you want to support scroll bars, override
1736      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
1737      * LayoutManager.</p>
1738      *
1739      * @return The horizontal extent of the scrollbar's thumb
1740      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
1741      */
1742     @Override
computeHorizontalScrollExtent()1743     public int computeHorizontalScrollExtent() {
1744         if (mLayout == null) {
1745             return 0;
1746         }
1747         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
1748     }
1749 
1750     /**
1751      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
1752      *
1753      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1754      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
1755      *
1756      * <p>Default implementation returns 0.</p>
1757      *
1758      * <p>If you want to support scroll bars, override
1759      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
1760      * LayoutManager.</p>
1761      *
1762      * @return The total horizontal range represented by the vertical scrollbar
1763      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
1764      */
1765     @Override
computeHorizontalScrollRange()1766     public int computeHorizontalScrollRange() {
1767         if (mLayout == null) {
1768             return 0;
1769         }
1770         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
1771     }
1772 
1773     /**
1774      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
1775      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
1776      *
1777      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1778      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
1779      *
1780      * <p>Default implementation returns 0.</p>
1781      *
1782      * <p>If you want to support scroll bars, override
1783      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
1784      * LayoutManager.</p>
1785      *
1786      * @return The vertical offset of the scrollbar's thumb
1787      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
1788      * (RecyclerView.State)
1789      */
1790     @Override
computeVerticalScrollOffset()1791     public int computeVerticalScrollOffset() {
1792         if (mLayout == null) {
1793             return 0;
1794         }
1795         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
1796     }
1797 
1798     /**
1799      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
1800      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
1801      *
1802      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1803      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
1804      *
1805      * <p>Default implementation returns 0.</p>
1806      *
1807      * <p>If you want to support scroll bars, override
1808      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
1809      * LayoutManager.</p>
1810      *
1811      * @return The vertical extent of the scrollbar's thumb
1812      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
1813      */
1814     @Override
computeVerticalScrollExtent()1815     public int computeVerticalScrollExtent() {
1816         if (mLayout == null) {
1817             return 0;
1818         }
1819         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
1820     }
1821 
1822     /**
1823      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
1824      *
1825      * <p>The range is expressed in arbitrary units that must be the same as the units used by
1826      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
1827      *
1828      * <p>Default implementation returns 0.</p>
1829      *
1830      * <p>If you want to support scroll bars, override
1831      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
1832      * LayoutManager.</p>
1833      *
1834      * @return The total vertical range represented by the vertical scrollbar
1835      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
1836      */
1837     @Override
computeVerticalScrollRange()1838     public int computeVerticalScrollRange() {
1839         if (mLayout == null) {
1840             return 0;
1841         }
1842         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
1843     }
1844 
1845 
eatRequestLayout()1846     void eatRequestLayout() {
1847         mEatRequestLayout++;
1848         if (mEatRequestLayout == 1 && !mLayoutFrozen) {
1849             mLayoutRequestEaten = false;
1850         }
1851     }
1852 
resumeRequestLayout(boolean performLayoutChildren)1853     void resumeRequestLayout(boolean performLayoutChildren) {
1854         if (mEatRequestLayout < 1) {
1855             //noinspection PointlessBooleanExpression
1856             if (DEBUG) {
1857                 throw new IllegalStateException("invalid eat request layout count");
1858             }
1859             mEatRequestLayout = 1;
1860         }
1861         if (!performLayoutChildren) {
1862             // Reset the layout request eaten counter.
1863             // This is necessary since eatRequest calls can be nested in which case the other
1864             // call will override the inner one.
1865             // for instance:
1866             // eat layout for process adapter updates
1867             //   eat layout for dispatchLayout
1868             //     a bunch of req layout calls arrive
1869 
1870             mLayoutRequestEaten = false;
1871         }
1872         if (mEatRequestLayout == 1) {
1873             // when layout is frozen we should delay dispatchLayout()
1874             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
1875                     && mLayout != null && mAdapter != null) {
1876                 dispatchLayout();
1877             }
1878             if (!mLayoutFrozen) {
1879                 mLayoutRequestEaten = false;
1880             }
1881         }
1882         mEatRequestLayout--;
1883     }
1884 
1885     /**
1886      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
1887      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
1888      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
1889      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
1890      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
1891      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
1892      * called.
1893      *
1894      * <p>
1895      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
1896      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
1897      * RecyclerView, State, int)}.
1898      * <p>
1899      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
1900      * stop frozen.
1901      * <p>
1902      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
1903      * responsibility to call ItemAnimator.end().
1904      *
1905      * @param frozen   true to freeze layout and scroll, false to re-enable.
1906      */
setLayoutFrozen(boolean frozen)1907     public void setLayoutFrozen(boolean frozen) {
1908         if (frozen != mLayoutFrozen) {
1909             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
1910             if (!frozen) {
1911                 mLayoutFrozen = false;
1912                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
1913                     requestLayout();
1914                 }
1915                 mLayoutRequestEaten = false;
1916             } else {
1917                 final long now = SystemClock.uptimeMillis();
1918                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
1919                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1920                 onTouchEvent(cancelEvent);
1921                 mLayoutFrozen = true;
1922                 mIgnoreMotionEventTillDown = true;
1923                 stopScroll();
1924             }
1925         }
1926     }
1927 
1928     /**
1929      * Returns true if layout and scroll are frozen.
1930      *
1931      * @return true if layout and scroll are frozen
1932      * @see #setLayoutFrozen(boolean)
1933      */
isLayoutFrozen()1934     public boolean isLayoutFrozen() {
1935         return mLayoutFrozen;
1936     }
1937 
1938     /**
1939      * Animate a scroll by the given amount of pixels along either axis.
1940      *
1941      * @param dx Pixels to scroll horizontally
1942      * @param dy Pixels to scroll vertically
1943      */
smoothScrollBy(int dx, int dy)1944     public void smoothScrollBy(int dx, int dy) {
1945         smoothScrollBy(dx, dy, null);
1946     }
1947 
1948     /**
1949      * Animate a scroll by the given amount of pixels along either axis.
1950      *
1951      * @param dx Pixels to scroll horizontally
1952      * @param dy Pixels to scroll vertically
1953      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
1954      *                     {@code null}, RecyclerView is going to use the default interpolator.
1955      */
smoothScrollBy(int dx, int dy, Interpolator interpolator)1956     public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
1957         if (mLayout == null) {
1958             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
1959                     + "Call setLayoutManager with a non-null argument.");
1960             return;
1961         }
1962         if (mLayoutFrozen) {
1963             return;
1964         }
1965         if (!mLayout.canScrollHorizontally()) {
1966             dx = 0;
1967         }
1968         if (!mLayout.canScrollVertically()) {
1969             dy = 0;
1970         }
1971         if (dx != 0 || dy != 0) {
1972             mViewFlinger.smoothScrollBy(dx, dy, interpolator);
1973         }
1974     }
1975 
1976     /**
1977      * Begin a standard fling with an initial velocity along each axis in pixels per second.
1978      * If the velocity given is below the system-defined minimum this method will return false
1979      * and no fling will occur.
1980      *
1981      * @param velocityX Initial horizontal velocity in pixels per second
1982      * @param velocityY Initial vertical velocity in pixels per second
1983      * @return true if the fling was started, false if the velocity was too low to fling or
1984      * LayoutManager does not support scrolling in the axis fling is issued.
1985      *
1986      * @see LayoutManager#canScrollVertically()
1987      * @see LayoutManager#canScrollHorizontally()
1988      */
fling(int velocityX, int velocityY)1989     public boolean fling(int velocityX, int velocityY) {
1990         if (mLayout == null) {
1991             Log.e(TAG, "Cannot fling without a LayoutManager set. "
1992                     + "Call setLayoutManager with a non-null argument.");
1993             return false;
1994         }
1995         if (mLayoutFrozen) {
1996             return false;
1997         }
1998 
1999         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
2000         final boolean canScrollVertical = mLayout.canScrollVertically();
2001 
2002         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
2003             velocityX = 0;
2004         }
2005         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
2006             velocityY = 0;
2007         }
2008         if (velocityX == 0 && velocityY == 0) {
2009             // If we don't have any velocity, return false
2010             return false;
2011         }
2012 
2013         if (!dispatchNestedPreFling(velocityX, velocityY)) {
2014             final View firstChild = mLayout.getChildAt(0);
2015             final View lastChild = mLayout.getChildAt(mLayout.getChildCount() - 1);
2016             boolean consumed = false;
2017             if (velocityY < 0) {
2018                 consumed = getChildAdapterPosition(firstChild) > 0
2019                         || firstChild.getTop() < getPaddingTop();
2020             }
2021 
2022             if (velocityY > 0) {
2023                 consumed = getChildAdapterPosition(lastChild) < mAdapter.getItemCount() - 1
2024                         || lastChild.getBottom() > getHeight() - getPaddingBottom();
2025             }
2026 
2027             dispatchNestedFling(velocityX, velocityY, consumed);
2028 
2029             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
2030                 return true;
2031             }
2032 
2033             final boolean canScroll = canScrollHorizontal || canScrollVertical;
2034 
2035             if (canScroll) {
2036                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
2037                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
2038                 mViewFlinger.fling(velocityX, velocityY);
2039                 return true;
2040             }
2041         }
2042         return false;
2043     }
2044 
2045     /**
2046      * Stop any current scroll in progress, such as one started by
2047      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
2048      */
stopScroll()2049     public void stopScroll() {
2050         setScrollState(SCROLL_STATE_IDLE);
2051         stopScrollersInternal();
2052     }
2053 
2054     /**
2055      * Similar to {@link #stopScroll()} but does not set the state.
2056      */
stopScrollersInternal()2057     private void stopScrollersInternal() {
2058         mViewFlinger.stop();
2059         if (mLayout != null) {
2060             mLayout.stopSmoothScroller();
2061         }
2062     }
2063 
2064     /**
2065      * Returns the minimum velocity to start a fling.
2066      *
2067      * @return The minimum velocity to start a fling
2068      */
getMinFlingVelocity()2069     public int getMinFlingVelocity() {
2070         return mMinFlingVelocity;
2071     }
2072 
2073 
2074     /**
2075      * Returns the maximum fling velocity used by this RecyclerView.
2076      *
2077      * @return The maximum fling velocity used by this RecyclerView.
2078      */
getMaxFlingVelocity()2079     public int getMaxFlingVelocity() {
2080         return mMaxFlingVelocity;
2081     }
2082 
2083     /**
2084      * Apply a pull to relevant overscroll glow effects
2085      */
pullGlows(float x, float overscrollX, float y, float overscrollY)2086     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
2087         boolean invalidate = false;
2088         if (overscrollX < 0) {
2089             ensureLeftGlow();
2090             mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight());
2091             invalidate = true;
2092         } else if (overscrollX > 0) {
2093             ensureRightGlow();
2094             mRightGlow.onPull(overscrollX / getWidth(), y / getHeight());
2095             invalidate = true;
2096         }
2097 
2098         if (overscrollY < 0) {
2099             ensureTopGlow();
2100             mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth());
2101             invalidate = true;
2102         } else if (overscrollY > 0) {
2103             ensureBottomGlow();
2104             mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth());
2105             invalidate = true;
2106         }
2107 
2108         if (invalidate || overscrollX != 0 || overscrollY != 0) {
2109             postInvalidateOnAnimation();
2110         }
2111     }
2112 
releaseGlows()2113     private void releaseGlows() {
2114         boolean needsInvalidate = false;
2115         if (mLeftGlow != null) {
2116             mLeftGlow.onRelease();
2117             needsInvalidate = true;
2118         }
2119         if (mTopGlow != null) {
2120             mTopGlow.onRelease();
2121             needsInvalidate = true;
2122         }
2123         if (mRightGlow != null) {
2124             mRightGlow.onRelease();
2125             needsInvalidate = true;
2126         }
2127         if (mBottomGlow != null) {
2128             mBottomGlow.onRelease();
2129             needsInvalidate = true;
2130         }
2131         if (needsInvalidate) {
2132             postInvalidateOnAnimation();
2133         }
2134     }
2135 
considerReleasingGlowsOnScroll(int dx, int dy)2136     void considerReleasingGlowsOnScroll(int dx, int dy) {
2137         boolean needsInvalidate = false;
2138         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
2139             mLeftGlow.onRelease();
2140             needsInvalidate = true;
2141         }
2142         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
2143             mRightGlow.onRelease();
2144             needsInvalidate = true;
2145         }
2146         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
2147             mTopGlow.onRelease();
2148             needsInvalidate = true;
2149         }
2150         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
2151             mBottomGlow.onRelease();
2152             needsInvalidate = true;
2153         }
2154         if (needsInvalidate) {
2155             postInvalidateOnAnimation();
2156         }
2157     }
2158 
absorbGlows(int velocityX, int velocityY)2159     void absorbGlows(int velocityX, int velocityY) {
2160         if (velocityX < 0) {
2161             ensureLeftGlow();
2162             mLeftGlow.onAbsorb(-velocityX);
2163         } else if (velocityX > 0) {
2164             ensureRightGlow();
2165             mRightGlow.onAbsorb(velocityX);
2166         }
2167 
2168         if (velocityY < 0) {
2169             ensureTopGlow();
2170             mTopGlow.onAbsorb(-velocityY);
2171         } else if (velocityY > 0) {
2172             ensureBottomGlow();
2173             mBottomGlow.onAbsorb(velocityY);
2174         }
2175 
2176         if (velocityX != 0 || velocityY != 0) {
2177             postInvalidateOnAnimation();
2178         }
2179     }
2180 
ensureLeftGlow()2181     void ensureLeftGlow() {
2182         if (mLeftGlow != null) {
2183             return;
2184         }
2185         mLeftGlow = new EdgeEffect(getContext());
2186         if (mClipToPadding) {
2187             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2188                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2189         } else {
2190             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2191         }
2192     }
2193 
ensureRightGlow()2194     void ensureRightGlow() {
2195         if (mRightGlow != null) {
2196             return;
2197         }
2198         mRightGlow = new EdgeEffect(getContext());
2199         if (mClipToPadding) {
2200             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
2201                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
2202         } else {
2203             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
2204         }
2205     }
2206 
ensureTopGlow()2207     void ensureTopGlow() {
2208         if (mTopGlow != null) {
2209             return;
2210         }
2211         mTopGlow = new EdgeEffect(getContext());
2212         if (mClipToPadding) {
2213             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2214                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2215         } else {
2216             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2217         }
2218 
2219     }
2220 
ensureBottomGlow()2221     void ensureBottomGlow() {
2222         if (mBottomGlow != null) {
2223             return;
2224         }
2225         mBottomGlow = new EdgeEffect(getContext());
2226         if (mClipToPadding) {
2227             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
2228                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
2229         } else {
2230             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
2231         }
2232     }
2233 
invalidateGlows()2234     void invalidateGlows() {
2235         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
2236     }
2237 
2238     /**
2239      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
2240      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
2241      * that differs from other ViewGroups.
2242      * <p>
2243      * It first does a focus search within the RecyclerView. If this search finds a View that is in
2244      * the focus direction with respect to the currently focused View, RecyclerView returns that
2245      * child as the next focus target. When it cannot find such child, it calls
2246      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
2247      * in the focus search direction. If LayoutManager adds a View that matches the
2248      * focus search criteria, it will be returned as the focus search result. Otherwise,
2249      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
2250      * <p>
2251      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
2252      * is not in the focus direction is still valid focus target which may not be the desired
2253      * behavior if the Adapter has more children in the focus direction. To handle this case,
2254      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
2255      * focus search in that direction. If there are no Views to gain focus, it will call
2256      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
2257      * focus search with the original (relative) direction. This allows RecyclerView to provide
2258      * better candidates to the focus search while still allowing the view system to take focus from
2259      * the RecyclerView and give it to a more suitable child if such child exists.
2260      *
2261      * @param focused The view that currently has focus
2262      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
2263      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
2264      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
2265      *
2266      * @return A new View that can be the next focus after the focused View
2267      */
2268     @Override
focusSearch(View focused, int direction)2269     public View focusSearch(View focused, int direction) {
2270         View result = mLayout.onInterceptFocusSearch(focused, direction);
2271         if (result != null) {
2272             return result;
2273         }
2274         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
2275                 && !isComputingLayout() && !mLayoutFrozen;
2276 
2277         final FocusFinder ff = FocusFinder.getInstance();
2278         if (canRunFocusFailure
2279                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
2280             // convert direction to absolute direction and see if we have a view there and if not
2281             // tell LayoutManager to add if it can.
2282             boolean needsFocusFailureLayout = false;
2283             if (mLayout.canScrollVertically()) {
2284                 final int absDir =
2285                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
2286                 final View found = ff.findNextFocus(this, focused, absDir);
2287                 needsFocusFailureLayout = found == null;
2288                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2289                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2290                     direction = absDir;
2291                 }
2292             }
2293             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
2294                 boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
2295                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
2296                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2297                 final View found = ff.findNextFocus(this, focused, absDir);
2298                 needsFocusFailureLayout = found == null;
2299                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
2300                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
2301                     direction = absDir;
2302                 }
2303             }
2304             if (needsFocusFailureLayout) {
2305                 consumePendingUpdateOperations();
2306                 final View focusedItemView = findContainingItemView(focused);
2307                 if (focusedItemView == null) {
2308                     // panic, focused view is not a child anymore, cannot call super.
2309                     return null;
2310                 }
2311                 eatRequestLayout();
2312                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2313                 resumeRequestLayout(false);
2314             }
2315             result = ff.findNextFocus(this, focused, direction);
2316         } else {
2317             result = ff.findNextFocus(this, focused, direction);
2318             if (result == null && canRunFocusFailure) {
2319                 consumePendingUpdateOperations();
2320                 final View focusedItemView = findContainingItemView(focused);
2321                 if (focusedItemView == null) {
2322                     // panic, focused view is not a child anymore, cannot call super.
2323                     return null;
2324                 }
2325                 eatRequestLayout();
2326                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
2327                 resumeRequestLayout(false);
2328             }
2329         }
2330         return isPreferredNextFocus(focused, result, direction)
2331                 ? result : super.focusSearch(focused, direction);
2332     }
2333 
2334     /**
2335      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
2336      * assign it as the next focus View instead of letting view hierarchy decide.
2337      * A good candidate means a View that is aligned in the focus direction wrt the focused View
2338      * and is not the RecyclerView itself.
2339      * When this method returns false, RecyclerView will let the parent make the decision so the
2340      * same View may still get the focus as a result of that search.
2341      */
isPreferredNextFocus(View focused, View next, int direction)2342     private boolean isPreferredNextFocus(View focused, View next, int direction) {
2343         if (next == null || next == this) {
2344             return false;
2345         }
2346         if (focused == null) {
2347             return true;
2348         }
2349 
2350         if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
2351             final boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
2352             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
2353                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
2354             if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
2355                 return true;
2356             }
2357             if (direction == View.FOCUS_FORWARD) {
2358                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
2359             } else {
2360                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
2361             }
2362         } else {
2363             return isPreferredNextFocusAbsolute(focused, next, direction);
2364         }
2365 
2366     }
2367 
2368     /**
2369      * Logic taken from FocusSearch#isCandidate
2370      */
isPreferredNextFocusAbsolute(View focused, View next, int direction)2371     private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
2372         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2373         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
2374         offsetDescendantRectToMyCoords(focused, mTempRect);
2375         offsetDescendantRectToMyCoords(next, mTempRect2);
2376         switch (direction) {
2377             case View.FOCUS_LEFT:
2378                 return (mTempRect.right > mTempRect2.right
2379                         || mTempRect.left >= mTempRect2.right)
2380                         && mTempRect.left > mTempRect2.left;
2381             case View.FOCUS_RIGHT:
2382                 return (mTempRect.left < mTempRect2.left
2383                         || mTempRect.right <= mTempRect2.left)
2384                         && mTempRect.right < mTempRect2.right;
2385             case View.FOCUS_UP:
2386                 return (mTempRect.bottom > mTempRect2.bottom
2387                         || mTempRect.top >= mTempRect2.bottom)
2388                         && mTempRect.top > mTempRect2.top;
2389             case View.FOCUS_DOWN:
2390                 return (mTempRect.top < mTempRect2.top
2391                         || mTempRect.bottom <= mTempRect2.top)
2392                         && mTempRect.bottom < mTempRect2.bottom;
2393         }
2394         throw new IllegalArgumentException("direction must be absolute. received:" + direction);
2395     }
2396 
2397     @Override
requestChildFocus(View child, View focused)2398     public void requestChildFocus(View child, View focused) {
2399         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
2400             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
2401 
2402             // get item decor offsets w/o refreshing. If they are invalid, there will be another
2403             // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
2404             // View in viewport.
2405             final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
2406             if (focusedLayoutParams instanceof LayoutParams) {
2407                 // if focused child has item decors, use them. Otherwise, ignore.
2408                 final LayoutParams lp = (LayoutParams) focusedLayoutParams;
2409                 if (!lp.mInsetsDirty) {
2410                     final Rect insets = lp.mDecorInsets;
2411                     mTempRect.left -= insets.left;
2412                     mTempRect.right += insets.right;
2413                     mTempRect.top -= insets.top;
2414                     mTempRect.bottom += insets.bottom;
2415                 }
2416             }
2417 
2418             offsetDescendantRectToMyCoords(focused, mTempRect);
2419             offsetRectIntoDescendantCoords(child, mTempRect);
2420             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
2421         }
2422         super.requestChildFocus(child, focused);
2423     }
2424 
2425     @Override
requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)2426     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
2427         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
2428     }
2429 
2430     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)2431     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
2432         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
2433             super.addFocusables(views, direction, focusableMode);
2434         }
2435     }
2436 
2437     @Override
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)2438     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
2439         if (isComputingLayout()) {
2440             // if we are in the middle of a layout calculation, don't let any child take focus.
2441             // RV will handle it after layout calculation is finished.
2442             return false;
2443         }
2444         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
2445     }
2446 
2447     @Override
onAttachedToWindow()2448     protected void onAttachedToWindow() {
2449         super.onAttachedToWindow();
2450         mLayoutOrScrollCounter = 0;
2451         mIsAttached = true;
2452         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
2453         if (mLayout != null) {
2454             mLayout.dispatchAttachedToWindow(this);
2455         }
2456         mPostedAnimatorRunner = false;
2457 
2458         if (ALLOW_THREAD_GAP_WORK) {
2459             // Register with gap worker
2460             mGapWorker = GapWorker.sGapWorker.get();
2461             if (mGapWorker == null) {
2462                 mGapWorker = new GapWorker();
2463 
2464                 // break 60 fps assumption if data from display appears valid
2465                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
2466                 Display display = getDisplay();
2467                 float refreshRate = 60.0f;
2468                 if (!isInEditMode() && display != null) {
2469                     float displayRefreshRate = display.getRefreshRate();
2470                     if (displayRefreshRate >= 30.0f) {
2471                         refreshRate = displayRefreshRate;
2472                     }
2473                 }
2474                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
2475                 GapWorker.sGapWorker.set(mGapWorker);
2476             }
2477             mGapWorker.add(this);
2478         }
2479     }
2480 
2481     @Override
onDetachedFromWindow()2482     protected void onDetachedFromWindow() {
2483         super.onDetachedFromWindow();
2484         if (mItemAnimator != null) {
2485             mItemAnimator.endAnimations();
2486         }
2487         stopScroll();
2488         mIsAttached = false;
2489         if (mLayout != null) {
2490             mLayout.dispatchDetachedFromWindow(this, mRecycler);
2491         }
2492         mPendingAccessibilityImportanceChange.clear();
2493         removeCallbacks(mItemAnimatorRunner);
2494         mViewInfoStore.onDetach();
2495 
2496         if (ALLOW_THREAD_GAP_WORK) {
2497             // Unregister with gap worker
2498             mGapWorker.remove(this);
2499             mGapWorker = null;
2500         }
2501     }
2502 
2503     /**
2504      * Returns true if RecyclerView is attached to window.
2505      */
2506     // @override
isAttachedToWindow()2507     public boolean isAttachedToWindow() {
2508         return mIsAttached;
2509     }
2510 
2511     /**
2512      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2513      * {@link IllegalStateException} if it <b>is not</b>.
2514      *
2515      * @param message The message for the exception. Can be null.
2516      * @see #assertNotInLayoutOrScroll(String)
2517      */
assertInLayoutOrScroll(String message)2518     void assertInLayoutOrScroll(String message) {
2519         if (!isComputingLayout()) {
2520             if (message == null) {
2521                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
2522                         + "computing a layout or scrolling");
2523             }
2524             throw new IllegalStateException(message);
2525 
2526         }
2527     }
2528 
2529     /**
2530      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
2531      * {@link IllegalStateException} if it <b>is</b>.
2532      *
2533      * @param message The message for the exception. Can be null.
2534      * @see #assertInLayoutOrScroll(String)
2535      */
assertNotInLayoutOrScroll(String message)2536     void assertNotInLayoutOrScroll(String message) {
2537         if (isComputingLayout()) {
2538             if (message == null) {
2539                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
2540                         + "computing a layout or scrolling");
2541             }
2542             throw new IllegalStateException(message);
2543         }
2544         if (mDispatchScrollCounter > 0) {
2545             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
2546                     + " during a measure & layout pass where you cannot change the RecyclerView"
2547                     + " data. Any method call that might change the structure of the RecyclerView"
2548                     + " or the adapter contents should be postponed to the next frame.",
2549                     new IllegalStateException(""));
2550         }
2551     }
2552 
2553     /**
2554      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
2555      * to child views or this view's standard scrolling behavior.
2556      *
2557      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
2558      * returns true from
2559      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
2560      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
2561      * for each incoming MotionEvent until the end of the gesture.</p>
2562      *
2563      * @param listener Listener to add
2564      * @see SimpleOnItemTouchListener
2565      */
addOnItemTouchListener(OnItemTouchListener listener)2566     public void addOnItemTouchListener(OnItemTouchListener listener) {
2567         mOnItemTouchListeners.add(listener);
2568     }
2569 
2570     /**
2571      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
2572      *
2573      * @param listener Listener to remove
2574      */
removeOnItemTouchListener(OnItemTouchListener listener)2575     public void removeOnItemTouchListener(OnItemTouchListener listener) {
2576         mOnItemTouchListeners.remove(listener);
2577         if (mActiveOnItemTouchListener == listener) {
2578             mActiveOnItemTouchListener = null;
2579         }
2580     }
2581 
dispatchOnItemTouchIntercept(MotionEvent e)2582     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
2583         final int action = e.getAction();
2584         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
2585             mActiveOnItemTouchListener = null;
2586         }
2587 
2588         final int listenerCount = mOnItemTouchListeners.size();
2589         for (int i = 0; i < listenerCount; i++) {
2590             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2591             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
2592                 mActiveOnItemTouchListener = listener;
2593                 return true;
2594             }
2595         }
2596         return false;
2597     }
2598 
dispatchOnItemTouch(MotionEvent e)2599     private boolean dispatchOnItemTouch(MotionEvent e) {
2600         final int action = e.getAction();
2601         if (mActiveOnItemTouchListener != null) {
2602             if (action == MotionEvent.ACTION_DOWN) {
2603                 // Stale state from a previous gesture, we're starting a new one. Clear it.
2604                 mActiveOnItemTouchListener = null;
2605             } else {
2606                 mActiveOnItemTouchListener.onTouchEvent(this, e);
2607                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
2608                     // Clean up for the next gesture.
2609                     mActiveOnItemTouchListener = null;
2610                 }
2611                 return true;
2612             }
2613         }
2614 
2615         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
2616         // as called from onInterceptTouchEvent; skip it.
2617         if (action != MotionEvent.ACTION_DOWN) {
2618             final int listenerCount = mOnItemTouchListeners.size();
2619             for (int i = 0; i < listenerCount; i++) {
2620                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2621                 if (listener.onInterceptTouchEvent(this, e)) {
2622                     mActiveOnItemTouchListener = listener;
2623                     return true;
2624                 }
2625             }
2626         }
2627         return false;
2628     }
2629 
2630     @Override
onInterceptTouchEvent(MotionEvent e)2631     public boolean onInterceptTouchEvent(MotionEvent e) {
2632         if (mLayoutFrozen) {
2633             // When layout is frozen,  RV does not intercept the motion event.
2634             // A child view e.g. a button may still get the click.
2635             return false;
2636         }
2637         if (dispatchOnItemTouchIntercept(e)) {
2638             cancelTouch();
2639             return true;
2640         }
2641 
2642         if (mLayout == null) {
2643             return false;
2644         }
2645 
2646         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2647         final boolean canScrollVertically = mLayout.canScrollVertically();
2648 
2649         if (mVelocityTracker == null) {
2650             mVelocityTracker = VelocityTracker.obtain();
2651         }
2652         mVelocityTracker.addMovement(e);
2653 
2654         final int action = e.getActionMasked();
2655         final int actionIndex = e.getActionIndex();
2656 
2657         switch (action) {
2658             case MotionEvent.ACTION_DOWN:
2659                 if (mIgnoreMotionEventTillDown) {
2660                     mIgnoreMotionEventTillDown = false;
2661                 }
2662                 mScrollPointerId = e.getPointerId(0);
2663                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2664                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2665 
2666                 if (mScrollState == SCROLL_STATE_SETTLING) {
2667                     getParent().requestDisallowInterceptTouchEvent(true);
2668                     setScrollState(SCROLL_STATE_DRAGGING);
2669                 }
2670 
2671                 // Clear the nested offsets
2672                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
2673 
2674                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
2675                 if (canScrollHorizontally) {
2676                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
2677                 }
2678                 if (canScrollVertically) {
2679                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
2680                 }
2681                 startNestedScroll(nestedScrollAxis);
2682                 break;
2683 
2684             case MotionEvent.ACTION_POINTER_DOWN:
2685                 mScrollPointerId = e.getPointerId(actionIndex);
2686                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2687                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2688                 break;
2689 
2690             case MotionEvent.ACTION_MOVE: {
2691                 final int index = e.findPointerIndex(mScrollPointerId);
2692                 if (index < 0) {
2693                     Log.e(TAG, "Error processing scroll; pointer index for id "
2694                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2695                     return false;
2696                 }
2697 
2698                 final int x = (int) (e.getX(index) + 0.5f);
2699                 final int y = (int) (e.getY(index) + 0.5f);
2700                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2701                     final int dx = x - mInitialTouchX;
2702                     final int dy = y - mInitialTouchY;
2703                     boolean startScroll = false;
2704                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2705                         mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
2706                         startScroll = true;
2707                     }
2708                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2709                         mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
2710                         startScroll = true;
2711                     }
2712                     if (startScroll) {
2713                         setScrollState(SCROLL_STATE_DRAGGING);
2714                     }
2715                 }
2716             } break;
2717 
2718             case MotionEvent.ACTION_POINTER_UP: {
2719                 onPointerUp(e);
2720             } break;
2721 
2722             case MotionEvent.ACTION_UP: {
2723                 mVelocityTracker.clear();
2724                 stopNestedScroll();
2725             } break;
2726 
2727             case MotionEvent.ACTION_CANCEL: {
2728                 cancelTouch();
2729             }
2730         }
2731         return mScrollState == SCROLL_STATE_DRAGGING;
2732     }
2733 
2734     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)2735     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2736         final int listenerCount = mOnItemTouchListeners.size();
2737         for (int i = 0; i < listenerCount; i++) {
2738             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
2739             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
2740         }
2741         super.requestDisallowInterceptTouchEvent(disallowIntercept);
2742     }
2743 
2744     @Override
onTouchEvent(MotionEvent e)2745     public boolean onTouchEvent(MotionEvent e) {
2746         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
2747             return false;
2748         }
2749         if (dispatchOnItemTouch(e)) {
2750             cancelTouch();
2751             return true;
2752         }
2753 
2754         if (mLayout == null) {
2755             return false;
2756         }
2757 
2758         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
2759         final boolean canScrollVertically = mLayout.canScrollVertically();
2760 
2761         if (mVelocityTracker == null) {
2762             mVelocityTracker = VelocityTracker.obtain();
2763         }
2764         boolean eventAddedToVelocityTracker = false;
2765 
2766         final MotionEvent vtev = MotionEvent.obtain(e);
2767         final int action = e.getActionMasked();
2768         final int actionIndex = e.getActionIndex();
2769 
2770         if (action == MotionEvent.ACTION_DOWN) {
2771             mNestedOffsets[0] = mNestedOffsets[1] = 0;
2772         }
2773         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
2774 
2775         switch (action) {
2776             case MotionEvent.ACTION_DOWN: {
2777                 mScrollPointerId = e.getPointerId(0);
2778                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
2779                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
2780 
2781                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
2782                 if (canScrollHorizontally) {
2783                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
2784                 }
2785                 if (canScrollVertically) {
2786                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
2787                 }
2788                 startNestedScroll(nestedScrollAxis);
2789             } break;
2790 
2791             case MotionEvent.ACTION_POINTER_DOWN: {
2792                 mScrollPointerId = e.getPointerId(actionIndex);
2793                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
2794                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
2795             } break;
2796 
2797             case MotionEvent.ACTION_MOVE: {
2798                 final int index = e.findPointerIndex(mScrollPointerId);
2799                 if (index < 0) {
2800                     Log.e(TAG, "Error processing scroll; pointer index for id "
2801                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
2802                     return false;
2803                 }
2804 
2805                 final int x = (int) (e.getX(index) + 0.5f);
2806                 final int y = (int) (e.getY(index) + 0.5f);
2807                 int dx = mLastTouchX - x;
2808                 int dy = mLastTouchY - y;
2809 
2810                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
2811                     dx -= mScrollConsumed[0];
2812                     dy -= mScrollConsumed[1];
2813                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
2814                     // Updated the nested offsets
2815                     mNestedOffsets[0] += mScrollOffset[0];
2816                     mNestedOffsets[1] += mScrollOffset[1];
2817                 }
2818 
2819                 if (mScrollState != SCROLL_STATE_DRAGGING) {
2820                     boolean startScroll = false;
2821                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
2822                         if (dx > 0) {
2823                             dx -= mTouchSlop;
2824                         } else {
2825                             dx += mTouchSlop;
2826                         }
2827                         startScroll = true;
2828                     }
2829                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
2830                         if (dy > 0) {
2831                             dy -= mTouchSlop;
2832                         } else {
2833                             dy += mTouchSlop;
2834                         }
2835                         startScroll = true;
2836                     }
2837                     if (startScroll) {
2838                         setScrollState(SCROLL_STATE_DRAGGING);
2839                     }
2840                 }
2841 
2842                 if (mScrollState == SCROLL_STATE_DRAGGING) {
2843                     mLastTouchX = x - mScrollOffset[0];
2844                     mLastTouchY = y - mScrollOffset[1];
2845 
2846                     if (scrollByInternal(
2847                             canScrollHorizontally ? dx : 0,
2848                             canScrollVertically ? dy : 0,
2849                             vtev)) {
2850                         getParent().requestDisallowInterceptTouchEvent(true);
2851                     }
2852                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
2853                         mGapWorker.postFromTraversal(this, dx, dy);
2854                     }
2855                 }
2856             } break;
2857 
2858             case MotionEvent.ACTION_POINTER_UP: {
2859                 onPointerUp(e);
2860             } break;
2861 
2862             case MotionEvent.ACTION_UP: {
2863                 mVelocityTracker.addMovement(vtev);
2864                 eventAddedToVelocityTracker = true;
2865                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
2866                 final float xvel = canScrollHorizontally
2867                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
2868                 final float yvel = canScrollVertically
2869                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
2870                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
2871                     setScrollState(SCROLL_STATE_IDLE);
2872                 }
2873                 resetTouch();
2874             } break;
2875 
2876             case MotionEvent.ACTION_CANCEL: {
2877                 cancelTouch();
2878             } break;
2879         }
2880 
2881         if (!eventAddedToVelocityTracker) {
2882             mVelocityTracker.addMovement(vtev);
2883         }
2884         vtev.recycle();
2885 
2886         return true;
2887     }
2888 
resetTouch()2889     private void resetTouch() {
2890         if (mVelocityTracker != null) {
2891             mVelocityTracker.clear();
2892         }
2893         stopNestedScroll();
2894         releaseGlows();
2895     }
2896 
cancelTouch()2897     private void cancelTouch() {
2898         resetTouch();
2899         setScrollState(SCROLL_STATE_IDLE);
2900     }
2901 
onPointerUp(MotionEvent e)2902     private void onPointerUp(MotionEvent e) {
2903         final int actionIndex = e.getActionIndex();
2904         if (e.getPointerId(actionIndex) == mScrollPointerId) {
2905             // Pick a new pointer to pick up the slack.
2906             final int newIndex = actionIndex == 0 ? 1 : 0;
2907             mScrollPointerId = e.getPointerId(newIndex);
2908             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
2909             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
2910         }
2911     }
2912 
2913     // @Override
onGenericMotionEvent(MotionEvent event)2914     public boolean onGenericMotionEvent(MotionEvent event) {
2915         if (mLayout == null) {
2916             return false;
2917         }
2918         if (mLayoutFrozen) {
2919             return false;
2920         }
2921         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
2922             if (event.getAction() == MotionEvent.ACTION_SCROLL) {
2923                 final float vScroll, hScroll;
2924                 if (mLayout.canScrollVertically()) {
2925                     // Inverse the sign of the vertical scroll to align the scroll orientation
2926                     // with AbsListView.
2927                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
2928                 } else {
2929                     vScroll = 0f;
2930                 }
2931                 if (mLayout.canScrollHorizontally()) {
2932                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
2933                 } else {
2934                     hScroll = 0f;
2935                 }
2936 
2937                 if (vScroll != 0 || hScroll != 0) {
2938                     final float scrollFactor = getScrollFactor();
2939                     scrollByInternal((int) (hScroll * scrollFactor),
2940                             (int) (vScroll * scrollFactor), event);
2941                 }
2942             }
2943         }
2944         return false;
2945     }
2946 
2947     /**
2948      * Ported from View.getVerticalScrollFactor.
2949      */
getScrollFactor()2950     private float getScrollFactor() {
2951         if (mScrollFactor == Float.MIN_VALUE) {
2952             TypedValue outValue = new TypedValue();
2953             if (getContext().getTheme().resolveAttribute(
2954                     android.R.attr.listPreferredItemHeight, outValue, true)) {
2955                 mScrollFactor = outValue.getDimension(
2956                         getContext().getResources().getDisplayMetrics());
2957             } else {
2958                 return 0; //listPreferredItemHeight is not defined, no generic scrolling
2959             }
2960         }
2961         return mScrollFactor;
2962     }
2963 
2964     @Override
onMeasure(int widthSpec, int heightSpec)2965     protected void onMeasure(int widthSpec, int heightSpec) {
2966         if (mLayout == null) {
2967             defaultOnMeasure(widthSpec, heightSpec);
2968             return;
2969         }
2970         if (mLayout.mAutoMeasure) {
2971             final int widthMode = MeasureSpec.getMode(widthSpec);
2972             final int heightMode = MeasureSpec.getMode(heightSpec);
2973             final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
2974                     && heightMode == MeasureSpec.EXACTLY;
2975             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
2976             if (skipMeasure || mAdapter == null) {
2977                 return;
2978             }
2979             if (mState.mLayoutStep == State.STEP_START) {
2980                 dispatchLayoutStep1();
2981             }
2982             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
2983             // consistency
2984             mLayout.setMeasureSpecs(widthSpec, heightSpec);
2985             mState.mIsMeasuring = true;
2986             dispatchLayoutStep2();
2987 
2988             // now we can get the width and height from the children.
2989             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
2990 
2991             // if RecyclerView has non-exact width and height and if there is at least one child
2992             // which also has non-exact width & height, we have to re-measure.
2993             if (mLayout.shouldMeasureTwice()) {
2994                 mLayout.setMeasureSpecs(
2995                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
2996                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
2997                 mState.mIsMeasuring = true;
2998                 dispatchLayoutStep2();
2999                 // now we can get the width and height from the children.
3000                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
3001             }
3002         } else {
3003             if (mHasFixedSize) {
3004                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3005                 return;
3006             }
3007             // custom onMeasure
3008             if (mAdapterUpdateDuringMeasure) {
3009                 eatRequestLayout();
3010                 onEnterLayoutOrScroll();
3011                 processAdapterUpdatesAndSetAnimationFlags();
3012                 onExitLayoutOrScroll();
3013 
3014                 if (mState.mRunPredictiveAnimations) {
3015                     mState.mInPreLayout = true;
3016                 } else {
3017                     // consume remaining updates to provide a consistent state with the layout pass.
3018                     mAdapterHelper.consumeUpdatesInOnePass();
3019                     mState.mInPreLayout = false;
3020                 }
3021                 mAdapterUpdateDuringMeasure = false;
3022                 resumeRequestLayout(false);
3023             }
3024 
3025             if (mAdapter != null) {
3026                 mState.mItemCount = mAdapter.getItemCount();
3027             } else {
3028                 mState.mItemCount = 0;
3029             }
3030             eatRequestLayout();
3031             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
3032             resumeRequestLayout(false);
3033             mState.mInPreLayout = false; // clear
3034         }
3035     }
3036 
3037     /**
3038      * Used when onMeasure is called before layout manager is set
3039      */
defaultOnMeasure(int widthSpec, int heightSpec)3040     void defaultOnMeasure(int widthSpec, int heightSpec) {
3041         // calling LayoutManager here is not pretty but that API is already public and it is better
3042         // than creating another method since this is internal.
3043         final int width = LayoutManager.chooseSize(widthSpec,
3044                 getPaddingLeft() + getPaddingRight(),
3045                 getMinimumWidth());
3046         final int height = LayoutManager.chooseSize(heightSpec,
3047                 getPaddingTop() + getPaddingBottom(),
3048                 getMinimumHeight());
3049 
3050         setMeasuredDimension(width, height);
3051     }
3052 
3053     @Override
onSizeChanged(int w, int h, int oldw, int oldh)3054     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
3055         super.onSizeChanged(w, h, oldw, oldh);
3056         if (w != oldw || h != oldh) {
3057             invalidateGlows();
3058             // layout's w/h are updated during measure/layout steps.
3059         }
3060     }
3061 
3062     /**
3063      * Sets the {@link ItemAnimator} that will handle animations involving changes
3064      * to the items in this RecyclerView. By default, RecyclerView instantiates and
3065      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
3066      * enabled for the RecyclerView depends on the ItemAnimator and whether
3067      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
3068      * supports item animations}.
3069      *
3070      * @param animator The ItemAnimator being set. If null, no animations will occur
3071      * when changes occur to the items in this RecyclerView.
3072      */
setItemAnimator(ItemAnimator animator)3073     public void setItemAnimator(ItemAnimator animator) {
3074         if (mItemAnimator != null) {
3075             mItemAnimator.endAnimations();
3076             mItemAnimator.setListener(null);
3077         }
3078         mItemAnimator = animator;
3079         if (mItemAnimator != null) {
3080             mItemAnimator.setListener(mItemAnimatorListener);
3081         }
3082     }
3083 
onEnterLayoutOrScroll()3084     void onEnterLayoutOrScroll() {
3085         mLayoutOrScrollCounter++;
3086     }
3087 
onExitLayoutOrScroll()3088     void onExitLayoutOrScroll() {
3089         mLayoutOrScrollCounter--;
3090         if (mLayoutOrScrollCounter < 1) {
3091             if (DEBUG && mLayoutOrScrollCounter < 0) {
3092                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
3093                         + "Some calls are not matching");
3094             }
3095             mLayoutOrScrollCounter = 0;
3096             dispatchContentChangedIfNecessary();
3097             dispatchPendingImportantForAccessibilityChanges();
3098         }
3099     }
3100 
isAccessibilityEnabled()3101     boolean isAccessibilityEnabled() {
3102         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
3103     }
3104 
dispatchContentChangedIfNecessary()3105     private void dispatchContentChangedIfNecessary() {
3106         final int flags = mEatenAccessibilityChangeFlags;
3107         mEatenAccessibilityChangeFlags = 0;
3108         if (flags != 0 && isAccessibilityEnabled()) {
3109             final AccessibilityEvent event = AccessibilityEvent.obtain();
3110             event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
3111             event.setContentChangeTypes(flags);
3112             sendAccessibilityEventUnchecked(event);
3113         }
3114     }
3115 
3116     /**
3117      * Returns whether RecyclerView is currently computing a layout.
3118      * <p>
3119      * If this method returns true, it means that RecyclerView is in a lockdown state and any
3120      * attempt to update adapter contents will result in an exception because adapter contents
3121      * cannot be changed while RecyclerView is trying to compute the layout.
3122      * <p>
3123      * It is very unlikely that your code will be running during this state as it is
3124      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
3125      * in response to system events (touch, accessibility etc).
3126      * <p>
3127      * This case may happen if you have some custom logic to change adapter contents in
3128      * response to a View callback (e.g. focus change callback) which might be triggered during a
3129      * layout calculation. In these cases, you should just postpone the change using a Handler or a
3130      * similar mechanism.
3131      *
3132      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
3133      *         otherwise
3134      */
isComputingLayout()3135     public boolean isComputingLayout() {
3136         return mLayoutOrScrollCounter > 0;
3137     }
3138 
3139     /**
3140      * Returns true if an accessibility event should not be dispatched now. This happens when an
3141      * accessibility request arrives while RecyclerView does not have a stable state which is very
3142      * hard to handle for a LayoutManager. Instead, this method records necessary information about
3143      * the event and dispatches a window change event after the critical section is finished.
3144      *
3145      * @return True if the accessibility event should be postponed.
3146      */
shouldDeferAccessibilityEvent(AccessibilityEvent event)3147     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
3148         if (isComputingLayout()) {
3149             int type = 0;
3150             if (event != null) {
3151                 type = event.getContentChangeTypes();
3152             }
3153             if (type == 0) {
3154                 type = AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
3155             }
3156             mEatenAccessibilityChangeFlags |= type;
3157             return true;
3158         }
3159         return false;
3160     }
3161 
3162     @Override
sendAccessibilityEventUnchecked(AccessibilityEvent event)3163     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
3164         if (shouldDeferAccessibilityEvent(event)) {
3165             return;
3166         }
3167         super.sendAccessibilityEventUnchecked(event);
3168     }
3169 
3170     /**
3171      * Gets the current ItemAnimator for this RecyclerView. A null return value
3172      * indicates that there is no animator and that item changes will happen without
3173      * any animations. By default, RecyclerView instantiates and
3174      * uses an instance of {@link DefaultItemAnimator}.
3175      *
3176      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
3177      * when changes occur to the items in this RecyclerView.
3178      */
getItemAnimator()3179     public ItemAnimator getItemAnimator() {
3180         return mItemAnimator;
3181     }
3182 
3183     /**
3184      * Post a runnable to the next frame to run pending item animations. Only the first such
3185      * request will be posted, governed by the mPostedAnimatorRunner flag.
3186      */
postAnimationRunner()3187     void postAnimationRunner() {
3188         if (!mPostedAnimatorRunner && mIsAttached) {
3189             postOnAnimation(mItemAnimatorRunner);
3190             mPostedAnimatorRunner = true;
3191         }
3192     }
3193 
predictiveItemAnimationsEnabled()3194     private boolean predictiveItemAnimationsEnabled() {
3195         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
3196     }
3197 
3198     /**
3199      * Consumes adapter updates and calculates which type of animations we want to run.
3200      * Called in onMeasure and dispatchLayout.
3201      * <p>
3202      * This method may process only the pre-layout state of updates or all of them.
3203      */
processAdapterUpdatesAndSetAnimationFlags()3204     private void processAdapterUpdatesAndSetAnimationFlags() {
3205         if (mDataSetHasChangedAfterLayout) {
3206             // Processing these items have no value since data set changed unexpectedly.
3207             // Instead, we just reset it.
3208             mAdapterHelper.reset();
3209             mLayout.onItemsChanged(this);
3210         }
3211         // simple animations are a subset of advanced animations (which will cause a
3212         // pre-layout step)
3213         // If layout supports predictive animations, pre-process to decide if we want to run them
3214         if (predictiveItemAnimationsEnabled()) {
3215             mAdapterHelper.preProcess();
3216         } else {
3217             mAdapterHelper.consumeUpdatesInOnePass();
3218         }
3219         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
3220         mState.mRunSimpleAnimations = mFirstLayoutComplete
3221                 && mItemAnimator != null
3222                 && (mDataSetHasChangedAfterLayout
3223                         || animationTypeSupported
3224                         || mLayout.mRequestedSimpleAnimations)
3225                 && (!mDataSetHasChangedAfterLayout
3226                         || mAdapter.hasStableIds());
3227         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
3228                 && animationTypeSupported
3229                 && !mDataSetHasChangedAfterLayout
3230                 && predictiveItemAnimationsEnabled();
3231     }
3232 
3233     /**
3234      * Wrapper around layoutChildren() that handles animating changes caused by layout.
3235      * Animations work on the assumption that there are five different kinds of items
3236      * in play:
3237      * PERSISTENT: items are visible before and after layout
3238      * REMOVED: items were visible before layout and were removed by the app
3239      * ADDED: items did not exist before layout and were added by the app
3240      * DISAPPEARING: items exist in the data set before/after, but changed from
3241      * visible to non-visible in the process of layout (they were moved off
3242      * screen as a side-effect of other changes)
3243      * APPEARING: items exist in the data set before/after, but changed from
3244      * non-visible to visible in the process of layout (they were moved on
3245      * screen as a side-effect of other changes)
3246      * The overall approach figures out what items exist before/after layout and
3247      * infers one of the five above states for each of the items. Then the animations
3248      * are set up accordingly:
3249      * PERSISTENT views are animated via
3250      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3251      * DISAPPEARING views are animated via
3252      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3253      * APPEARING views are animated via
3254      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
3255      * and changed views are animated via
3256      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
3257      */
dispatchLayout()3258     void dispatchLayout() {
3259         if (mAdapter == null) {
3260             Log.e(TAG, "No adapter attached; skipping layout");
3261             // leave the state in START
3262             return;
3263         }
3264         if (mLayout == null) {
3265             Log.e(TAG, "No layout manager attached; skipping layout");
3266             // leave the state in START
3267             return;
3268         }
3269         mState.mIsMeasuring = false;
3270         if (mState.mLayoutStep == State.STEP_START) {
3271             dispatchLayoutStep1();
3272             mLayout.setExactMeasureSpecsFrom(this);
3273             dispatchLayoutStep2();
3274         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
3275                 || mLayout.getHeight() != getHeight()) {
3276             // First 2 steps are done in onMeasure but looks like we have to run again due to
3277             // changed size.
3278             mLayout.setExactMeasureSpecsFrom(this);
3279             dispatchLayoutStep2();
3280         } else {
3281             // always make sure we sync them (to ensure mode is exact)
3282             mLayout.setExactMeasureSpecsFrom(this);
3283         }
3284         dispatchLayoutStep3();
3285     }
3286 
saveFocusInfo()3287     private void saveFocusInfo() {
3288         View child = null;
3289         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
3290             child = getFocusedChild();
3291         }
3292 
3293         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
3294         if (focusedVh == null) {
3295             resetFocusInfo();
3296         } else {
3297             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
3298             // mFocusedItemPosition should hold the current adapter position of the previously
3299             // focused item. If the item is removed, we store the previous adapter position of the
3300             // removed item.
3301             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
3302                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
3303                             : focusedVh.getAdapterPosition());
3304             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
3305         }
3306     }
3307 
resetFocusInfo()3308     private void resetFocusInfo() {
3309         mState.mFocusedItemId = NO_ID;
3310         mState.mFocusedItemPosition = NO_POSITION;
3311         mState.mFocusedSubChildId = View.NO_ID;
3312     }
3313 
3314     /**
3315      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
3316      * previously focused item. It first traverses the adapter forward to find a focusable candidate
3317      * and if no such candidate is found, it reverses the focus search direction for the items
3318      * before the mFocusedItemPosition'th index;
3319      * @return The best candidate to request focus on, or null if no such candidate exists. Null
3320      * indicates all the existing adapter items are unfocusable.
3321      */
3322     @Nullable
findNextViewToFocus()3323     private View findNextViewToFocus() {
3324         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
3325                 : 0;
3326         ViewHolder nextFocus;
3327         final int itemCount = mState.getItemCount();
3328         for (int i = startFocusSearchIndex; i < itemCount; i++) {
3329             nextFocus = findViewHolderForAdapterPosition(i);
3330             if (nextFocus == null) {
3331                 break;
3332             }
3333             if (nextFocus.itemView.hasFocusable()) {
3334                 return nextFocus.itemView;
3335             }
3336         }
3337         final int limit = Math.min(itemCount, startFocusSearchIndex);
3338         for (int i = limit - 1; i >= 0; i--) {
3339             nextFocus = findViewHolderForAdapterPosition(i);
3340             if (nextFocus == null) {
3341                 return null;
3342             }
3343             if (nextFocus.itemView.hasFocusable()) {
3344                 return nextFocus.itemView;
3345             }
3346         }
3347         return null;
3348     }
3349 
recoverFocusFromState()3350     private void recoverFocusFromState() {
3351         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
3352                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
3353                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
3354             // No-op if either of these cases happens:
3355             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
3356             // before its children and is focused (i.e. it already stole the focus away from its
3357             // descendants).
3358             return;
3359         }
3360         // only recover focus if RV itself has the focus or the focused view is hidden
3361         if (!isFocused()) {
3362             final View focusedChild = getFocusedChild();
3363             if (IGNORE_DETACHED_FOCUSED_CHILD
3364                     && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
3365                 // Special handling of API 15-. A focused child can be invalid because mFocus is not
3366                 // cleared when the child is detached (mParent = null),
3367                 // This happens because clearFocus on API 15- does not invalidate mFocus of its
3368                 // parent when this child is detached.
3369                 // For API 16+, this is not an issue because requestFocus takes care of clearing the
3370                 // prior detached focused child. For API 15- the problem happens in 2 cases because
3371                 // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
3372                 // for the current focused item which calls clearChild or 2. when the prior focused
3373                 // child is removed, removeDetachedView called in layout step 3 which calls
3374                 // clearChild. We should ignore this invalid focused child in all our calculations
3375                 // for the next view to receive focus, and apply the focus recovery logic instead.
3376                 if (mChildHelper.getChildCount() == 0) {
3377                     // No children left. Request focus on the RV itself since one of its children
3378                     // was holding focus previously.
3379                     requestFocus();
3380                     return;
3381                 }
3382             } else if (!mChildHelper.isHidden(focusedChild)) {
3383                 // If the currently focused child is hidden, apply the focus recovery logic.
3384                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
3385                 return;
3386             }
3387         }
3388         ViewHolder focusTarget = null;
3389         // RV first attempts to locate the previously focused item to request focus on using
3390         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
3391         // find the next best candidate to request focus on based on mFocusedItemPosition.
3392         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
3393             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
3394         }
3395         View viewToFocus = null;
3396         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
3397                 || !focusTarget.itemView.hasFocusable()) {
3398             if (mChildHelper.getChildCount() > 0) {
3399                 // At this point, RV has focus and either of these conditions are true:
3400                 // 1. There's no previously focused item either because RV received focused before
3401                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
3402                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
3403                 // focusable. In either of these cases, we make sure that RV still passes down the
3404                 // focus to one of its focusable children using a best-effort algorithm.
3405                 viewToFocus = findNextViewToFocus();
3406             }
3407         } else {
3408             // looks like the focused item has been replaced with another view that represents the
3409             // same item in the adapter. Request focus on that.
3410             viewToFocus = focusTarget.itemView;
3411         }
3412 
3413         if (viewToFocus != null) {
3414             if (mState.mFocusedSubChildId != NO_ID) {
3415                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
3416                 if (child != null && child.isFocusable()) {
3417                     viewToFocus = child;
3418                 }
3419             }
3420             viewToFocus.requestFocus();
3421         }
3422     }
3423 
getDeepestFocusedViewWithId(View view)3424     private int getDeepestFocusedViewWithId(View view) {
3425         int lastKnownId = view.getId();
3426         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
3427             view = ((ViewGroup) view).getFocusedChild();
3428             final int id = view.getId();
3429             if (id != View.NO_ID) {
3430                 lastKnownId = view.getId();
3431             }
3432         }
3433         return lastKnownId;
3434     }
3435 
3436     /**
3437      * The first step of a layout where we;
3438      * - process adapter updates
3439      * - decide which animation should run
3440      * - save information about current views
3441      * - If necessary, run predictive layout and save its information
3442      */
dispatchLayoutStep1()3443     private void dispatchLayoutStep1() {
3444         mState.assertLayoutStep(State.STEP_START);
3445         mState.mIsMeasuring = false;
3446         eatRequestLayout();
3447         mViewInfoStore.clear();
3448         onEnterLayoutOrScroll();
3449         processAdapterUpdatesAndSetAnimationFlags();
3450         saveFocusInfo();
3451         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
3452         mItemsAddedOrRemoved = mItemsChanged = false;
3453         mState.mInPreLayout = mState.mRunPredictiveAnimations;
3454         mState.mItemCount = mAdapter.getItemCount();
3455         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3456 
3457         if (mState.mRunSimpleAnimations) {
3458             // Step 0: Find out where all non-removed items are, pre-layout
3459             int count = mChildHelper.getChildCount();
3460             for (int i = 0; i < count; ++i) {
3461                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3462                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
3463                     continue;
3464                 }
3465                 final ItemHolderInfo animationInfo = mItemAnimator
3466                         .recordPreLayoutInformation(mState, holder,
3467                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
3468                                 holder.getUnmodifiedPayloads());
3469                 mViewInfoStore.addToPreLayout(holder, animationInfo);
3470                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
3471                         && !holder.shouldIgnore() && !holder.isInvalid()) {
3472                     long key = getChangedHolderKey(holder);
3473                     // This is NOT the only place where a ViewHolder is added to old change holders
3474                     // list. There is another case where:
3475                     //    * A VH is currently hidden but not deleted
3476                     //    * The hidden item is changed in the adapter
3477                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
3478                     // When this case is detected, RV will un-hide that view and add to the old
3479                     // change holders list.
3480                     mViewInfoStore.addToOldChangeHolders(key, holder);
3481                 }
3482             }
3483         }
3484         if (mState.mRunPredictiveAnimations) {
3485             // Step 1: run prelayout: This will use the old positions of items. The layout manager
3486             // is expected to layout everything, even removed items (though not to add removed
3487             // items back to the container). This gives the pre-layout position of APPEARING views
3488             // which come into existence as part of the real layout.
3489 
3490             // Save old positions so that LayoutManager can run its mapping logic.
3491             saveOldPositions();
3492             final boolean didStructureChange = mState.mStructureChanged;
3493             mState.mStructureChanged = false;
3494             // temporarily disable flag because we are asking for previous layout
3495             mLayout.onLayoutChildren(mRecycler, mState);
3496             mState.mStructureChanged = didStructureChange;
3497 
3498             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
3499                 final View child = mChildHelper.getChildAt(i);
3500                 final ViewHolder viewHolder = getChildViewHolderInt(child);
3501                 if (viewHolder.shouldIgnore()) {
3502                     continue;
3503                 }
3504                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
3505                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
3506                     boolean wasHidden = viewHolder
3507                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3508                     if (!wasHidden) {
3509                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
3510                     }
3511                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
3512                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
3513                     if (wasHidden) {
3514                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
3515                     } else {
3516                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
3517                     }
3518                 }
3519             }
3520             // we don't process disappearing list because they may re-appear in post layout pass.
3521             clearOldPositions();
3522         } else {
3523             clearOldPositions();
3524         }
3525         onExitLayoutOrScroll();
3526         resumeRequestLayout(false);
3527         mState.mLayoutStep = State.STEP_LAYOUT;
3528     }
3529 
3530     /**
3531      * The second layout step where we do the actual layout of the views for the final state.
3532      * This step might be run multiple times if necessary (e.g. measure).
3533      */
dispatchLayoutStep2()3534     private void dispatchLayoutStep2() {
3535         eatRequestLayout();
3536         onEnterLayoutOrScroll();
3537         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
3538         mAdapterHelper.consumeUpdatesInOnePass();
3539         mState.mItemCount = mAdapter.getItemCount();
3540         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
3541 
3542         // Step 2: Run layout
3543         mState.mInPreLayout = false;
3544         mLayout.onLayoutChildren(mRecycler, mState);
3545 
3546         mState.mStructureChanged = false;
3547         mPendingSavedState = null;
3548 
3549         // onLayoutChildren may have caused client code to disable item animations; re-check
3550         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
3551         mState.mLayoutStep = State.STEP_ANIMATIONS;
3552         onExitLayoutOrScroll();
3553         resumeRequestLayout(false);
3554     }
3555 
3556     /**
3557      * The final step of the layout where we save the information about views for animations,
3558      * trigger animations and do any necessary cleanup.
3559      */
dispatchLayoutStep3()3560     private void dispatchLayoutStep3() {
3561         mState.assertLayoutStep(State.STEP_ANIMATIONS);
3562         eatRequestLayout();
3563         onEnterLayoutOrScroll();
3564         mState.mLayoutStep = State.STEP_START;
3565         if (mState.mRunSimpleAnimations) {
3566             // Step 3: Find out where things are now, and process change animations.
3567             // traverse list in reverse because we may call animateChange in the loop which may
3568             // remove the target view holder.
3569             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
3570                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3571                 if (holder.shouldIgnore()) {
3572                     continue;
3573                 }
3574                 long key = getChangedHolderKey(holder);
3575                 final ItemHolderInfo animationInfo = mItemAnimator
3576                         .recordPostLayoutInformation(mState, holder);
3577                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
3578                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
3579                     // run a change animation
3580 
3581                     // If an Item is CHANGED but the updated version is disappearing, it creates
3582                     // a conflicting case.
3583                     // Since a view that is marked as disappearing is likely to be going out of
3584                     // bounds, we run a change animation. Both views will be cleaned automatically
3585                     // once their animations finish.
3586                     // On the other hand, if it is the same view holder instance, we run a
3587                     // disappearing animation instead because we are not going to rebind the updated
3588                     // VH unless it is enforced by the layout manager.
3589                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
3590                             oldChangeViewHolder);
3591                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
3592                     if (oldDisappearing && oldChangeViewHolder == holder) {
3593                         // run disappear animation instead of change
3594                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3595                     } else {
3596                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
3597                                 oldChangeViewHolder);
3598                         // we add and remove so that any post info is merged.
3599                         mViewInfoStore.addToPostLayout(holder, animationInfo);
3600                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
3601                         if (preInfo == null) {
3602                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
3603                         } else {
3604                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
3605                                     oldDisappearing, newDisappearing);
3606                         }
3607                     }
3608                 } else {
3609                     mViewInfoStore.addToPostLayout(holder, animationInfo);
3610                 }
3611             }
3612 
3613             // Step 4: Process view info lists and trigger animations
3614             mViewInfoStore.process(mViewInfoProcessCallback);
3615         }
3616 
3617         mLayout.removeAndRecycleScrapInt(mRecycler);
3618         mState.mPreviousLayoutItemCount = mState.mItemCount;
3619         mDataSetHasChangedAfterLayout = false;
3620         mState.mRunSimpleAnimations = false;
3621 
3622         mState.mRunPredictiveAnimations = false;
3623         mLayout.mRequestedSimpleAnimations = false;
3624         if (mRecycler.mChangedScrap != null) {
3625             mRecycler.mChangedScrap.clear();
3626         }
3627         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
3628             // Initial prefetch has expanded cache, so reset until next prefetch.
3629             // This prevents initial prefetches from expanding the cache permanently.
3630             mLayout.mPrefetchMaxCountObserved = 0;
3631             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
3632             mRecycler.updateViewCacheSize();
3633         }
3634 
3635         mLayout.onLayoutCompleted(mState);
3636         onExitLayoutOrScroll();
3637         resumeRequestLayout(false);
3638         mViewInfoStore.clear();
3639         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
3640             dispatchOnScrolled(0, 0);
3641         }
3642         recoverFocusFromState();
3643         resetFocusInfo();
3644     }
3645 
3646     /**
3647      * This handles the case where there is an unexpected VH missing in the pre-layout map.
3648      * <p>
3649      * We might be able to detect the error in the application which will help the developer to
3650      * resolve the issue.
3651      * <p>
3652      * If it is not an expected error, we at least print an error to notify the developer and ignore
3653      * the animation.
3654      *
3655      * https://code.google.com/p/android/issues/detail?id=193958
3656      *
3657      * @param key The change key
3658      * @param holder Current ViewHolder
3659      * @param oldChangeViewHolder Changed ViewHolder
3660      */
handleMissingPreInfoForChangeError(long key, ViewHolder holder, ViewHolder oldChangeViewHolder)3661     private void handleMissingPreInfoForChangeError(long key,
3662             ViewHolder holder, ViewHolder oldChangeViewHolder) {
3663         // check if two VH have the same key, if so, print that as an error
3664         final int childCount = mChildHelper.getChildCount();
3665         for (int i = 0; i < childCount; i++) {
3666             View view = mChildHelper.getChildAt(i);
3667             ViewHolder other = getChildViewHolderInt(view);
3668             if (other == holder) {
3669                 continue;
3670             }
3671             final long otherKey = getChangedHolderKey(other);
3672             if (otherKey == key) {
3673                 if (mAdapter != null && mAdapter.hasStableIds()) {
3674                     throw new IllegalStateException("Two different ViewHolders have the same stable"
3675                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
3676                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3677                 } else {
3678                     throw new IllegalStateException("Two different ViewHolders have the same change"
3679                             + " ID. This might happen due to inconsistent Adapter update events or"
3680                             + " if the LayoutManager lays out the same View multiple times."
3681                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
3682                 }
3683             }
3684         }
3685         // Very unlikely to happen but if it does, notify the developer.
3686         Log.e(TAG, "Problem while matching changed view holders with the new"
3687                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
3688                 + " cannot be found but it is necessary for " + holder);
3689     }
3690 
3691     /**
3692      * Records the animation information for a view holder that was bounced from hidden list. It
3693      * also clears the bounce back flag.
3694      */
recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder, ItemHolderInfo animationInfo)3695     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
3696             ItemHolderInfo animationInfo) {
3697         // looks like this view bounced back from hidden list!
3698         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
3699         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
3700                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
3701             long key = getChangedHolderKey(viewHolder);
3702             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
3703         }
3704         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
3705     }
3706 
findMinMaxChildLayoutPositions(int[] into)3707     private void findMinMaxChildLayoutPositions(int[] into) {
3708         final int count = mChildHelper.getChildCount();
3709         if (count == 0) {
3710             into[0] = NO_POSITION;
3711             into[1] = NO_POSITION;
3712             return;
3713         }
3714         int minPositionPreLayout = Integer.MAX_VALUE;
3715         int maxPositionPreLayout = Integer.MIN_VALUE;
3716         for (int i = 0; i < count; ++i) {
3717             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
3718             if (holder.shouldIgnore()) {
3719                 continue;
3720             }
3721             final int pos = holder.getLayoutPosition();
3722             if (pos < minPositionPreLayout) {
3723                 minPositionPreLayout = pos;
3724             }
3725             if (pos > maxPositionPreLayout) {
3726                 maxPositionPreLayout = pos;
3727             }
3728         }
3729         into[0] = minPositionPreLayout;
3730         into[1] = maxPositionPreLayout;
3731     }
3732 
didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout)3733     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
3734         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
3735         return mMinMaxLayoutPositions[0] != minPositionPreLayout
3736                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
3737     }
3738 
3739     @Override
removeDetachedView(View child, boolean animate)3740     protected void removeDetachedView(View child, boolean animate) {
3741         ViewHolder vh = getChildViewHolderInt(child);
3742         if (vh != null) {
3743             if (vh.isTmpDetached()) {
3744                 vh.clearTmpDetachFlag();
3745             } else if (!vh.shouldIgnore()) {
3746                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
3747                         + " is not flagged as tmp detached." + vh);
3748             }
3749         }
3750         dispatchChildDetached(child);
3751         super.removeDetachedView(child, animate);
3752     }
3753 
3754     /**
3755      * Returns a unique key to be used while handling change animations.
3756      * It might be child's position or stable id depending on the adapter type.
3757      */
getChangedHolderKey(ViewHolder holder)3758     long getChangedHolderKey(ViewHolder holder) {
3759         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
3760     }
3761 
animateAppearance(@onNull ViewHolder itemHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)3762     void animateAppearance(@NonNull ViewHolder itemHolder,
3763             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
3764         itemHolder.setIsRecyclable(false);
3765         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
3766             postAnimationRunner();
3767         }
3768     }
3769 
animateDisappearance(@onNull ViewHolder holder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)3770     void animateDisappearance(@NonNull ViewHolder holder,
3771             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
3772         addAnimatingView(holder);
3773         holder.setIsRecyclable(false);
3774         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
3775             postAnimationRunner();
3776         }
3777     }
3778 
animateChange(@onNull ViewHolder oldHolder, @NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo, boolean oldHolderDisappearing, boolean newHolderDisappearing)3779     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
3780             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
3781             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
3782         oldHolder.setIsRecyclable(false);
3783         if (oldHolderDisappearing) {
3784             addAnimatingView(oldHolder);
3785         }
3786         if (oldHolder != newHolder) {
3787             if (newHolderDisappearing) {
3788                 addAnimatingView(newHolder);
3789             }
3790             oldHolder.mShadowedHolder = newHolder;
3791             // old holder should disappear after animation ends
3792             addAnimatingView(oldHolder);
3793             mRecycler.unscrapView(oldHolder);
3794             newHolder.setIsRecyclable(false);
3795             newHolder.mShadowingHolder = oldHolder;
3796         }
3797         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
3798             postAnimationRunner();
3799         }
3800     }
3801 
3802     @Override
onLayout(boolean changed, int l, int t, int r, int b)3803     protected void onLayout(boolean changed, int l, int t, int r, int b) {
3804         Trace.beginSection(TRACE_ON_LAYOUT_TAG);
3805         dispatchLayout();
3806         Trace.endSection();
3807         mFirstLayoutComplete = true;
3808     }
3809 
3810     @Override
requestLayout()3811     public void requestLayout() {
3812         if (mEatRequestLayout == 0 && !mLayoutFrozen) {
3813             super.requestLayout();
3814         } else {
3815             mLayoutRequestEaten = true;
3816         }
3817     }
3818 
markItemDecorInsetsDirty()3819     void markItemDecorInsetsDirty() {
3820         final int childCount = mChildHelper.getUnfilteredChildCount();
3821         for (int i = 0; i < childCount; i++) {
3822             final View child = mChildHelper.getUnfilteredChildAt(i);
3823             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
3824         }
3825         mRecycler.markItemDecorInsetsDirty();
3826     }
3827 
3828     @Override
draw(Canvas c)3829     public void draw(Canvas c) {
3830         super.draw(c);
3831 
3832         final int count = mItemDecorations.size();
3833         for (int i = 0; i < count; i++) {
3834             mItemDecorations.get(i).onDrawOver(c, this, mState);
3835         }
3836         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
3837         // need find children closest to edges. Not sure if it is worth the effort.
3838         boolean needsInvalidate = false;
3839         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
3840             final int restore = c.save();
3841             final int padding = mClipToPadding ? getPaddingBottom() : 0;
3842             c.rotate(270);
3843             c.translate(-getHeight() + padding, 0);
3844             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
3845             c.restoreToCount(restore);
3846         }
3847         if (mTopGlow != null && !mTopGlow.isFinished()) {
3848             final int restore = c.save();
3849             if (mClipToPadding) {
3850                 c.translate(getPaddingLeft(), getPaddingTop());
3851             }
3852             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
3853             c.restoreToCount(restore);
3854         }
3855         if (mRightGlow != null && !mRightGlow.isFinished()) {
3856             final int restore = c.save();
3857             final int width = getWidth();
3858             final int padding = mClipToPadding ? getPaddingTop() : 0;
3859             c.rotate(90);
3860             c.translate(-padding, -width);
3861             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
3862             c.restoreToCount(restore);
3863         }
3864         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
3865             final int restore = c.save();
3866             c.rotate(180);
3867             if (mClipToPadding) {
3868                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
3869             } else {
3870                 c.translate(-getWidth(), -getHeight());
3871             }
3872             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
3873             c.restoreToCount(restore);
3874         }
3875 
3876         // If some views are animating, ItemDecorators are likely to move/change with them.
3877         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
3878         // display lists are not invalidated.
3879         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
3880                 && mItemAnimator.isRunning()) {
3881             needsInvalidate = true;
3882         }
3883 
3884         if (needsInvalidate) {
3885             postInvalidateOnAnimation();
3886         }
3887     }
3888 
3889     @Override
onDraw(Canvas c)3890     public void onDraw(Canvas c) {
3891         super.onDraw(c);
3892 
3893         final int count = mItemDecorations.size();
3894         for (int i = 0; i < count; i++) {
3895             mItemDecorations.get(i).onDraw(c, this, mState);
3896         }
3897     }
3898 
3899     @Override
checkLayoutParams(ViewGroup.LayoutParams p)3900     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3901         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
3902     }
3903 
3904     @Override
generateDefaultLayoutParams()3905     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
3906         if (mLayout == null) {
3907             throw new IllegalStateException("RecyclerView has no LayoutManager");
3908         }
3909         return mLayout.generateDefaultLayoutParams();
3910     }
3911 
3912     @Override
generateLayoutParams(AttributeSet attrs)3913     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
3914         if (mLayout == null) {
3915             throw new IllegalStateException("RecyclerView has no LayoutManager");
3916         }
3917         return mLayout.generateLayoutParams(getContext(), attrs);
3918     }
3919 
3920     @Override
generateLayoutParams(ViewGroup.LayoutParams p)3921     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3922         if (mLayout == null) {
3923             throw new IllegalStateException("RecyclerView has no LayoutManager");
3924         }
3925         return mLayout.generateLayoutParams(p);
3926     }
3927 
3928     /**
3929      * Returns true if RecyclerView is currently running some animations.
3930      * <p>
3931      * If you want to be notified when animations are finished, use
3932      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
3933      *
3934      * @return True if there are some item animations currently running or waiting to be started.
3935      */
isAnimating()3936     public boolean isAnimating() {
3937         return mItemAnimator != null && mItemAnimator.isRunning();
3938     }
3939 
saveOldPositions()3940     void saveOldPositions() {
3941         final int childCount = mChildHelper.getUnfilteredChildCount();
3942         for (int i = 0; i < childCount; i++) {
3943             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3944             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
3945                 throw new IllegalStateException("view holder cannot have position -1 unless it"
3946                         + " is removed");
3947             }
3948             if (!holder.shouldIgnore()) {
3949                 holder.saveOldPosition();
3950             }
3951         }
3952     }
3953 
clearOldPositions()3954     void clearOldPositions() {
3955         final int childCount = mChildHelper.getUnfilteredChildCount();
3956         for (int i = 0; i < childCount; i++) {
3957             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3958             if (!holder.shouldIgnore()) {
3959                 holder.clearOldPosition();
3960             }
3961         }
3962         mRecycler.clearOldPositions();
3963     }
3964 
offsetPositionRecordsForMove(int from, int to)3965     void offsetPositionRecordsForMove(int from, int to) {
3966         final int childCount = mChildHelper.getUnfilteredChildCount();
3967         final int start, end, inBetweenOffset;
3968         if (from < to) {
3969             start = from;
3970             end = to;
3971             inBetweenOffset = -1;
3972         } else {
3973             start = to;
3974             end = from;
3975             inBetweenOffset = 1;
3976         }
3977 
3978         for (int i = 0; i < childCount; i++) {
3979             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
3980             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
3981                 continue;
3982             }
3983             if (DEBUG) {
3984                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
3985                         + holder);
3986             }
3987             if (holder.mPosition == from) {
3988                 holder.offsetPosition(to - from, false);
3989             } else {
3990                 holder.offsetPosition(inBetweenOffset, false);
3991             }
3992 
3993             mState.mStructureChanged = true;
3994         }
3995         mRecycler.offsetPositionRecordsForMove(from, to);
3996         requestLayout();
3997     }
3998 
offsetPositionRecordsForInsert(int positionStart, int itemCount)3999     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
4000         final int childCount = mChildHelper.getUnfilteredChildCount();
4001         for (int i = 0; i < childCount; i++) {
4002             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4003             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
4004                 if (DEBUG) {
4005                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
4006                             + holder + " now at position " + (holder.mPosition + itemCount));
4007                 }
4008                 holder.offsetPosition(itemCount, false);
4009                 mState.mStructureChanged = true;
4010             }
4011         }
4012         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
4013         requestLayout();
4014     }
4015 
offsetPositionRecordsForRemove(int positionStart, int itemCount, boolean applyToPreLayout)4016     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
4017             boolean applyToPreLayout) {
4018         final int positionEnd = positionStart + itemCount;
4019         final int childCount = mChildHelper.getUnfilteredChildCount();
4020         for (int i = 0; i < childCount; i++) {
4021             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4022             if (holder != null && !holder.shouldIgnore()) {
4023                 if (holder.mPosition >= positionEnd) {
4024                     if (DEBUG) {
4025                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4026                                 + " holder " + holder + " now at position "
4027                                 + (holder.mPosition - itemCount));
4028                     }
4029                     holder.offsetPosition(-itemCount, applyToPreLayout);
4030                     mState.mStructureChanged = true;
4031                 } else if (holder.mPosition >= positionStart) {
4032                     if (DEBUG) {
4033                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
4034                                 + " holder " + holder + " now REMOVED");
4035                     }
4036                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
4037                             applyToPreLayout);
4038                     mState.mStructureChanged = true;
4039                 }
4040             }
4041         }
4042         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
4043         requestLayout();
4044     }
4045 
4046     /**
4047      * Rebind existing views for the given range, or create as needed.
4048      *
4049      * @param positionStart Adapter position to start at
4050      * @param itemCount Number of views that must explicitly be rebound
4051      */
viewRangeUpdate(int positionStart, int itemCount, Object payload)4052     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
4053         final int childCount = mChildHelper.getUnfilteredChildCount();
4054         final int positionEnd = positionStart + itemCount;
4055 
4056         for (int i = 0; i < childCount; i++) {
4057             final View child = mChildHelper.getUnfilteredChildAt(i);
4058             final ViewHolder holder = getChildViewHolderInt(child);
4059             if (holder == null || holder.shouldIgnore()) {
4060                 continue;
4061             }
4062             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
4063                 // We re-bind these view holders after pre-processing is complete so that
4064                 // ViewHolders have their final positions assigned.
4065                 holder.addFlags(ViewHolder.FLAG_UPDATE);
4066                 holder.addChangePayload(payload);
4067                 // lp cannot be null since we get ViewHolder from it.
4068                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
4069             }
4070         }
4071         mRecycler.viewRangeUpdate(positionStart, itemCount);
4072     }
4073 
canReuseUpdatedViewHolder(ViewHolder viewHolder)4074     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
4075         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
4076                 viewHolder.getUnmodifiedPayloads());
4077     }
4078 
4079 
4080     /**
4081      * Call this method to signal that *all* adapter content has changed (generally, because of
4082      * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
4083      * be discarded or animated. Note that this work is deferred because RecyclerView requires a
4084      * layout to resolve non-incremental changes to the data set.
4085      *
4086      * Attached items are labeled as position unknown, and may no longer be cached.
4087      *
4088      * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
4089      * so calling this method *must* be associated with marking the cache invalid, so that the
4090      * only valid items that remain in the cache, once layout occurs, are prefetched items.
4091      */
setDataSetChangedAfterLayout()4092     void setDataSetChangedAfterLayout() {
4093         if (mDataSetHasChangedAfterLayout) {
4094             return;
4095         }
4096         mDataSetHasChangedAfterLayout = true;
4097         final int childCount = mChildHelper.getUnfilteredChildCount();
4098         for (int i = 0; i < childCount; i++) {
4099             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4100             if (holder != null && !holder.shouldIgnore()) {
4101                 holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
4102             }
4103         }
4104         mRecycler.setAdapterPositionsAsUnknown();
4105 
4106         // immediately mark all views as invalid, so prefetched views can be
4107         // differentiated from views bound to previous data set - both in children, and cache
4108         markKnownViewsInvalid();
4109     }
4110 
4111     /**
4112      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
4113      * data change event.
4114      */
markKnownViewsInvalid()4115     void markKnownViewsInvalid() {
4116         final int childCount = mChildHelper.getUnfilteredChildCount();
4117         for (int i = 0; i < childCount; i++) {
4118             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4119             if (holder != null && !holder.shouldIgnore()) {
4120                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
4121             }
4122         }
4123         markItemDecorInsetsDirty();
4124         mRecycler.markKnownViewsInvalid();
4125     }
4126 
4127     /**
4128      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
4129      * will trigger a {@link #requestLayout()} call.
4130      */
invalidateItemDecorations()4131     public void invalidateItemDecorations() {
4132         if (mItemDecorations.size() == 0) {
4133             return;
4134         }
4135         if (mLayout != null) {
4136             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
4137                     + " or layout");
4138         }
4139         markItemDecorInsetsDirty();
4140         requestLayout();
4141     }
4142 
4143     /**
4144      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
4145      * focus even if the View representing the Item is replaced during a layout calculation.
4146      * <p>
4147      * By default, this value is {@code true}.
4148      *
4149      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
4150      * focus.
4151      *
4152      * @see #setPreserveFocusAfterLayout(boolean)
4153      */
getPreserveFocusAfterLayout()4154     public boolean getPreserveFocusAfterLayout() {
4155         return mPreserveFocusAfterLayout;
4156     }
4157 
4158     /**
4159      * Set whether the RecyclerView should try to keep the same Item focused after a layout
4160      * calculation or not.
4161      * <p>
4162      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
4163      * views may lose focus during a layout calculation as their state changes or they are replaced
4164      * with another view due to type change or animation. In these cases, RecyclerView can request
4165      * focus on the new view automatically.
4166      *
4167      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
4168      *                                 layout calculations. Defaults to true.
4169      *
4170      * @see #getPreserveFocusAfterLayout()
4171      */
setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout)4172     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
4173         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
4174     }
4175 
4176     /**
4177      * Retrieve the {@link ViewHolder} for the given child view.
4178      *
4179      * @param child Child of this RecyclerView to query for its ViewHolder
4180      * @return The child view's ViewHolder
4181      */
getChildViewHolder(View child)4182     public ViewHolder getChildViewHolder(View child) {
4183         final ViewParent parent = child.getParent();
4184         if (parent != null && parent != this) {
4185             throw new IllegalArgumentException("View " + child + " is not a direct child of "
4186                     + this);
4187         }
4188         return getChildViewHolderInt(child);
4189     }
4190 
4191     /**
4192      * Traverses the ancestors of the given view and returns the item view that contains it and
4193      * also a direct child of the RecyclerView. This returned view can be used to get the
4194      * ViewHolder by calling {@link #getChildViewHolder(View)}.
4195      *
4196      * @param view The view that is a descendant of the RecyclerView.
4197      *
4198      * @return The direct child of the RecyclerView which contains the given view or null if the
4199      * provided view is not a descendant of this RecyclerView.
4200      *
4201      * @see #getChildViewHolder(View)
4202      * @see #findContainingViewHolder(View)
4203      */
4204     @Nullable
findContainingItemView(View view)4205     public View findContainingItemView(View view) {
4206         ViewParent parent = view.getParent();
4207         while (parent != null && parent != this && parent instanceof View) {
4208             view = (View) parent;
4209             parent = view.getParent();
4210         }
4211         return parent == this ? view : null;
4212     }
4213 
4214     /**
4215      * Returns the ViewHolder that contains the given view.
4216      *
4217      * @param view The view that is a descendant of the RecyclerView.
4218      *
4219      * @return The ViewHolder that contains the given view or null if the provided view is not a
4220      * descendant of this RecyclerView.
4221      */
4222     @Nullable
findContainingViewHolder(View view)4223     public ViewHolder findContainingViewHolder(View view) {
4224         View itemView = findContainingItemView(view);
4225         return itemView == null ? null : getChildViewHolder(itemView);
4226     }
4227 
4228 
getChildViewHolderInt(View child)4229     static ViewHolder getChildViewHolderInt(View child) {
4230         if (child == null) {
4231             return null;
4232         }
4233         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
4234     }
4235 
4236     /**
4237      * @deprecated use {@link #getChildAdapterPosition(View)} or
4238      * {@link #getChildLayoutPosition(View)}.
4239      */
4240     @Deprecated
getChildPosition(View child)4241     public int getChildPosition(View child) {
4242         return getChildAdapterPosition(child);
4243     }
4244 
4245     /**
4246      * Return the adapter position that the given child view corresponds to.
4247      *
4248      * @param child Child View to query
4249      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
4250      */
getChildAdapterPosition(View child)4251     public int getChildAdapterPosition(View child) {
4252         final ViewHolder holder = getChildViewHolderInt(child);
4253         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
4254     }
4255 
4256     /**
4257      * Return the adapter position of the given child view as of the latest completed layout pass.
4258      * <p>
4259      * This position may not be equal to Item's adapter position if there are pending changes
4260      * in the adapter which have not been reflected to the layout yet.
4261      *
4262      * @param child Child View to query
4263      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
4264      * the View is representing a removed item.
4265      */
getChildLayoutPosition(View child)4266     public int getChildLayoutPosition(View child) {
4267         final ViewHolder holder = getChildViewHolderInt(child);
4268         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
4269     }
4270 
4271     /**
4272      * Return the stable item id that the given child view corresponds to.
4273      *
4274      * @param child Child View to query
4275      * @return Item id corresponding to the given view or {@link #NO_ID}
4276      */
getChildItemId(View child)4277     public long getChildItemId(View child) {
4278         if (mAdapter == null || !mAdapter.hasStableIds()) {
4279             return NO_ID;
4280         }
4281         final ViewHolder holder = getChildViewHolderInt(child);
4282         return holder != null ? holder.getItemId() : NO_ID;
4283     }
4284 
4285     /**
4286      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
4287      * {@link #findViewHolderForAdapterPosition(int)}
4288      */
4289     @Deprecated
findViewHolderForPosition(int position)4290     public ViewHolder findViewHolderForPosition(int position) {
4291         return findViewHolderForPosition(position, false);
4292     }
4293 
4294     /**
4295      * Return the ViewHolder for the item in the given position of the data set as of the latest
4296      * layout pass.
4297      * <p>
4298      * This method checks only the children of RecyclerView. If the item at the given
4299      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4300      * <p>
4301      * Note that when Adapter contents change, ViewHolder positions are not updated until the
4302      * next layout calculation. If there are pending adapter updates, the return value of this
4303      * method may not match your adapter contents. You can use
4304      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
4305      * <p>
4306      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4307      * with the same layout position representing the same Item. In this case, the updated
4308      * ViewHolder will be returned.
4309      *
4310      * @param position The position of the item in the data set of the adapter
4311      * @return The ViewHolder at <code>position</code> or null if there is no such item
4312      */
findViewHolderForLayoutPosition(int position)4313     public ViewHolder findViewHolderForLayoutPosition(int position) {
4314         return findViewHolderForPosition(position, false);
4315     }
4316 
4317     /**
4318      * Return the ViewHolder for the item in the given position of the data set. Unlike
4319      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
4320      * adapter changes that may not be reflected to the layout yet. On the other hand, if
4321      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
4322      * calculated yet, this method will return <code>null</code> since the new positions of views
4323      * are unknown until the layout is calculated.
4324      * <p>
4325      * This method checks only the children of RecyclerView. If the item at the given
4326      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
4327      * <p>
4328      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
4329      * representing the same Item. In this case, the updated ViewHolder will be returned.
4330      *
4331      * @param position The position of the item in the data set of the adapter
4332      * @return The ViewHolder at <code>position</code> or null if there is no such item
4333      */
findViewHolderForAdapterPosition(int position)4334     public ViewHolder findViewHolderForAdapterPosition(int position) {
4335         if (mDataSetHasChangedAfterLayout) {
4336             return null;
4337         }
4338         final int childCount = mChildHelper.getUnfilteredChildCount();
4339         // hidden VHs are not preferred but if that is the only one we find, we rather return it
4340         ViewHolder hidden = null;
4341         for (int i = 0; i < childCount; i++) {
4342             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4343             if (holder != null && !holder.isRemoved()
4344                     && getAdapterPositionFor(holder) == position) {
4345                 if (mChildHelper.isHidden(holder.itemView)) {
4346                     hidden = holder;
4347                 } else {
4348                     return holder;
4349                 }
4350             }
4351         }
4352         return hidden;
4353     }
4354 
findViewHolderForPosition(int position, boolean checkNewPosition)4355     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
4356         final int childCount = mChildHelper.getUnfilteredChildCount();
4357         ViewHolder hidden = null;
4358         for (int i = 0; i < childCount; i++) {
4359             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4360             if (holder != null && !holder.isRemoved()) {
4361                 if (checkNewPosition) {
4362                     if (holder.mPosition != position) {
4363                         continue;
4364                     }
4365                 } else if (holder.getLayoutPosition() != position) {
4366                     continue;
4367                 }
4368                 if (mChildHelper.isHidden(holder.itemView)) {
4369                     hidden = holder;
4370                 } else {
4371                     return holder;
4372                 }
4373             }
4374         }
4375         // This method should not query cached views. It creates a problem during adapter updates
4376         // when we are dealing with already laid out views. Also, for the public method, it is more
4377         // reasonable to return null if position is not laid out.
4378         return hidden;
4379     }
4380 
4381     /**
4382      * Return the ViewHolder for the item with the given id. The RecyclerView must
4383      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
4384      * return a non-null value.
4385      * <p>
4386      * This method checks only the children of RecyclerView. If the item with the given
4387      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
4388      *
4389      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
4390      * same id. In this case, the updated ViewHolder will be returned.
4391      *
4392      * @param id The id for the requested item
4393      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
4394      */
findViewHolderForItemId(long id)4395     public ViewHolder findViewHolderForItemId(long id) {
4396         if (mAdapter == null || !mAdapter.hasStableIds()) {
4397             return null;
4398         }
4399         final int childCount = mChildHelper.getUnfilteredChildCount();
4400         ViewHolder hidden = null;
4401         for (int i = 0; i < childCount; i++) {
4402             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
4403             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
4404                 if (mChildHelper.isHidden(holder.itemView)) {
4405                     hidden = holder;
4406                 } else {
4407                     return holder;
4408                 }
4409             }
4410         }
4411         return hidden;
4412     }
4413 
4414     /**
4415      * Find the topmost view under the given point.
4416      *
4417      * @param x Horizontal position in pixels to search
4418      * @param y Vertical position in pixels to search
4419      * @return The child view under (x, y) or null if no matching child is found
4420      */
findChildViewUnder(float x, float y)4421     public View findChildViewUnder(float x, float y) {
4422         final int count = mChildHelper.getChildCount();
4423         for (int i = count - 1; i >= 0; i--) {
4424             final View child = mChildHelper.getChildAt(i);
4425             final float translationX = child.getTranslationX();
4426             final float translationY = child.getTranslationY();
4427             if (x >= child.getLeft() + translationX
4428                     && x <= child.getRight() + translationX
4429                     && y >= child.getTop() + translationY
4430                     && y <= child.getBottom() + translationY) {
4431                 return child;
4432             }
4433         }
4434         return null;
4435     }
4436 
4437     @Override
drawChild(Canvas canvas, View child, long drawingTime)4438     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
4439         return super.drawChild(canvas, child, drawingTime);
4440     }
4441 
4442     /**
4443      * Offset the bounds of all child views by <code>dy</code> pixels.
4444      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4445      *
4446      * @param dy Vertical pixel offset to apply to the bounds of all child views
4447      */
offsetChildrenVertical(int dy)4448     public void offsetChildrenVertical(int dy) {
4449         final int childCount = mChildHelper.getChildCount();
4450         for (int i = 0; i < childCount; i++) {
4451             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
4452         }
4453     }
4454 
4455     /**
4456      * Called when an item view is attached to this RecyclerView.
4457      *
4458      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4459      * of child views as they become attached. This will be called before a
4460      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
4461      * changes.</p>
4462      *
4463      * @param child Child view that is now attached to this RecyclerView and its associated window
4464      */
onChildAttachedToWindow(View child)4465     public void onChildAttachedToWindow(View child) {
4466     }
4467 
4468     /**
4469      * Called when an item view is detached from this RecyclerView.
4470      *
4471      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
4472      * of child views as they become detached. This will be called as a
4473      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
4474      *
4475      * @param child Child view that is now detached from this RecyclerView and its associated window
4476      */
onChildDetachedFromWindow(View child)4477     public void onChildDetachedFromWindow(View child) {
4478     }
4479 
4480     /**
4481      * Offset the bounds of all child views by <code>dx</code> pixels.
4482      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
4483      *
4484      * @param dx Horizontal pixel offset to apply to the bounds of all child views
4485      */
offsetChildrenHorizontal(int dx)4486     public void offsetChildrenHorizontal(int dx) {
4487         final int childCount = mChildHelper.getChildCount();
4488         for (int i = 0; i < childCount; i++) {
4489             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
4490         }
4491     }
4492 
4493     /**
4494      * Returns the bounds of the view including its decoration and margins.
4495      *
4496      * @param view The view element to check
4497      * @param outBounds A rect that will receive the bounds of the element including its
4498      *                  decoration and margins.
4499      */
getDecoratedBoundsWithMargins(View view, Rect outBounds)4500     public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
4501         getDecoratedBoundsWithMarginsInt(view, outBounds);
4502     }
4503 
getDecoratedBoundsWithMarginsInt(View view, Rect outBounds)4504     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
4505         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
4506         final Rect insets = lp.mDecorInsets;
4507         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
4508                 view.getTop() - insets.top - lp.topMargin,
4509                 view.getRight() + insets.right + lp.rightMargin,
4510                 view.getBottom() + insets.bottom + lp.bottomMargin);
4511     }
4512 
getItemDecorInsetsForChild(View child)4513     Rect getItemDecorInsetsForChild(View child) {
4514         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
4515         if (!lp.mInsetsDirty) {
4516             return lp.mDecorInsets;
4517         }
4518 
4519         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
4520             // changed/invalid items should not be updated until they are rebound.
4521             return lp.mDecorInsets;
4522         }
4523         final Rect insets = lp.mDecorInsets;
4524         insets.set(0, 0, 0, 0);
4525         final int decorCount = mItemDecorations.size();
4526         for (int i = 0; i < decorCount; i++) {
4527             mTempRect.set(0, 0, 0, 0);
4528             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
4529             insets.left += mTempRect.left;
4530             insets.top += mTempRect.top;
4531             insets.right += mTempRect.right;
4532             insets.bottom += mTempRect.bottom;
4533         }
4534         lp.mInsetsDirty = false;
4535         return insets;
4536     }
4537 
4538     /**
4539      * Called when the scroll position of this RecyclerView changes. Subclasses should use
4540      * this method to respond to scrolling within the adapter's data set instead of an explicit
4541      * listener.
4542      *
4543      * <p>This method will always be invoked before listeners. If a subclass needs to perform
4544      * any additional upkeep or bookkeeping after scrolling but before listeners run,
4545      * this is a good place to do so.</p>
4546      *
4547      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
4548      * the distance scrolled in either direction within the adapter's data set instead of absolute
4549      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
4550      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
4551      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
4552      * do not correspond to the data set scroll position. However, some subclasses may choose
4553      * to use these fields as special offsets.</p>
4554      *
4555      * @param dx horizontal distance scrolled in pixels
4556      * @param dy vertical distance scrolled in pixels
4557      */
onScrolled(int dx, int dy)4558     public void onScrolled(int dx, int dy) {
4559         // Do nothing
4560     }
4561 
dispatchOnScrolled(int hresult, int vresult)4562     void dispatchOnScrolled(int hresult, int vresult) {
4563         mDispatchScrollCounter++;
4564         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
4565         // but some general-purpose code may choose to respond to changes this way.
4566         final int scrollX = getScrollX();
4567         final int scrollY = getScrollY();
4568         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
4569 
4570         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
4571         onScrolled(hresult, vresult);
4572 
4573         // Invoke listeners last. Subclassed view methods always handle the event first.
4574         // All internal state is consistent by the time listeners are invoked.
4575         if (mScrollListener != null) {
4576             mScrollListener.onScrolled(this, hresult, vresult);
4577         }
4578         if (mScrollListeners != null) {
4579             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4580                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
4581             }
4582         }
4583         mDispatchScrollCounter--;
4584     }
4585 
4586     /**
4587      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
4588      * method to respond to state changes instead of an explicit listener.
4589      *
4590      * <p>This method will always be invoked before listeners, but after the LayoutManager
4591      * responds to the scroll state change.</p>
4592      *
4593      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
4594      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
4595      */
onScrollStateChanged(int state)4596     public void onScrollStateChanged(int state) {
4597         // Do nothing
4598     }
4599 
dispatchOnScrollStateChanged(int state)4600     void dispatchOnScrollStateChanged(int state) {
4601         // Let the LayoutManager go first; this allows it to bring any properties into
4602         // a consistent state before the RecyclerView subclass responds.
4603         if (mLayout != null) {
4604             mLayout.onScrollStateChanged(state);
4605         }
4606 
4607         // Let the RecyclerView subclass handle this event next; any LayoutManager property
4608         // changes will be reflected by this time.
4609         onScrollStateChanged(state);
4610 
4611         // Listeners go last. All other internal state is consistent by this point.
4612         if (mScrollListener != null) {
4613             mScrollListener.onScrollStateChanged(this, state);
4614         }
4615         if (mScrollListeners != null) {
4616             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
4617                 mScrollListeners.get(i).onScrollStateChanged(this, state);
4618             }
4619         }
4620     }
4621 
4622     /**
4623      * Returns whether there are pending adapter updates which are not yet applied to the layout.
4624      * <p>
4625      * If this method returns <code>true</code>, it means that what user is currently seeing may not
4626      * reflect them adapter contents (depending on what has changed).
4627      * You may use this information to defer or cancel some operations.
4628      * <p>
4629      * This method returns true if RecyclerView has not yet calculated the first layout after it is
4630      * attached to the Window or the Adapter has been replaced.
4631      *
4632      * @return True if there are some adapter updates which are not yet reflected to layout or false
4633      * if layout is up to date.
4634      */
hasPendingAdapterUpdates()4635     public boolean hasPendingAdapterUpdates() {
4636         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
4637                 || mAdapterHelper.hasPendingUpdates();
4638     }
4639 
4640     class ViewFlinger implements Runnable {
4641         private int mLastFlingX;
4642         private int mLastFlingY;
4643         private OverScroller mScroller;
4644         Interpolator mInterpolator = sQuinticInterpolator;
4645 
4646 
4647         // When set to true, postOnAnimation callbacks are delayed until the run method completes
4648         private boolean mEatRunOnAnimationRequest = false;
4649 
4650         // Tracks if postAnimationCallback should be re-attached when it is done
4651         private boolean mReSchedulePostAnimationCallback = false;
4652 
ViewFlinger()4653         ViewFlinger() {
4654             mScroller = new OverScroller(getContext(), sQuinticInterpolator);
4655         }
4656 
4657         @Override
run()4658         public void run() {
4659             if (mLayout == null) {
4660                 stop();
4661                 return; // no layout, cannot scroll.
4662             }
4663             disableRunOnAnimationRequests();
4664             consumePendingUpdateOperations();
4665             // keep a local reference so that if it is changed during onAnimation method, it won't
4666             // cause unexpected behaviors
4667             final OverScroller scroller = mScroller;
4668             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
4669             if (scroller.computeScrollOffset()) {
4670                 final int x = scroller.getCurrX();
4671                 final int y = scroller.getCurrY();
4672                 final int dx = x - mLastFlingX;
4673                 final int dy = y - mLastFlingY;
4674                 int hresult = 0;
4675                 int vresult = 0;
4676                 mLastFlingX = x;
4677                 mLastFlingY = y;
4678                 int overscrollX = 0, overscrollY = 0;
4679                 if (mAdapter != null) {
4680                     eatRequestLayout();
4681                     onEnterLayoutOrScroll();
4682                     Trace.beginSection(TRACE_SCROLL_TAG);
4683                     if (dx != 0) {
4684                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
4685                         overscrollX = dx - hresult;
4686                     }
4687                     if (dy != 0) {
4688                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
4689                         overscrollY = dy - vresult;
4690                     }
4691                     Trace.endSection();
4692                     repositionShadowingViews();
4693 
4694                     onExitLayoutOrScroll();
4695                     resumeRequestLayout(false);
4696 
4697                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
4698                             && smoothScroller.isRunning()) {
4699                         final int adapterSize = mState.getItemCount();
4700                         if (adapterSize == 0) {
4701                             smoothScroller.stop();
4702                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
4703                             smoothScroller.setTargetPosition(adapterSize - 1);
4704                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4705                         } else {
4706                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
4707                         }
4708                     }
4709                 }
4710                 if (!mItemDecorations.isEmpty()) {
4711                     invalidate();
4712                 }
4713                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4714                     considerReleasingGlowsOnScroll(dx, dy);
4715                 }
4716                 if (overscrollX != 0 || overscrollY != 0) {
4717                     final int vel = (int) scroller.getCurrVelocity();
4718 
4719                     int velX = 0;
4720                     if (overscrollX != x) {
4721                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
4722                     }
4723 
4724                     int velY = 0;
4725                     if (overscrollY != y) {
4726                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
4727                     }
4728 
4729                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
4730                         absorbGlows(velX, velY);
4731                     }
4732                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
4733                             && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
4734                         scroller.abortAnimation();
4735                     }
4736                 }
4737                 if (hresult != 0 || vresult != 0) {
4738                     dispatchOnScrolled(hresult, vresult);
4739                 }
4740 
4741                 if (!awakenScrollBars()) {
4742                     invalidate();
4743                 }
4744 
4745                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
4746                         && vresult == dy;
4747                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
4748                         && hresult == dx;
4749                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
4750                         || fullyConsumedVertical;
4751 
4752                 if (scroller.isFinished() || !fullyConsumedAny) {
4753                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
4754                     if (ALLOW_THREAD_GAP_WORK) {
4755                         mPrefetchRegistry.clearPrefetchPositions();
4756                     }
4757                 } else {
4758                     postOnAnimation();
4759                     if (mGapWorker != null) {
4760                         mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
4761                     }
4762                 }
4763             }
4764             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
4765             if (smoothScroller != null) {
4766                 if (smoothScroller.isPendingInitialRun()) {
4767                     smoothScroller.onAnimation(0, 0);
4768                 }
4769                 if (!mReSchedulePostAnimationCallback) {
4770                     smoothScroller.stop(); //stop if it does not trigger any scroll
4771                 }
4772             }
4773             enableRunOnAnimationRequests();
4774         }
4775 
disableRunOnAnimationRequests()4776         private void disableRunOnAnimationRequests() {
4777             mReSchedulePostAnimationCallback = false;
4778             mEatRunOnAnimationRequest = true;
4779         }
4780 
enableRunOnAnimationRequests()4781         private void enableRunOnAnimationRequests() {
4782             mEatRunOnAnimationRequest = false;
4783             if (mReSchedulePostAnimationCallback) {
4784                 postOnAnimation();
4785             }
4786         }
4787 
postOnAnimation()4788         void postOnAnimation() {
4789             if (mEatRunOnAnimationRequest) {
4790                 mReSchedulePostAnimationCallback = true;
4791             } else {
4792                 removeCallbacks(this);
4793                 RecyclerView.this.postOnAnimation(this);
4794             }
4795         }
4796 
fling(int velocityX, int velocityY)4797         public void fling(int velocityX, int velocityY) {
4798             setScrollState(SCROLL_STATE_SETTLING);
4799             mLastFlingX = mLastFlingY = 0;
4800             mScroller.fling(0, 0, velocityX, velocityY,
4801                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
4802             postOnAnimation();
4803         }
4804 
smoothScrollBy(int dx, int dy)4805         public void smoothScrollBy(int dx, int dy) {
4806             smoothScrollBy(dx, dy, 0, 0);
4807         }
4808 
smoothScrollBy(int dx, int dy, int vx, int vy)4809         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
4810             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
4811         }
4812 
distanceInfluenceForSnapDuration(float f)4813         private float distanceInfluenceForSnapDuration(float f) {
4814             f -= 0.5f; // center the values about 0.
4815             f *= 0.3f * Math.PI / 2.0f;
4816             return (float) Math.sin(f);
4817         }
4818 
computeScrollDuration(int dx, int dy, int vx, int vy)4819         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
4820             final int absDx = Math.abs(dx);
4821             final int absDy = Math.abs(dy);
4822             final boolean horizontal = absDx > absDy;
4823             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
4824             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
4825             final int containerSize = horizontal ? getWidth() : getHeight();
4826             final int halfContainerSize = containerSize / 2;
4827             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
4828             final float distance = halfContainerSize + halfContainerSize
4829                     * distanceInfluenceForSnapDuration(distanceRatio);
4830 
4831             final int duration;
4832             if (velocity > 0) {
4833                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
4834             } else {
4835                 float absDelta = (float) (horizontal ? absDx : absDy);
4836                 duration = (int) (((absDelta / containerSize) + 1) * 300);
4837             }
4838             return Math.min(duration, MAX_SCROLL_DURATION);
4839         }
4840 
smoothScrollBy(int dx, int dy, int duration)4841         public void smoothScrollBy(int dx, int dy, int duration) {
4842             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
4843         }
4844 
smoothScrollBy(int dx, int dy, Interpolator interpolator)4845         public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
4846             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
4847                     interpolator == null ? sQuinticInterpolator : interpolator);
4848         }
4849 
smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator)4850         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
4851             if (mInterpolator != interpolator) {
4852                 mInterpolator = interpolator;
4853                 mScroller = new OverScroller(getContext(), interpolator);
4854             }
4855             setScrollState(SCROLL_STATE_SETTLING);
4856             mLastFlingX = mLastFlingY = 0;
4857             mScroller.startScroll(0, 0, dx, dy, duration);
4858             postOnAnimation();
4859         }
4860 
stop()4861         public void stop() {
4862             removeCallbacks(this);
4863             mScroller.abortAnimation();
4864         }
4865 
4866     }
4867 
repositionShadowingViews()4868     void repositionShadowingViews() {
4869         // Fix up shadow views used by change animations
4870         int count = mChildHelper.getChildCount();
4871         for (int i = 0; i < count; i++) {
4872             View view = mChildHelper.getChildAt(i);
4873             ViewHolder holder = getChildViewHolder(view);
4874             if (holder != null && holder.mShadowingHolder != null) {
4875                 View shadowingView = holder.mShadowingHolder.itemView;
4876                 int left = view.getLeft();
4877                 int top = view.getTop();
4878                 if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
4879                     shadowingView.layout(left, top,
4880                             left + shadowingView.getWidth(),
4881                             top + shadowingView.getHeight());
4882                 }
4883             }
4884         }
4885     }
4886 
4887     private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver()4888         RecyclerViewDataObserver() {
4889         }
4890 
4891         @Override
onChanged()4892         public void onChanged() {
4893             assertNotInLayoutOrScroll(null);
4894             mState.mStructureChanged = true;
4895 
4896             setDataSetChangedAfterLayout();
4897             if (!mAdapterHelper.hasPendingUpdates()) {
4898                 requestLayout();
4899             }
4900         }
4901 
4902         @Override
onItemRangeChanged(int positionStart, int itemCount, Object payload)4903         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
4904             assertNotInLayoutOrScroll(null);
4905             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
4906                 triggerUpdateProcessor();
4907             }
4908         }
4909 
4910         @Override
onItemRangeInserted(int positionStart, int itemCount)4911         public void onItemRangeInserted(int positionStart, int itemCount) {
4912             assertNotInLayoutOrScroll(null);
4913             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
4914                 triggerUpdateProcessor();
4915             }
4916         }
4917 
4918         @Override
onItemRangeRemoved(int positionStart, int itemCount)4919         public void onItemRangeRemoved(int positionStart, int itemCount) {
4920             assertNotInLayoutOrScroll(null);
4921             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
4922                 triggerUpdateProcessor();
4923             }
4924         }
4925 
4926         @Override
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)4927         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
4928             assertNotInLayoutOrScroll(null);
4929             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
4930                 triggerUpdateProcessor();
4931             }
4932         }
4933 
triggerUpdateProcessor()4934         void triggerUpdateProcessor() {
4935             if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
4936                 RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
4937             } else {
4938                 mAdapterUpdateDuringMeasure = true;
4939                 requestLayout();
4940             }
4941         }
4942     }
4943 
4944     /**
4945      * RecycledViewPool lets you share Views between multiple RecyclerViews.
4946      * <p>
4947      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
4948      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
4949      * <p>
4950      * RecyclerView automatically creates a pool for itself if you don't provide one.
4951      *
4952      */
4953     public static class RecycledViewPool {
4954         private static final int DEFAULT_MAX_SCRAP = 5;
4955 
4956         /**
4957          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
4958          *
4959          * Note that this tracks running averages of create/bind time across all RecyclerViews
4960          * (and, indirectly, Adapters) that use this pool.
4961          *
4962          * 1) This enables us to track average create and bind times across multiple adapters. Even
4963          * though create (and especially bind) may behave differently for different Adapter
4964          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
4965          *
4966          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
4967          * false for all other views of its type for the same deadline. This prevents items
4968          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
4969          */
4970         static class ScrapData {
4971             @UnsupportedAppUsage
4972             ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
4973             int mMaxScrap = DEFAULT_MAX_SCRAP;
4974             long mCreateRunningAverageNs = 0;
4975             long mBindRunningAverageNs = 0;
4976         }
4977         SparseArray<ScrapData> mScrap = new SparseArray<>();
4978 
4979         private int mAttachCount = 0;
4980 
clear()4981         public void clear() {
4982             for (int i = 0; i < mScrap.size(); i++) {
4983                 ScrapData data = mScrap.valueAt(i);
4984                 data.mScrapHeap.clear();
4985             }
4986         }
4987 
setMaxRecycledViews(int viewType, int max)4988         public void setMaxRecycledViews(int viewType, int max) {
4989             ScrapData scrapData = getScrapDataForType(viewType);
4990             scrapData.mMaxScrap = max;
4991             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
4992             if (scrapHeap != null) {
4993                 while (scrapHeap.size() > max) {
4994                     scrapHeap.remove(scrapHeap.size() - 1);
4995                 }
4996             }
4997         }
4998 
4999         /**
5000          * Returns the current number of Views held by the RecycledViewPool of the given view type.
5001          */
getRecycledViewCount(int viewType)5002         public int getRecycledViewCount(int viewType) {
5003             return getScrapDataForType(viewType).mScrapHeap.size();
5004         }
5005 
getRecycledView(int viewType)5006         public ViewHolder getRecycledView(int viewType) {
5007             final ScrapData scrapData = mScrap.get(viewType);
5008             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
5009                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
5010                 return scrapHeap.remove(scrapHeap.size() - 1);
5011             }
5012             return null;
5013         }
5014 
size()5015         int size() {
5016             int count = 0;
5017             for (int i = 0; i < mScrap.size(); i++) {
5018                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
5019                 if (viewHolders != null) {
5020                     count += viewHolders.size();
5021                 }
5022             }
5023             return count;
5024         }
5025 
putRecycledView(ViewHolder scrap)5026         public void putRecycledView(ViewHolder scrap) {
5027             final int viewType = scrap.getItemViewType();
5028             final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
5029             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
5030                 return;
5031             }
5032             if (DEBUG && scrapHeap.contains(scrap)) {
5033                 throw new IllegalArgumentException("this scrap item already exists");
5034             }
5035             scrap.resetInternal();
5036             scrapHeap.add(scrap);
5037         }
5038 
runningAverage(long oldAverage, long newValue)5039         long runningAverage(long oldAverage, long newValue) {
5040             if (oldAverage == 0) {
5041                 return newValue;
5042             }
5043             return (oldAverage / 4 * 3) + (newValue / 4);
5044         }
5045 
factorInCreateTime(int viewType, long createTimeNs)5046         void factorInCreateTime(int viewType, long createTimeNs) {
5047             ScrapData scrapData = getScrapDataForType(viewType);
5048             scrapData.mCreateRunningAverageNs = runningAverage(
5049                     scrapData.mCreateRunningAverageNs, createTimeNs);
5050         }
5051 
factorInBindTime(int viewType, long bindTimeNs)5052         void factorInBindTime(int viewType, long bindTimeNs) {
5053             ScrapData scrapData = getScrapDataForType(viewType);
5054             scrapData.mBindRunningAverageNs = runningAverage(
5055                     scrapData.mBindRunningAverageNs, bindTimeNs);
5056         }
5057 
willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs)5058         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5059             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
5060             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5061         }
5062 
willBindInTime(int viewType, long approxCurrentNs, long deadlineNs)5063         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
5064             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
5065             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
5066         }
5067 
attach(Adapter adapter)5068         void attach(Adapter adapter) {
5069             mAttachCount++;
5070         }
5071 
detach()5072         void detach() {
5073             mAttachCount--;
5074         }
5075 
5076 
5077         /**
5078          * Detaches the old adapter and attaches the new one.
5079          * <p>
5080          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
5081          * adapter uses a different ViewHolder than the oldAdapter.
5082          *
5083          * @param oldAdapter The previous adapter instance. Will be detached.
5084          * @param newAdapter The new adapter instance. Will be attached.
5085          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
5086          *                               ViewHolder and view types.
5087          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)5088         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
5089                 boolean compatibleWithPrevious) {
5090             if (oldAdapter != null) {
5091                 detach();
5092             }
5093             if (!compatibleWithPrevious && mAttachCount == 0) {
5094                 clear();
5095             }
5096             if (newAdapter != null) {
5097                 attach(newAdapter);
5098             }
5099         }
5100 
getScrapDataForType(int viewType)5101         private ScrapData getScrapDataForType(int viewType) {
5102             ScrapData scrapData = mScrap.get(viewType);
5103             if (scrapData == null) {
5104                 scrapData = new ScrapData();
5105                 mScrap.put(viewType, scrapData);
5106             }
5107             return scrapData;
5108         }
5109     }
5110 
5111     /**
5112      * Utility method for finding an internal RecyclerView, if present
5113      */
5114     @Nullable
findNestedRecyclerView(@onNull View view)5115     static RecyclerView findNestedRecyclerView(@NonNull View view) {
5116         if (!(view instanceof ViewGroup)) {
5117             return null;
5118         }
5119         if (view instanceof RecyclerView) {
5120             return (RecyclerView) view;
5121         }
5122         final ViewGroup parent = (ViewGroup) view;
5123         final int count = parent.getChildCount();
5124         for (int i = 0; i < count; i++) {
5125             final View child = parent.getChildAt(i);
5126             final RecyclerView descendant = findNestedRecyclerView(child);
5127             if (descendant != null) {
5128                 return descendant;
5129             }
5130         }
5131         return null;
5132     }
5133 
5134     /**
5135      * Utility method for clearing holder's internal RecyclerView, if present
5136      */
clearNestedRecyclerViewIfNotNested(@onNull ViewHolder holder)5137     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
5138         if (holder.mNestedRecyclerView != null) {
5139             View item = holder.mNestedRecyclerView.get();
5140             while (item != null) {
5141                 if (item == holder.itemView) {
5142                     return; // match found, don't need to clear
5143                 }
5144 
5145                 ViewParent parent = item.getParent();
5146                 if (parent instanceof View) {
5147                     item = (View) parent;
5148                 } else {
5149                     item = null;
5150                 }
5151             }
5152             holder.mNestedRecyclerView = null; // not nested
5153         }
5154     }
5155 
5156     /**
5157      * Time base for deadline-aware work scheduling. Overridable for testing.
5158      *
5159      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
5160      * isn't relevant.
5161      */
getNanoTime()5162     long getNanoTime() {
5163         if (ALLOW_THREAD_GAP_WORK) {
5164             return System.nanoTime();
5165         } else {
5166             return 0;
5167         }
5168     }
5169 
5170     /**
5171      * A Recycler is responsible for managing scrapped or detached item views for reuse.
5172      *
5173      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
5174      * that has been marked for removal or reuse.</p>
5175      *
5176      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
5177      * an adapter's data set representing the data at a given position or item ID.
5178      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
5179      * If not, the view can be quickly reused by the LayoutManager with no further work.
5180      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
5181      * may be repositioned by a LayoutManager without remeasurement.</p>
5182      */
5183     public final class Recycler {
5184         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
5185         ArrayList<ViewHolder> mChangedScrap = null;
5186 
5187         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
5188 
5189         private final List<ViewHolder>
5190                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
5191 
5192         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
5193         int mViewCacheMax = DEFAULT_CACHE_SIZE;
5194 
5195         RecycledViewPool mRecyclerPool;
5196 
5197         private ViewCacheExtension mViewCacheExtension;
5198 
5199         static final int DEFAULT_CACHE_SIZE = 2;
5200 
5201         /**
5202          * Clear scrap views out of this recycler. Detached views contained within a
5203          * recycled view pool will remain.
5204          */
clear()5205         public void clear() {
5206             mAttachedScrap.clear();
5207             recycleAndClearCachedViews();
5208         }
5209 
5210         /**
5211          * Set the maximum number of detached, valid views we should retain for later use.
5212          *
5213          * @param viewCount Number of views to keep before sending views to the shared pool
5214          */
setViewCacheSize(int viewCount)5215         public void setViewCacheSize(int viewCount) {
5216             mRequestedCacheMax = viewCount;
5217             updateViewCacheSize();
5218         }
5219 
updateViewCacheSize()5220         void updateViewCacheSize() {
5221             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
5222             mViewCacheMax = mRequestedCacheMax + extraCache;
5223 
5224             // first, try the views that can be recycled
5225             for (int i = mCachedViews.size() - 1;
5226                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
5227                 recycleCachedViewAt(i);
5228             }
5229         }
5230 
5231         /**
5232          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
5233          *
5234          * @return List of ViewHolders in the scrap list.
5235          */
getScrapList()5236         public List<ViewHolder> getScrapList() {
5237             return mUnmodifiableAttachedScrap;
5238         }
5239 
5240         /**
5241          * Helper method for getViewForPosition.
5242          * <p>
5243          * Checks whether a given view holder can be used for the provided position.
5244          *
5245          * @param holder ViewHolder
5246          * @return true if ViewHolder matches the provided position, false otherwise
5247          */
validateViewHolderForOffsetPosition(ViewHolder holder)5248         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
5249             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
5250             // if it is not removed, verify the type and id.
5251             if (holder.isRemoved()) {
5252                 if (DEBUG && !mState.isPreLayout()) {
5253                     throw new IllegalStateException("should not receive a removed view unless it"
5254                             + " is pre layout");
5255                 }
5256                 return mState.isPreLayout();
5257             }
5258             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
5259                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
5260                         + "adapter position" + holder);
5261             }
5262             if (!mState.isPreLayout()) {
5263                 // don't check type if it is pre-layout.
5264                 final int type = mAdapter.getItemViewType(holder.mPosition);
5265                 if (type != holder.getItemViewType()) {
5266                     return false;
5267                 }
5268             }
5269             if (mAdapter.hasStableIds()) {
5270                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
5271             }
5272             return true;
5273         }
5274 
5275         /**
5276          * Attempts to bind view, and account for relevant timing information. If
5277          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
5278          *
5279          * @param holder Holder to be bound.
5280          * @param offsetPosition Position of item to be bound.
5281          * @param position Pre-layout position of item to be bound.
5282          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5283          *                   complete. If FOREVER_NS is passed, this method will not fail to
5284          *                   bind the holder.
5285          * @return
5286          */
tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition, int position, long deadlineNs)5287         private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
5288                 int position, long deadlineNs) {
5289             holder.mOwnerRecyclerView = RecyclerView.this;
5290             final int viewType = holder.getItemViewType();
5291             long startBindNs = getNanoTime();
5292             if (deadlineNs != FOREVER_NS
5293                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
5294                 // abort - we have a deadline we can't meet
5295                 return false;
5296             }
5297             mAdapter.bindViewHolder(holder, offsetPosition);
5298             long endBindNs = getNanoTime();
5299             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
5300             attachAccessibilityDelegate(holder.itemView);
5301             if (mState.isPreLayout()) {
5302                 holder.mPreLayoutPosition = position;
5303             }
5304             return true;
5305         }
5306 
5307         /**
5308          * Binds the given View to the position. The View can be a View previously retrieved via
5309          * {@link #getViewForPosition(int)} or created by
5310          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
5311          * <p>
5312          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
5313          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
5314          * wants to handle its own recycling logic.
5315          * <p>
5316          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
5317          * you don't need to call this method unless you want to bind this View to another position.
5318          *
5319          * @param view The view to update.
5320          * @param position The position of the item to bind to this View.
5321          */
bindViewToPosition(View view, int position)5322         public void bindViewToPosition(View view, int position) {
5323             ViewHolder holder = getChildViewHolderInt(view);
5324             if (holder == null) {
5325                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
5326                         + " pass arbitrary views to this method, they should be created by the "
5327                         + "Adapter");
5328             }
5329             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5330             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5331                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5332                         + "position " + position + "(offset:" + offsetPosition + ")."
5333                         + "state:" + mState.getItemCount());
5334             }
5335             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
5336 
5337             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5338             final LayoutParams rvLayoutParams;
5339             if (lp == null) {
5340                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5341                 holder.itemView.setLayoutParams(rvLayoutParams);
5342             } else if (!checkLayoutParams(lp)) {
5343                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5344                 holder.itemView.setLayoutParams(rvLayoutParams);
5345             } else {
5346                 rvLayoutParams = (LayoutParams) lp;
5347             }
5348 
5349             rvLayoutParams.mInsetsDirty = true;
5350             rvLayoutParams.mViewHolder = holder;
5351             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
5352         }
5353 
5354         /**
5355          * RecyclerView provides artificial position range (item count) in pre-layout state and
5356          * automatically maps these positions to {@link Adapter} positions when
5357          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
5358          * <p>
5359          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
5360          * LayoutManager may need to call some custom component with item positions in which
5361          * case you need the actual adapter position instead of the pre layout position. You
5362          * can use this method to convert a pre-layout position to adapter (post layout) position.
5363          * <p>
5364          * Note that if the provided position belongs to a deleted ViewHolder, this method will
5365          * return -1.
5366          * <p>
5367          * Calling this method in post-layout state returns the same value back.
5368          *
5369          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
5370          *                 less than {@link State#getItemCount()}.
5371          */
convertPreLayoutPositionToPostLayout(int position)5372         public int convertPreLayoutPositionToPostLayout(int position) {
5373             if (position < 0 || position >= mState.getItemCount()) {
5374                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
5375                         + "item count is " + mState.getItemCount());
5376             }
5377             if (!mState.isPreLayout()) {
5378                 return position;
5379             }
5380             return mAdapterHelper.findPositionOffset(position);
5381         }
5382 
5383         /**
5384          * Obtain a view initialized for the given position.
5385          *
5386          * This method should be used by {@link LayoutManager} implementations to obtain
5387          * views to represent data from an {@link Adapter}.
5388          * <p>
5389          * The Recycler may reuse a scrap or detached view from a shared pool if one is
5390          * available for the correct view type. If the adapter has not indicated that the
5391          * data at the given position has changed, the Recycler will attempt to hand back
5392          * a scrap view that was previously initialized for that data without rebinding.
5393          *
5394          * @param position Position to obtain a view for
5395          * @return A view representing the data at <code>position</code> from <code>adapter</code>
5396          */
getViewForPosition(int position)5397         public View getViewForPosition(int position) {
5398             return getViewForPosition(position, false);
5399         }
5400 
getViewForPosition(int position, boolean dryRun)5401         View getViewForPosition(int position, boolean dryRun) {
5402             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
5403         }
5404 
5405         /**
5406          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
5407          * cache, the RecycledViewPool, or creating it directly.
5408          * <p>
5409          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
5410          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
5411          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
5412          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
5413          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
5414          *
5415          * @param position Position of ViewHolder to be returned.
5416          * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
5417          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
5418          *                   complete. If FOREVER_NS is passed, this method will not fail to
5419          *                   create/bind the holder if needed.
5420          *
5421          * @return ViewHolder for requested position
5422          */
5423         @Nullable
tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs)5424         ViewHolder tryGetViewHolderForPositionByDeadline(int position,
5425                 boolean dryRun, long deadlineNs) {
5426             if (position < 0 || position >= mState.getItemCount()) {
5427                 throw new IndexOutOfBoundsException("Invalid item position " + position
5428                         + "(" + position + "). Item count:" + mState.getItemCount());
5429             }
5430             boolean fromScrapOrHiddenOrCache = false;
5431             ViewHolder holder = null;
5432             // 0) If there is a changed scrap, try to find from there
5433             if (mState.isPreLayout()) {
5434                 holder = getChangedScrapViewForPosition(position);
5435                 fromScrapOrHiddenOrCache = holder != null;
5436             }
5437             // 1) Find by position from scrap/hidden list/cache
5438             if (holder == null) {
5439                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
5440                 if (holder != null) {
5441                     if (!validateViewHolderForOffsetPosition(holder)) {
5442                         // recycle holder (and unscrap if relevant) since it can't be used
5443                         if (!dryRun) {
5444                             // we would like to recycle this but need to make sure it is not used by
5445                             // animation logic etc.
5446                             holder.addFlags(ViewHolder.FLAG_INVALID);
5447                             if (holder.isScrap()) {
5448                                 removeDetachedView(holder.itemView, false);
5449                                 holder.unScrap();
5450                             } else if (holder.wasReturnedFromScrap()) {
5451                                 holder.clearReturnedFromScrapFlag();
5452                             }
5453                             recycleViewHolderInternal(holder);
5454                         }
5455                         holder = null;
5456                     } else {
5457                         fromScrapOrHiddenOrCache = true;
5458                     }
5459                 }
5460             }
5461             if (holder == null) {
5462                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5463                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
5464                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
5465                             + "position " + position + "(offset:" + offsetPosition + ")."
5466                             + "state:" + mState.getItemCount());
5467                 }
5468 
5469                 final int type = mAdapter.getItemViewType(offsetPosition);
5470                 // 2) Find from scrap/cache via stable ids, if exists
5471                 if (mAdapter.hasStableIds()) {
5472                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
5473                             type, dryRun);
5474                     if (holder != null) {
5475                         // update position
5476                         holder.mPosition = offsetPosition;
5477                         fromScrapOrHiddenOrCache = true;
5478                     }
5479                 }
5480                 if (holder == null && mViewCacheExtension != null) {
5481                     // We are NOT sending the offsetPosition because LayoutManager does not
5482                     // know it.
5483                     final View view = mViewCacheExtension
5484                             .getViewForPositionAndType(this, position, type);
5485                     if (view != null) {
5486                         holder = getChildViewHolder(view);
5487                         if (holder == null) {
5488                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5489                                     + " a view which does not have a ViewHolder");
5490                         } else if (holder.shouldIgnore()) {
5491                             throw new IllegalArgumentException("getViewForPositionAndType returned"
5492                                     + " a view that is ignored. You must call stopIgnoring before"
5493                                     + " returning this view.");
5494                         }
5495                     }
5496                 }
5497                 if (holder == null) { // fallback to pool
5498                     if (DEBUG) {
5499                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
5500                                 + position + ") fetching from shared pool");
5501                     }
5502                     holder = getRecycledViewPool().getRecycledView(type);
5503                     if (holder != null) {
5504                         holder.resetInternal();
5505                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
5506                             invalidateDisplayListInt(holder);
5507                         }
5508                     }
5509                 }
5510                 if (holder == null) {
5511                     long start = getNanoTime();
5512                     if (deadlineNs != FOREVER_NS
5513                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
5514                         // abort - we have a deadline we can't meet
5515                         return null;
5516                     }
5517                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
5518                     if (ALLOW_THREAD_GAP_WORK) {
5519                         // only bother finding nested RV if prefetching
5520                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
5521                         if (innerView != null) {
5522                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
5523                         }
5524                     }
5525 
5526                     long end = getNanoTime();
5527                     mRecyclerPool.factorInCreateTime(type, end - start);
5528                     if (DEBUG) {
5529                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
5530                     }
5531                 }
5532             }
5533 
5534             // This is very ugly but the only place we can grab this information
5535             // before the View is rebound and returned to the LayoutManager for post layout ops.
5536             // We don't need this in pre-layout since the VH is not updated by the LM.
5537             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
5538                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
5539                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5540                 if (mState.mRunSimpleAnimations) {
5541                     int changeFlags = ItemAnimator
5542                             .buildAdapterChangeFlagsForAnimations(holder);
5543                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
5544                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
5545                             holder, changeFlags, holder.getUnmodifiedPayloads());
5546                     recordAnimationInfoIfBouncedHiddenView(holder, info);
5547                 }
5548             }
5549 
5550             boolean bound = false;
5551             if (mState.isPreLayout() && holder.isBound()) {
5552                 // do not update unless we absolutely have to.
5553                 holder.mPreLayoutPosition = position;
5554             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
5555                 if (DEBUG && holder.isRemoved()) {
5556                     throw new IllegalStateException("Removed holder should be bound and it should"
5557                             + " come here only in pre-layout. Holder: " + holder);
5558                 }
5559                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5560                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
5561             }
5562 
5563             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
5564             final LayoutParams rvLayoutParams;
5565             if (lp == null) {
5566                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
5567                 holder.itemView.setLayoutParams(rvLayoutParams);
5568             } else if (!checkLayoutParams(lp)) {
5569                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
5570                 holder.itemView.setLayoutParams(rvLayoutParams);
5571             } else {
5572                 rvLayoutParams = (LayoutParams) lp;
5573             }
5574             rvLayoutParams.mViewHolder = holder;
5575             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
5576             return holder;
5577         }
5578 
attachAccessibilityDelegate(View itemView)5579         private void attachAccessibilityDelegate(View itemView) {
5580             if (isAccessibilityEnabled()) {
5581                 if (itemView.getImportantForAccessibility()
5582                         == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
5583                     itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
5584                 }
5585 
5586                 if (itemView.getAccessibilityDelegate() == null) {
5587                     itemView.setAccessibilityDelegate(mAccessibilityDelegate.getItemDelegate());
5588                 }
5589             }
5590         }
5591 
invalidateDisplayListInt(ViewHolder holder)5592         private void invalidateDisplayListInt(ViewHolder holder) {
5593             if (holder.itemView instanceof ViewGroup) {
5594                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
5595             }
5596         }
5597 
invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis)5598         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
5599             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
5600                 final View view = viewGroup.getChildAt(i);
5601                 if (view instanceof ViewGroup) {
5602                     invalidateDisplayListInt((ViewGroup) view, true);
5603                 }
5604             }
5605             if (!invalidateThis) {
5606                 return;
5607             }
5608             // we need to force it to become invisible
5609             if (viewGroup.getVisibility() == View.INVISIBLE) {
5610                 viewGroup.setVisibility(View.VISIBLE);
5611                 viewGroup.setVisibility(View.INVISIBLE);
5612             } else {
5613                 final int visibility = viewGroup.getVisibility();
5614                 viewGroup.setVisibility(View.INVISIBLE);
5615                 viewGroup.setVisibility(visibility);
5616             }
5617         }
5618 
5619         /**
5620          * Recycle a detached view. The specified view will be added to a pool of views
5621          * for later rebinding and reuse.
5622          *
5623          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
5624          * View is scrapped, it will be removed from scrap list.</p>
5625          *
5626          * @param view Removed view for recycling
5627          * @see LayoutManager#removeAndRecycleView(View, Recycler)
5628          */
recycleView(View view)5629         public void recycleView(View view) {
5630             // This public recycle method tries to make view recycle-able since layout manager
5631             // intended to recycle this view (e.g. even if it is in scrap or change cache)
5632             ViewHolder holder = getChildViewHolderInt(view);
5633             if (holder.isTmpDetached()) {
5634                 removeDetachedView(view, false);
5635             }
5636             if (holder.isScrap()) {
5637                 holder.unScrap();
5638             } else if (holder.wasReturnedFromScrap()) {
5639                 holder.clearReturnedFromScrapFlag();
5640             }
5641             recycleViewHolderInternal(holder);
5642         }
5643 
5644         /**
5645          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
5646          * catch potential bugs.
5647          * @param view
5648          */
recycleViewInternal(View view)5649         void recycleViewInternal(View view) {
5650             recycleViewHolderInternal(getChildViewHolderInt(view));
5651         }
5652 
recycleAndClearCachedViews()5653         void recycleAndClearCachedViews() {
5654             final int count = mCachedViews.size();
5655             for (int i = count - 1; i >= 0; i--) {
5656                 recycleCachedViewAt(i);
5657             }
5658             mCachedViews.clear();
5659             if (ALLOW_THREAD_GAP_WORK) {
5660                 mPrefetchRegistry.clearPrefetchPositions();
5661             }
5662         }
5663 
5664         /**
5665          * Recycles a cached view and removes the view from the list. Views are added to cache
5666          * if and only if they are recyclable, so this method does not check it again.
5667          * <p>
5668          * A small exception to this rule is when the view does not have an animator reference
5669          * but transient state is true (due to animations created outside ItemAnimator). In that
5670          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
5671          * still recyclable since Adapter wants to do so.
5672          *
5673          * @param cachedViewIndex The index of the view in cached views list
5674          */
recycleCachedViewAt(int cachedViewIndex)5675         void recycleCachedViewAt(int cachedViewIndex) {
5676             if (DEBUG) {
5677                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
5678             }
5679             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
5680             if (DEBUG) {
5681                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
5682             }
5683             addViewHolderToRecycledViewPool(viewHolder, true);
5684             mCachedViews.remove(cachedViewIndex);
5685         }
5686 
5687         /**
5688          * internal implementation checks if view is scrapped or attached and throws an exception
5689          * if so.
5690          * Public version un-scraps before calling recycle.
5691          */
recycleViewHolderInternal(ViewHolder holder)5692         void recycleViewHolderInternal(ViewHolder holder) {
5693             if (holder.isScrap() || holder.itemView.getParent() != null) {
5694                 throw new IllegalArgumentException(
5695                         "Scrapped or attached views may not be recycled. isScrap:"
5696                                 + holder.isScrap() + " isAttached:"
5697                                 + (holder.itemView.getParent() != null));
5698             }
5699 
5700             if (holder.isTmpDetached()) {
5701                 throw new IllegalArgumentException("Tmp detached view should be removed "
5702                         + "from RecyclerView before it can be recycled: " + holder);
5703             }
5704 
5705             if (holder.shouldIgnore()) {
5706                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
5707                         + " should first call stopIgnoringView(view) before calling recycle.");
5708             }
5709             //noinspection unchecked
5710             final boolean transientStatePreventsRecycling = holder
5711                     .doesTransientStatePreventRecycling();
5712             final boolean forceRecycle = mAdapter != null
5713                     && transientStatePreventsRecycling
5714                     && mAdapter.onFailedToRecycleView(holder);
5715             boolean cached = false;
5716             boolean recycled = false;
5717             if (DEBUG && mCachedViews.contains(holder)) {
5718                 throw new IllegalArgumentException("cached view received recycle internal? "
5719                         + holder);
5720             }
5721             if (forceRecycle || holder.isRecyclable()) {
5722                 if (mViewCacheMax > 0
5723                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
5724                                 | ViewHolder.FLAG_REMOVED
5725                                 | ViewHolder.FLAG_UPDATE
5726                                 | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
5727                     // Retire oldest cached view
5728                     int cachedViewSize = mCachedViews.size();
5729                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
5730                         recycleCachedViewAt(0);
5731                         cachedViewSize--;
5732                     }
5733 
5734                     int targetCacheIndex = cachedViewSize;
5735                     if (ALLOW_THREAD_GAP_WORK
5736                             && cachedViewSize > 0
5737                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
5738                         // when adding the view, skip past most recently prefetched views
5739                         int cacheIndex = cachedViewSize - 1;
5740                         while (cacheIndex >= 0) {
5741                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
5742                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
5743                                 break;
5744                             }
5745                             cacheIndex--;
5746                         }
5747                         targetCacheIndex = cacheIndex + 1;
5748                     }
5749                     mCachedViews.add(targetCacheIndex, holder);
5750                     cached = true;
5751                 }
5752                 if (!cached) {
5753                     addViewHolderToRecycledViewPool(holder, true);
5754                     recycled = true;
5755                 }
5756             } else {
5757                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
5758                 // runs. In this case, the item is eventually recycled by
5759                 // ItemAnimatorRestoreListener#onAnimationFinished.
5760 
5761                 // TODO: consider cancelling an animation when an item is removed scrollBy,
5762                 // to return it to the pool faster
5763                 if (DEBUG) {
5764                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
5765                             + "re-visit here. We are still removing it from animation lists");
5766                 }
5767             }
5768             // even if the holder is not removed, we still call this method so that it is removed
5769             // from view holder lists.
5770             mViewInfoStore.removeViewHolder(holder);
5771             if (!cached && !recycled && transientStatePreventsRecycling) {
5772                 holder.mOwnerRecyclerView = null;
5773             }
5774         }
5775 
5776         /**
5777          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
5778          *
5779          * Pass false to dispatchRecycled for views that have not been bound.
5780          *
5781          * @param holder Holder to be added to the pool.
5782          * @param dispatchRecycled True to dispatch View recycled callbacks.
5783          */
addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled)5784         void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
5785             clearNestedRecyclerViewIfNotNested(holder);
5786             holder.itemView.setAccessibilityDelegate(null);
5787             if (dispatchRecycled) {
5788                 dispatchViewRecycled(holder);
5789             }
5790             holder.mOwnerRecyclerView = null;
5791             getRecycledViewPool().putRecycledView(holder);
5792         }
5793 
5794         /**
5795          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
5796          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
5797          * internal bookkeeping.
5798          */
quickRecycleScrapView(View view)5799         void quickRecycleScrapView(View view) {
5800             final ViewHolder holder = getChildViewHolderInt(view);
5801             holder.mScrapContainer = null;
5802             holder.mInChangeScrap = false;
5803             holder.clearReturnedFromScrapFlag();
5804             recycleViewHolderInternal(holder);
5805         }
5806 
5807         /**
5808          * Mark an attached view as scrap.
5809          *
5810          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
5811          * for rebinding and reuse. Requests for a view for a given position may return a
5812          * reused or rebound scrap view instance.</p>
5813          *
5814          * @param view View to scrap
5815          */
scrapView(View view)5816         void scrapView(View view) {
5817             final ViewHolder holder = getChildViewHolderInt(view);
5818             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
5819                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
5820                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
5821                     throw new IllegalArgumentException("Called scrap view with an invalid view."
5822                             + " Invalid views cannot be reused from scrap, they should rebound from"
5823                             + " recycler pool.");
5824                 }
5825                 holder.setScrapContainer(this, false);
5826                 mAttachedScrap.add(holder);
5827             } else {
5828                 if (mChangedScrap == null) {
5829                     mChangedScrap = new ArrayList<ViewHolder>();
5830                 }
5831                 holder.setScrapContainer(this, true);
5832                 mChangedScrap.add(holder);
5833             }
5834         }
5835 
5836         /**
5837          * Remove a previously scrapped view from the pool of eligible scrap.
5838          *
5839          * <p>This view will no longer be eligible for reuse until re-scrapped or
5840          * until it is explicitly removed and recycled.</p>
5841          */
unscrapView(ViewHolder holder)5842         void unscrapView(ViewHolder holder) {
5843             if (holder.mInChangeScrap) {
5844                 mChangedScrap.remove(holder);
5845             } else {
5846                 mAttachedScrap.remove(holder);
5847             }
5848             holder.mScrapContainer = null;
5849             holder.mInChangeScrap = false;
5850             holder.clearReturnedFromScrapFlag();
5851         }
5852 
getScrapCount()5853         int getScrapCount() {
5854             return mAttachedScrap.size();
5855         }
5856 
getScrapViewAt(int index)5857         View getScrapViewAt(int index) {
5858             return mAttachedScrap.get(index).itemView;
5859         }
5860 
clearScrap()5861         void clearScrap() {
5862             mAttachedScrap.clear();
5863             if (mChangedScrap != null) {
5864                 mChangedScrap.clear();
5865             }
5866         }
5867 
getChangedScrapViewForPosition(int position)5868         ViewHolder getChangedScrapViewForPosition(int position) {
5869             // If pre-layout, check the changed scrap for an exact match.
5870             final int changedScrapSize;
5871             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
5872                 return null;
5873             }
5874             // find by position
5875             for (int i = 0; i < changedScrapSize; i++) {
5876                 final ViewHolder holder = mChangedScrap.get(i);
5877                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
5878                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5879                     return holder;
5880                 }
5881             }
5882             // find by id
5883             if (mAdapter.hasStableIds()) {
5884                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
5885                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
5886                     final long id = mAdapter.getItemId(offsetPosition);
5887                     for (int i = 0; i < changedScrapSize; i++) {
5888                         final ViewHolder holder = mChangedScrap.get(i);
5889                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
5890                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5891                             return holder;
5892                         }
5893                     }
5894                 }
5895             }
5896             return null;
5897         }
5898 
5899         /**
5900          * Returns a view for the position either from attach scrap, hidden children, or cache.
5901          *
5902          * @param position Item position
5903          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
5904          * @return a ViewHolder that can be re-used for this position.
5905          */
getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun)5906         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
5907             final int scrapCount = mAttachedScrap.size();
5908 
5909             // Try first for an exact, non-invalid match from scrap.
5910             for (int i = 0; i < scrapCount; i++) {
5911                 final ViewHolder holder = mAttachedScrap.get(i);
5912                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
5913                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
5914                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5915                     return holder;
5916                 }
5917             }
5918 
5919             if (!dryRun) {
5920                 View view = mChildHelper.findHiddenNonRemovedView(position);
5921                 if (view != null) {
5922                     // This View is good to be used. We just need to unhide, detach and move to the
5923                     // scrap list.
5924                     final ViewHolder vh = getChildViewHolderInt(view);
5925                     mChildHelper.unhide(view);
5926                     int layoutIndex = mChildHelper.indexOfChild(view);
5927                     if (layoutIndex == RecyclerView.NO_POSITION) {
5928                         throw new IllegalStateException("layout index should not be -1 after "
5929                                 + "unhiding a view:" + vh);
5930                     }
5931                     mChildHelper.detachViewFromParent(layoutIndex);
5932                     scrapView(view);
5933                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
5934                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
5935                     return vh;
5936                 }
5937             }
5938 
5939             // Search in our first-level recycled view cache.
5940             final int cacheSize = mCachedViews.size();
5941             for (int i = 0; i < cacheSize; i++) {
5942                 final ViewHolder holder = mCachedViews.get(i);
5943                 // invalid view holders may be in cache if adapter has stable ids as they can be
5944                 // retrieved via getScrapOrCachedViewForId
5945                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
5946                     if (!dryRun) {
5947                         mCachedViews.remove(i);
5948                     }
5949                     if (DEBUG) {
5950                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
5951                                 + ") found match in cache: " + holder);
5952                     }
5953                     return holder;
5954                 }
5955             }
5956             return null;
5957         }
5958 
getScrapOrCachedViewForId(long id, int type, boolean dryRun)5959         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
5960             // Look in our attached views first
5961             final int count = mAttachedScrap.size();
5962             for (int i = count - 1; i >= 0; i--) {
5963                 final ViewHolder holder = mAttachedScrap.get(i);
5964                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
5965                     if (type == holder.getItemViewType()) {
5966                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
5967                         if (holder.isRemoved()) {
5968                             // this might be valid in two cases:
5969                             // > item is removed but we are in pre-layout pass
5970                             // >> do nothing. return as is. make sure we don't rebind
5971                             // > item is removed then added to another position and we are in
5972                             // post layout.
5973                             // >> remove removed and invalid flags, add update flag to rebind
5974                             // because item was invisible to us and we don't know what happened in
5975                             // between.
5976                             if (!mState.isPreLayout()) {
5977                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
5978                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
5979                             }
5980                         }
5981                         return holder;
5982                     } else if (!dryRun) {
5983                         // if we are running animations, it is actually better to keep it in scrap
5984                         // but this would force layout manager to lay it out which would be bad.
5985                         // Recycle this scrap. Type mismatch.
5986                         mAttachedScrap.remove(i);
5987                         removeDetachedView(holder.itemView, false);
5988                         quickRecycleScrapView(holder.itemView);
5989                     }
5990                 }
5991             }
5992 
5993             // Search the first-level cache
5994             final int cacheSize = mCachedViews.size();
5995             for (int i = cacheSize - 1; i >= 0; i--) {
5996                 final ViewHolder holder = mCachedViews.get(i);
5997                 if (holder.getItemId() == id) {
5998                     if (type == holder.getItemViewType()) {
5999                         if (!dryRun) {
6000                             mCachedViews.remove(i);
6001                         }
6002                         return holder;
6003                     } else if (!dryRun) {
6004                         recycleCachedViewAt(i);
6005                         return null;
6006                     }
6007                 }
6008             }
6009             return null;
6010         }
6011 
dispatchViewRecycled(ViewHolder holder)6012         void dispatchViewRecycled(ViewHolder holder) {
6013             if (mRecyclerListener != null) {
6014                 mRecyclerListener.onViewRecycled(holder);
6015             }
6016             if (mAdapter != null) {
6017                 mAdapter.onViewRecycled(holder);
6018             }
6019             if (mState != null) {
6020                 mViewInfoStore.removeViewHolder(holder);
6021             }
6022             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
6023         }
6024 
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter, boolean compatibleWithPrevious)6025         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
6026                 boolean compatibleWithPrevious) {
6027             clear();
6028             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
6029         }
6030 
offsetPositionRecordsForMove(int from, int to)6031         void offsetPositionRecordsForMove(int from, int to) {
6032             final int start, end, inBetweenOffset;
6033             if (from < to) {
6034                 start = from;
6035                 end = to;
6036                 inBetweenOffset = -1;
6037             } else {
6038                 start = to;
6039                 end = from;
6040                 inBetweenOffset = 1;
6041             }
6042             final int cachedCount = mCachedViews.size();
6043             for (int i = 0; i < cachedCount; i++) {
6044                 final ViewHolder holder = mCachedViews.get(i);
6045                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
6046                     continue;
6047                 }
6048                 if (holder.mPosition == from) {
6049                     holder.offsetPosition(to - from, false);
6050                 } else {
6051                     holder.offsetPosition(inBetweenOffset, false);
6052                 }
6053                 if (DEBUG) {
6054                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
6055                             + holder);
6056                 }
6057             }
6058         }
6059 
offsetPositionRecordsForInsert(int insertedAt, int count)6060         void offsetPositionRecordsForInsert(int insertedAt, int count) {
6061             final int cachedCount = mCachedViews.size();
6062             for (int i = 0; i < cachedCount; i++) {
6063                 final ViewHolder holder = mCachedViews.get(i);
6064                 if (holder != null && holder.mPosition >= insertedAt) {
6065                     if (DEBUG) {
6066                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
6067                                 + holder + " now at position " + (holder.mPosition + count));
6068                     }
6069                     holder.offsetPosition(count, true);
6070                 }
6071             }
6072         }
6073 
6074         /**
6075          * @param removedFrom Remove start index
6076          * @param count Remove count
6077          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
6078          *                         false, they'll be applied before the second layout pass
6079          */
offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout)6080         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
6081             final int removedEnd = removedFrom + count;
6082             final int cachedCount = mCachedViews.size();
6083             for (int i = cachedCount - 1; i >= 0; i--) {
6084                 final ViewHolder holder = mCachedViews.get(i);
6085                 if (holder != null) {
6086                     if (holder.mPosition >= removedEnd) {
6087                         if (DEBUG) {
6088                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
6089                                     + " holder " + holder + " now at position "
6090                                     + (holder.mPosition - count));
6091                         }
6092                         holder.offsetPosition(-count, applyToPreLayout);
6093                     } else if (holder.mPosition >= removedFrom) {
6094                         // Item for this view was removed. Dump it from the cache.
6095                         holder.addFlags(ViewHolder.FLAG_REMOVED);
6096                         recycleCachedViewAt(i);
6097                     }
6098                 }
6099             }
6100         }
6101 
setViewCacheExtension(ViewCacheExtension extension)6102         void setViewCacheExtension(ViewCacheExtension extension) {
6103             mViewCacheExtension = extension;
6104         }
6105 
setRecycledViewPool(RecycledViewPool pool)6106         void setRecycledViewPool(RecycledViewPool pool) {
6107             if (mRecyclerPool != null) {
6108                 mRecyclerPool.detach();
6109             }
6110             mRecyclerPool = pool;
6111             if (pool != null) {
6112                 mRecyclerPool.attach(getAdapter());
6113             }
6114         }
6115 
getRecycledViewPool()6116         RecycledViewPool getRecycledViewPool() {
6117             if (mRecyclerPool == null) {
6118                 mRecyclerPool = new RecycledViewPool();
6119             }
6120             return mRecyclerPool;
6121         }
6122 
viewRangeUpdate(int positionStart, int itemCount)6123         void viewRangeUpdate(int positionStart, int itemCount) {
6124             final int positionEnd = positionStart + itemCount;
6125             final int cachedCount = mCachedViews.size();
6126             for (int i = cachedCount - 1; i >= 0; i--) {
6127                 final ViewHolder holder = mCachedViews.get(i);
6128                 if (holder == null) {
6129                     continue;
6130                 }
6131 
6132                 final int pos = holder.getLayoutPosition();
6133                 if (pos >= positionStart && pos < positionEnd) {
6134                     holder.addFlags(ViewHolder.FLAG_UPDATE);
6135                     recycleCachedViewAt(i);
6136                     // cached views should not be flagged as changed because this will cause them
6137                     // to animate when they are returned from cache.
6138                 }
6139             }
6140         }
6141 
setAdapterPositionsAsUnknown()6142         void setAdapterPositionsAsUnknown() {
6143             final int cachedCount = mCachedViews.size();
6144             for (int i = 0; i < cachedCount; i++) {
6145                 final ViewHolder holder = mCachedViews.get(i);
6146                 if (holder != null) {
6147                     holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6148                 }
6149             }
6150         }
6151 
markKnownViewsInvalid()6152         void markKnownViewsInvalid() {
6153             if (mAdapter != null && mAdapter.hasStableIds()) {
6154                 final int cachedCount = mCachedViews.size();
6155                 for (int i = 0; i < cachedCount; i++) {
6156                     final ViewHolder holder = mCachedViews.get(i);
6157                     if (holder != null) {
6158                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
6159                         holder.addChangePayload(null);
6160                     }
6161                 }
6162             } else {
6163                 // we cannot re-use cached views in this case. Recycle them all
6164                 recycleAndClearCachedViews();
6165             }
6166         }
6167 
clearOldPositions()6168         void clearOldPositions() {
6169             final int cachedCount = mCachedViews.size();
6170             for (int i = 0; i < cachedCount; i++) {
6171                 final ViewHolder holder = mCachedViews.get(i);
6172                 holder.clearOldPosition();
6173             }
6174             final int scrapCount = mAttachedScrap.size();
6175             for (int i = 0; i < scrapCount; i++) {
6176                 mAttachedScrap.get(i).clearOldPosition();
6177             }
6178             if (mChangedScrap != null) {
6179                 final int changedScrapCount = mChangedScrap.size();
6180                 for (int i = 0; i < changedScrapCount; i++) {
6181                     mChangedScrap.get(i).clearOldPosition();
6182                 }
6183             }
6184         }
6185 
markItemDecorInsetsDirty()6186         void markItemDecorInsetsDirty() {
6187             final int cachedCount = mCachedViews.size();
6188             for (int i = 0; i < cachedCount; i++) {
6189                 final ViewHolder holder = mCachedViews.get(i);
6190                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
6191                 if (layoutParams != null) {
6192                     layoutParams.mInsetsDirty = true;
6193                 }
6194             }
6195         }
6196     }
6197 
6198     /**
6199      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
6200      * be controlled by the developer.
6201      * <p>
6202      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
6203      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
6204      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
6205      * {@link RecycledViewPool}.
6206      * <p>
6207      * Note that, Recycler never sends Views to this method to be cached. It is developers
6208      * responsibility to decide whether they want to keep their Views in this custom cache or let
6209      * the default recycling policy handle it.
6210      */
6211     public abstract static class ViewCacheExtension {
6212 
6213         /**
6214          * Returns a View that can be binded to the given Adapter position.
6215          * <p>
6216          * This method should <b>not</b> create a new View. Instead, it is expected to return
6217          * an already created View that can be re-used for the given type and position.
6218          * If the View is marked as ignored, it should first call
6219          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
6220          * <p>
6221          * RecyclerView will re-bind the returned View to the position if necessary.
6222          *
6223          * @param recycler The Recycler that can be used to bind the View
6224          * @param position The adapter position
6225          * @param type     The type of the View, defined by adapter
6226          * @return A View that is bound to the given position or NULL if there is no View to re-use
6227          * @see LayoutManager#ignoreView(View)
6228          */
6229         public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
6230     }
6231 
6232     /**
6233      * Base class for an Adapter
6234      *
6235      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
6236      * within a {@link RecyclerView}.</p>
6237      *
6238      * @param <VH> A class that extends ViewHolder that will be used by the adapter.
6239      */
6240     public abstract static class Adapter<VH extends ViewHolder> {
6241         private final AdapterDataObservable mObservable = new AdapterDataObservable();
6242         private boolean mHasStableIds = false;
6243 
6244         /**
6245          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
6246          * an item.
6247          * <p>
6248          * This new ViewHolder should be constructed with a new View that can represent the items
6249          * of the given type. You can either create a new View manually or inflate it from an XML
6250          * layout file.
6251          * <p>
6252          * The new ViewHolder will be used to display items of the adapter using
6253          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
6254          * different items in the data set, it is a good idea to cache references to sub views of
6255          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
6256          *
6257          * @param parent The ViewGroup into which the new View will be added after it is bound to
6258          *               an adapter position.
6259          * @param viewType The view type of the new View.
6260          *
6261          * @return A new ViewHolder that holds a View of the given view type.
6262          * @see #getItemViewType(int)
6263          * @see #onBindViewHolder(ViewHolder, int)
6264          */
6265         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
6266 
6267         /**
6268          * Called by RecyclerView to display the data at the specified position. This method should
6269          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
6270          * position.
6271          * <p>
6272          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6273          * again if the position of the item changes in the data set unless the item itself is
6274          * invalidated or the new position cannot be determined. For this reason, you should only
6275          * use the <code>position</code> parameter while acquiring the related data item inside
6276          * this method and should not keep a copy of it. If you need the position of an item later
6277          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6278          * have the updated adapter position.
6279          *
6280          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
6281          * handle efficient partial bind.
6282          *
6283          * @param holder The ViewHolder which should be updated to represent the contents of the
6284          *        item at the given position in the data set.
6285          * @param position The position of the item within the adapter's data set.
6286          */
6287         public abstract void onBindViewHolder(VH holder, int position);
6288 
6289         /**
6290          * Called by RecyclerView to display the data at the specified position. This method
6291          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
6292          * the given position.
6293          * <p>
6294          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
6295          * again if the position of the item changes in the data set unless the item itself is
6296          * invalidated or the new position cannot be determined. For this reason, you should only
6297          * use the <code>position</code> parameter while acquiring the related data item inside
6298          * this method and should not keep a copy of it. If you need the position of an item later
6299          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
6300          * have the updated adapter position.
6301          * <p>
6302          * Partial bind vs full bind:
6303          * <p>
6304          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
6305          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
6306          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
6307          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
6308          * Adapter should not assume that the payload passed in notify methods will be received by
6309          * onBindViewHolder().  For example when the view is not attached to the screen, the
6310          * payload in notifyItemChange() will be simply dropped.
6311          *
6312          * @param holder The ViewHolder which should be updated to represent the contents of the
6313          *               item at the given position in the data set.
6314          * @param position The position of the item within the adapter's data set.
6315          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
6316          *                 update.
6317          */
onBindViewHolder(VH holder, int position, List<Object> payloads)6318         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
6319             onBindViewHolder(holder, position);
6320         }
6321 
6322         /**
6323          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
6324          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
6325          *
6326          * @see #onCreateViewHolder(ViewGroup, int)
6327          */
createViewHolder(ViewGroup parent, int viewType)6328         public final VH createViewHolder(ViewGroup parent, int viewType) {
6329             Trace.beginSection(TRACE_CREATE_VIEW_TAG);
6330             final VH holder = onCreateViewHolder(parent, viewType);
6331             holder.mItemViewType = viewType;
6332             Trace.endSection();
6333             return holder;
6334         }
6335 
6336         /**
6337          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
6338          * {@link ViewHolder} contents with the item at the given position and also sets up some
6339          * private fields to be used by RecyclerView.
6340          *
6341          * @see #onBindViewHolder(ViewHolder, int)
6342          */
bindViewHolder(VH holder, int position)6343         public final void bindViewHolder(VH holder, int position) {
6344             holder.mPosition = position;
6345             if (hasStableIds()) {
6346                 holder.mItemId = getItemId(position);
6347             }
6348             holder.setFlags(ViewHolder.FLAG_BOUND,
6349                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
6350                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
6351             Trace.beginSection(TRACE_BIND_VIEW_TAG);
6352             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
6353             holder.clearPayload();
6354             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
6355             if (layoutParams instanceof RecyclerView.LayoutParams) {
6356                 ((LayoutParams) layoutParams).mInsetsDirty = true;
6357             }
6358             Trace.endSection();
6359         }
6360 
6361         /**
6362          * Return the view type of the item at <code>position</code> for the purposes
6363          * of view recycling.
6364          *
6365          * <p>The default implementation of this method returns 0, making the assumption of
6366          * a single view type for the adapter. Unlike ListView adapters, types need not
6367          * be contiguous. Consider using id resources to uniquely identify item view types.
6368          *
6369          * @param position position to query
6370          * @return integer value identifying the type of the view needed to represent the item at
6371          *                 <code>position</code>. Type codes need not be contiguous.
6372          */
getItemViewType(int position)6373         public int getItemViewType(int position) {
6374             return 0;
6375         }
6376 
6377         /**
6378          * Indicates whether each item in the data set can be represented with a unique identifier
6379          * of type {@link java.lang.Long}.
6380          *
6381          * @param hasStableIds Whether items in data set have unique identifiers or not.
6382          * @see #hasStableIds()
6383          * @see #getItemId(int)
6384          */
setHasStableIds(boolean hasStableIds)6385         public void setHasStableIds(boolean hasStableIds) {
6386             if (hasObservers()) {
6387                 throw new IllegalStateException("Cannot change whether this adapter has "
6388                         + "stable IDs while the adapter has registered observers.");
6389             }
6390             mHasStableIds = hasStableIds;
6391         }
6392 
6393         /**
6394          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
6395          * would return false this method should return {@link #NO_ID}. The default implementation
6396          * of this method returns {@link #NO_ID}.
6397          *
6398          * @param position Adapter position to query
6399          * @return the stable ID of the item at position
6400          */
getItemId(int position)6401         public long getItemId(int position) {
6402             return NO_ID;
6403         }
6404 
6405         /**
6406          * Returns the total number of items in the data set held by the adapter.
6407          *
6408          * @return The total number of items in this adapter.
6409          */
6410         public abstract int getItemCount();
6411 
6412         /**
6413          * Returns true if this adapter publishes a unique <code>long</code> value that can
6414          * act as a key for the item at a given position in the data set. If that item is relocated
6415          * in the data set, the ID returned for that item should be the same.
6416          *
6417          * @return true if this adapter's items have stable IDs
6418          */
hasStableIds()6419         public final boolean hasStableIds() {
6420             return mHasStableIds;
6421         }
6422 
6423         /**
6424          * Called when a view created by this adapter has been recycled.
6425          *
6426          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
6427          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
6428          * fallen out of visibility or a set of cached views represented by views still
6429          * attached to the parent RecyclerView. If an item view has large or expensive data
6430          * bound to it such as large bitmaps, this may be a good place to release those
6431          * resources.</p>
6432          * <p>
6433          * RecyclerView calls this method right before clearing ViewHolder's internal data and
6434          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
6435          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
6436          * its adapter position.
6437          *
6438          * @param holder The ViewHolder for the view being recycled
6439          */
onViewRecycled(VH holder)6440         public void onViewRecycled(VH holder) {
6441         }
6442 
6443         /**
6444          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
6445          * due to its transient state. Upon receiving this callback, Adapter can clear the
6446          * animation(s) that effect the View's transient state and return <code>true</code> so that
6447          * the View can be recycled. Keep in mind that the View in question is already removed from
6448          * the RecyclerView.
6449          * <p>
6450          * In some cases, it is acceptable to recycle a View although it has transient state. Most
6451          * of the time, this is a case where the transient state will be cleared in
6452          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
6453          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
6454          * value of this method to decide whether the View should be recycled or not.
6455          * <p>
6456          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
6457          * should never receive this callback because RecyclerView keeps those Views as children
6458          * until their animations are complete. This callback is useful when children of the item
6459          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
6460          * <p>
6461          * You should <em>never</em> fix this issue by calling
6462          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
6463          * <code>holder.itemView.setHasTransientState(true);</code>. Each
6464          * <code>View.setHasTransientState(true)</code> call must be matched by a
6465          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
6466          * may become inconsistent. You should always prefer to end or cancel animations that are
6467          * triggering the transient state instead of handling it manually.
6468          *
6469          * @param holder The ViewHolder containing the View that could not be recycled due to its
6470          *               transient state.
6471          * @return True if the View should be recycled, false otherwise. Note that if this method
6472          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
6473          * the View and recycle it regardless. If this method returns <code>false</code>,
6474          * RecyclerView will check the View's transient state again before giving a final decision.
6475          * Default implementation returns false.
6476          */
onFailedToRecycleView(VH holder)6477         public boolean onFailedToRecycleView(VH holder) {
6478             return false;
6479         }
6480 
6481         /**
6482          * Called when a view created by this adapter has been attached to a window.
6483          *
6484          * <p>This can be used as a reasonable signal that the view is about to be seen
6485          * by the user. If the adapter previously freed any resources in
6486          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
6487          * those resources should be restored here.</p>
6488          *
6489          * @param holder Holder of the view being attached
6490          */
onViewAttachedToWindow(VH holder)6491         public void onViewAttachedToWindow(VH holder) {
6492         }
6493 
6494         /**
6495          * Called when a view created by this adapter has been detached from its window.
6496          *
6497          * <p>Becoming detached from the window is not necessarily a permanent condition;
6498          * the consumer of an Adapter's views may choose to cache views offscreen while they
6499          * are not visible, attaching and detaching them as appropriate.</p>
6500          *
6501          * @param holder Holder of the view being detached
6502          */
onViewDetachedFromWindow(VH holder)6503         public void onViewDetachedFromWindow(VH holder) {
6504         }
6505 
6506         /**
6507          * Returns true if one or more observers are attached to this adapter.
6508          *
6509          * @return true if this adapter has observers
6510          */
hasObservers()6511         public final boolean hasObservers() {
6512             return mObservable.hasObservers();
6513         }
6514 
6515         /**
6516          * Register a new observer to listen for data changes.
6517          *
6518          * <p>The adapter may publish a variety of events describing specific changes.
6519          * Not all adapters may support all change types and some may fall back to a generic
6520          * {@link com.android.internal.widget.RecyclerView.AdapterDataObserver#onChanged()
6521          * "something changed"} event if more specific data is not available.</p>
6522          *
6523          * <p>Components registering observers with an adapter are responsible for
6524          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6525          * unregistering} those observers when finished.</p>
6526          *
6527          * @param observer Observer to register
6528          *
6529          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
6530          */
registerAdapterDataObserver(AdapterDataObserver observer)6531         public void registerAdapterDataObserver(AdapterDataObserver observer) {
6532             mObservable.registerObserver(observer);
6533         }
6534 
6535         /**
6536          * Unregister an observer currently listening for data changes.
6537          *
6538          * <p>The unregistered observer will no longer receive events about changes
6539          * to the adapter.</p>
6540          *
6541          * @param observer Observer to unregister
6542          *
6543          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
6544          */
unregisterAdapterDataObserver(AdapterDataObserver observer)6545         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
6546             mObservable.unregisterObserver(observer);
6547         }
6548 
6549         /**
6550          * Called by RecyclerView when it starts observing this Adapter.
6551          * <p>
6552          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
6553          *
6554          * @param recyclerView The RecyclerView instance which started observing this adapter.
6555          * @see #onDetachedFromRecyclerView(RecyclerView)
6556          */
onAttachedToRecyclerView(RecyclerView recyclerView)6557         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
6558         }
6559 
6560         /**
6561          * Called by RecyclerView when it stops observing this Adapter.
6562          *
6563          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
6564          * @see #onAttachedToRecyclerView(RecyclerView)
6565          */
onDetachedFromRecyclerView(RecyclerView recyclerView)6566         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
6567         }
6568 
6569         /**
6570          * Notify any registered observers that the data set has changed.
6571          *
6572          * <p>There are two different classes of data change events, item changes and structural
6573          * changes. Item changes are when a single item has its data updated but no positional
6574          * changes have occurred. Structural changes are when items are inserted, removed or moved
6575          * within the data set.</p>
6576          *
6577          * <p>This event does not specify what about the data set has changed, forcing
6578          * any observers to assume that all existing items and structure may no longer be valid.
6579          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
6580          *
6581          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
6582          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
6583          * this method is used. This can help for the purposes of animation and visual
6584          * object persistence but individual item views will still need to be rebound
6585          * and relaid out.</p>
6586          *
6587          * <p>If you are writing an adapter it will always be more efficient to use the more
6588          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
6589          * as a last resort.</p>
6590          *
6591          * @see #notifyItemChanged(int)
6592          * @see #notifyItemInserted(int)
6593          * @see #notifyItemRemoved(int)
6594          * @see #notifyItemRangeChanged(int, int)
6595          * @see #notifyItemRangeInserted(int, int)
6596          * @see #notifyItemRangeRemoved(int, int)
6597          */
notifyDataSetChanged()6598         public final void notifyDataSetChanged() {
6599             mObservable.notifyChanged();
6600         }
6601 
6602         /**
6603          * Notify any registered observers that the item at <code>position</code> has changed.
6604          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
6605          *
6606          * <p>This is an item change event, not a structural change event. It indicates that any
6607          * reflection of the data at <code>position</code> is out of date and should be updated.
6608          * The item at <code>position</code> retains the same identity.</p>
6609          *
6610          * @param position Position of the item that has changed
6611          *
6612          * @see #notifyItemRangeChanged(int, int)
6613          */
notifyItemChanged(int position)6614         public final void notifyItemChanged(int position) {
6615             mObservable.notifyItemRangeChanged(position, 1);
6616         }
6617 
6618         /**
6619          * Notify any registered observers that the item at <code>position</code> has changed with
6620          * an optional payload object.
6621          *
6622          * <p>This is an item change event, not a structural change event. It indicates that any
6623          * reflection of the data at <code>position</code> is out of date and should be updated.
6624          * The item at <code>position</code> retains the same identity.
6625          * </p>
6626          *
6627          * <p>
6628          * Client can optionally pass a payload for partial change. These payloads will be merged
6629          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6630          * item is already represented by a ViewHolder and it will be rebound to the same
6631          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6632          * payloads on that item and prevent future payload until
6633          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6634          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6635          * attached, the payload will be simply dropped.
6636          *
6637          * @param position Position of the item that has changed
6638          * @param payload Optional parameter, use null to identify a "full" update
6639          *
6640          * @see #notifyItemRangeChanged(int, int)
6641          */
notifyItemChanged(int position, Object payload)6642         public final void notifyItemChanged(int position, Object payload) {
6643             mObservable.notifyItemRangeChanged(position, 1, payload);
6644         }
6645 
6646         /**
6647          * Notify any registered observers that the <code>itemCount</code> items starting at
6648          * position <code>positionStart</code> have changed.
6649          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
6650          *
6651          * <p>This is an item change event, not a structural change event. It indicates that
6652          * any reflection of the data in the given position range is out of date and should
6653          * be updated. The items in the given range retain the same identity.</p>
6654          *
6655          * @param positionStart Position of the first item that has changed
6656          * @param itemCount Number of items that have changed
6657          *
6658          * @see #notifyItemChanged(int)
6659          */
notifyItemRangeChanged(int positionStart, int itemCount)6660         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
6661             mObservable.notifyItemRangeChanged(positionStart, itemCount);
6662         }
6663 
6664         /**
6665          * Notify any registered observers that the <code>itemCount</code> items starting at
6666          * position <code>positionStart</code> have changed. An optional payload can be
6667          * passed to each changed item.
6668          *
6669          * <p>This is an item change event, not a structural change event. It indicates that any
6670          * reflection of the data in the given position range is out of date and should be updated.
6671          * The items in the given range retain the same identity.
6672          * </p>
6673          *
6674          * <p>
6675          * Client can optionally pass a payload for partial change. These payloads will be merged
6676          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
6677          * item is already represented by a ViewHolder and it will be rebound to the same
6678          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
6679          * payloads on that item and prevent future payload until
6680          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
6681          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
6682          * attached, the payload will be simply dropped.
6683          *
6684          * @param positionStart Position of the first item that has changed
6685          * @param itemCount Number of items that have changed
6686          * @param payload  Optional parameter, use null to identify a "full" update
6687          *
6688          * @see #notifyItemChanged(int)
6689          */
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)6690         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
6691             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
6692         }
6693 
6694         /**
6695          * Notify any registered observers that the item reflected at <code>position</code>
6696          * has been newly inserted. The item previously at <code>position</code> is now at
6697          * position <code>position + 1</code>.
6698          *
6699          * <p>This is a structural change event. Representations of other existing items in the
6700          * data set are still considered up to date and will not be rebound, though their
6701          * positions may be altered.</p>
6702          *
6703          * @param position Position of the newly inserted item in the data set
6704          *
6705          * @see #notifyItemRangeInserted(int, int)
6706          */
notifyItemInserted(int position)6707         public final void notifyItemInserted(int position) {
6708             mObservable.notifyItemRangeInserted(position, 1);
6709         }
6710 
6711         /**
6712          * Notify any registered observers that the item reflected at <code>fromPosition</code>
6713          * has been moved to <code>toPosition</code>.
6714          *
6715          * <p>This is a structural change event. Representations of other existing items in the
6716          * data set are still considered up to date and will not be rebound, though their
6717          * positions may be altered.</p>
6718          *
6719          * @param fromPosition Previous position of the item.
6720          * @param toPosition New position of the item.
6721          */
notifyItemMoved(int fromPosition, int toPosition)6722         public final void notifyItemMoved(int fromPosition, int toPosition) {
6723             mObservable.notifyItemMoved(fromPosition, toPosition);
6724         }
6725 
6726         /**
6727          * Notify any registered observers that the currently reflected <code>itemCount</code>
6728          * items starting at <code>positionStart</code> have been newly inserted. The items
6729          * previously located at <code>positionStart</code> and beyond can now be found starting
6730          * at position <code>positionStart + itemCount</code>.
6731          *
6732          * <p>This is a structural change event. Representations of other existing items in the
6733          * data set are still considered up to date and will not be rebound, though their positions
6734          * may be altered.</p>
6735          *
6736          * @param positionStart Position of the first item that was inserted
6737          * @param itemCount Number of items inserted
6738          *
6739          * @see #notifyItemInserted(int)
6740          */
notifyItemRangeInserted(int positionStart, int itemCount)6741         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
6742             mObservable.notifyItemRangeInserted(positionStart, itemCount);
6743         }
6744 
6745         /**
6746          * Notify any registered observers that the item previously located at <code>position</code>
6747          * has been removed from the data set. The items previously located at and after
6748          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
6749          *
6750          * <p>This is a structural change event. Representations of other existing items in the
6751          * data set are still considered up to date and will not be rebound, though their positions
6752          * may be altered.</p>
6753          *
6754          * @param position Position of the item that has now been removed
6755          *
6756          * @see #notifyItemRangeRemoved(int, int)
6757          */
notifyItemRemoved(int position)6758         public final void notifyItemRemoved(int position) {
6759             mObservable.notifyItemRangeRemoved(position, 1);
6760         }
6761 
6762         /**
6763          * Notify any registered observers that the <code>itemCount</code> items previously
6764          * located at <code>positionStart</code> have been removed from the data set. The items
6765          * previously located at and after <code>positionStart + itemCount</code> may now be found
6766          * at <code>oldPosition - itemCount</code>.
6767          *
6768          * <p>This is a structural change event. Representations of other existing items in the data
6769          * set are still considered up to date and will not be rebound, though their positions
6770          * may be altered.</p>
6771          *
6772          * @param positionStart Previous position of the first item that was removed
6773          * @param itemCount Number of items removed from the data set
6774          */
notifyItemRangeRemoved(int positionStart, int itemCount)6775         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
6776             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
6777         }
6778     }
6779 
dispatchChildDetached(View child)6780     void dispatchChildDetached(View child) {
6781         final ViewHolder viewHolder = getChildViewHolderInt(child);
6782         onChildDetachedFromWindow(child);
6783         if (mAdapter != null && viewHolder != null) {
6784             mAdapter.onViewDetachedFromWindow(viewHolder);
6785         }
6786         if (mOnChildAttachStateListeners != null) {
6787             final int cnt = mOnChildAttachStateListeners.size();
6788             for (int i = cnt - 1; i >= 0; i--) {
6789                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
6790             }
6791         }
6792     }
6793 
dispatchChildAttached(View child)6794     void dispatchChildAttached(View child) {
6795         final ViewHolder viewHolder = getChildViewHolderInt(child);
6796         onChildAttachedToWindow(child);
6797         if (mAdapter != null && viewHolder != null) {
6798             mAdapter.onViewAttachedToWindow(viewHolder);
6799         }
6800         if (mOnChildAttachStateListeners != null) {
6801             final int cnt = mOnChildAttachStateListeners.size();
6802             for (int i = cnt - 1; i >= 0; i--) {
6803                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
6804             }
6805         }
6806     }
6807 
6808     /**
6809      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
6810      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
6811      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
6812      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
6813      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
6814      * layout managers are provided for general use.
6815      * <p/>
6816      * If the LayoutManager specifies a default constructor or one with the signature
6817      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
6818      * instantiate and set the LayoutManager when being inflated. Most used properties can
6819      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
6820      * a LayoutManager specifies both constructors, the non-default constructor will take
6821      * precedence.
6822      *
6823      */
6824     public abstract static class LayoutManager {
6825         ChildHelper mChildHelper;
6826         RecyclerView mRecyclerView;
6827 
6828         @Nullable
6829         SmoothScroller mSmoothScroller;
6830 
6831         boolean mRequestedSimpleAnimations = false;
6832 
6833         boolean mIsAttachedToWindow = false;
6834 
6835         boolean mAutoMeasure = false;
6836 
6837         /**
6838          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
6839          * if the space that will be given to it is already larger than what it has measured before.
6840          */
6841         private boolean mMeasurementCacheEnabled = true;
6842 
6843         private boolean mItemPrefetchEnabled = true;
6844 
6845         /**
6846          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
6847          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
6848          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
6849          *
6850          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
6851          * will be reset upon layout to prevent initial prefetches (often large, since they're
6852          * proportional to expected child count) from expanding cache permanently.
6853          */
6854         int mPrefetchMaxCountObserved;
6855 
6856         /**
6857          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
6858          */
6859         boolean mPrefetchMaxObservedInInitialPrefetch;
6860 
6861         /**
6862          * These measure specs might be the measure specs that were passed into RecyclerView's
6863          * onMeasure method OR fake measure specs created by the RecyclerView.
6864          * For example, when a layout is run, RecyclerView always sets these specs to be
6865          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
6866          * <p>
6867          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
6868          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
6869          * corrupt values. Older platforms have no responsibility to provide a size if they set
6870          * mode to unspecified.
6871          */
6872         private int mWidthMode, mHeightMode;
6873         private int mWidth, mHeight;
6874 
6875 
6876         /**
6877          * Interface for LayoutManagers to request items to be prefetched, based on position, with
6878          * specified distance from viewport, which indicates priority.
6879          *
6880          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
6881          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
6882          */
6883         public interface LayoutPrefetchRegistry {
6884             /**
6885              * Requests an an item to be prefetched, based on position, with a specified distance,
6886              * indicating priority.
6887              *
6888              * @param layoutPosition Position of the item to prefetch.
6889              * @param pixelDistance Distance from the current viewport to the bounds of the item,
6890              *                      must be non-negative.
6891              */
6892             void addPosition(int layoutPosition, int pixelDistance);
6893         }
6894 
setRecyclerView(RecyclerView recyclerView)6895         void setRecyclerView(RecyclerView recyclerView) {
6896             if (recyclerView == null) {
6897                 mRecyclerView = null;
6898                 mChildHelper = null;
6899                 mWidth = 0;
6900                 mHeight = 0;
6901             } else {
6902                 mRecyclerView = recyclerView;
6903                 mChildHelper = recyclerView.mChildHelper;
6904                 mWidth = recyclerView.getWidth();
6905                 mHeight = recyclerView.getHeight();
6906             }
6907             mWidthMode = MeasureSpec.EXACTLY;
6908             mHeightMode = MeasureSpec.EXACTLY;
6909         }
6910 
setMeasureSpecs(int wSpec, int hSpec)6911         void setMeasureSpecs(int wSpec, int hSpec) {
6912             mWidth = MeasureSpec.getSize(wSpec);
6913             mWidthMode = MeasureSpec.getMode(wSpec);
6914             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6915                 mWidth = 0;
6916             }
6917 
6918             mHeight = MeasureSpec.getSize(hSpec);
6919             mHeightMode = MeasureSpec.getMode(hSpec);
6920             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
6921                 mHeight = 0;
6922             }
6923         }
6924 
6925         /**
6926          * Called after a layout is calculated during a measure pass when using auto-measure.
6927          * <p>
6928          * It simply traverses all children to calculate a bounding box then calls
6929          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
6930          * if they need to handle the bounding box differently.
6931          * <p>
6932          * For example, GridLayoutManager override that method to ensure that even if a column is
6933          * empty, the GridLayoutManager still measures wide enough to include it.
6934          *
6935          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
6936          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
6937          */
setMeasuredDimensionFromChildren(int widthSpec, int heightSpec)6938         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
6939             final int count = getChildCount();
6940             if (count == 0) {
6941                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
6942                 return;
6943             }
6944             int minX = Integer.MAX_VALUE;
6945             int minY = Integer.MAX_VALUE;
6946             int maxX = Integer.MIN_VALUE;
6947             int maxY = Integer.MIN_VALUE;
6948 
6949             for (int i = 0; i < count; i++) {
6950                 View child = getChildAt(i);
6951                 final Rect bounds = mRecyclerView.mTempRect;
6952                 getDecoratedBoundsWithMargins(child, bounds);
6953                 if (bounds.left < minX) {
6954                     minX = bounds.left;
6955                 }
6956                 if (bounds.right > maxX) {
6957                     maxX = bounds.right;
6958                 }
6959                 if (bounds.top < minY) {
6960                     minY = bounds.top;
6961                 }
6962                 if (bounds.bottom > maxY) {
6963                     maxY = bounds.bottom;
6964                 }
6965             }
6966             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
6967             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
6968         }
6969 
6970         /**
6971          * Sets the measured dimensions from the given bounding box of the children and the
6972          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
6973          * called after the RecyclerView calls
6974          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
6975          * <p>
6976          * This method should call {@link #setMeasuredDimension(int, int)}.
6977          * <p>
6978          * The default implementation adds the RecyclerView's padding to the given bounding box
6979          * then caps the value to be within the given measurement specs.
6980          * <p>
6981          * This method is only called if the LayoutManager opted into the auto measurement API.
6982          *
6983          * @param childrenBounds The bounding box of all children
6984          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
6985          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
6986          *
6987          * @see #setAutoMeasureEnabled(boolean)
6988          */
setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec)6989         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
6990             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
6991             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
6992             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
6993             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
6994             setMeasuredDimension(width, height);
6995         }
6996 
6997         /**
6998          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
6999          */
requestLayout()7000         public void requestLayout() {
7001             if (mRecyclerView != null) {
7002                 mRecyclerView.requestLayout();
7003             }
7004         }
7005 
7006         /**
7007          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7008          * {@link IllegalStateException} if it <b>is not</b>.
7009          *
7010          * @param message The message for the exception. Can be null.
7011          * @see #assertNotInLayoutOrScroll(String)
7012          */
assertInLayoutOrScroll(String message)7013         public void assertInLayoutOrScroll(String message) {
7014             if (mRecyclerView != null) {
7015                 mRecyclerView.assertInLayoutOrScroll(message);
7016             }
7017         }
7018 
7019         /**
7020          * Chooses a size from the given specs and parameters that is closest to the desired size
7021          * and also complies with the spec.
7022          *
7023          * @param spec The measureSpec
7024          * @param desired The preferred measurement
7025          * @param min The minimum value
7026          *
7027          * @return A size that fits to the given specs
7028          */
chooseSize(int spec, int desired, int min)7029         public static int chooseSize(int spec, int desired, int min) {
7030             final int mode = View.MeasureSpec.getMode(spec);
7031             final int size = View.MeasureSpec.getSize(spec);
7032             switch (mode) {
7033                 case View.MeasureSpec.EXACTLY:
7034                     return size;
7035                 case View.MeasureSpec.AT_MOST:
7036                     return Math.min(size, Math.max(desired, min));
7037                 case View.MeasureSpec.UNSPECIFIED:
7038                 default:
7039                     return Math.max(desired, min);
7040             }
7041         }
7042 
7043         /**
7044          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
7045          * {@link IllegalStateException} if it <b>is</b>.
7046          *
7047          * @param message The message for the exception. Can be null.
7048          * @see #assertInLayoutOrScroll(String)
7049          */
assertNotInLayoutOrScroll(String message)7050         public void assertNotInLayoutOrScroll(String message) {
7051             if (mRecyclerView != null) {
7052                 mRecyclerView.assertNotInLayoutOrScroll(message);
7053             }
7054         }
7055 
7056         /**
7057          * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
7058          * wants to handle the layout measurements itself.
7059          * <p>
7060          * This method is usually called by the LayoutManager with value {@code true} if it wants
7061          * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
7062          * the measurement logic, you can call this method with {@code false} and override
7063          * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
7064          * <p>
7065          * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
7066          * handle various specs provided by the RecyclerView's parent.
7067          * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
7068          * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
7069          * on children's positions. It does this while supporting all existing animation
7070          * capabilities of the RecyclerView.
7071          * <p>
7072          * AutoMeasure works as follows:
7073          * <ol>
7074          * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
7075          * the framework LayoutManagers use {@code auto-measure}.</li>
7076          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
7077          * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
7078          * doing any layout calculation.</li>
7079          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
7080          * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
7081          * decide whether to run a predictive layout or not. If it decides to do so, it will first
7082          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
7083          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
7084          * return the width and height of the RecyclerView as of the last layout calculation.
7085          * <p>
7086          * After handling the predictive case, RecyclerView will call
7087          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7088          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
7089          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
7090          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
7091          * <li>After the layout calculation, RecyclerView sets the measured width & height by
7092          * calculating the bounding box for the children (+ RecyclerView's padding). The
7093          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
7094          * different values. For instance, GridLayoutManager overrides this value to handle the case
7095          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
7096          * width to fit 3 items, not 2.</li>
7097          * <li>Any following on measure call to the RecyclerView will run
7098          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
7099          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
7100          * take care of which views are actually added / removed / moved / changed for animations so
7101          * that the LayoutManager should not worry about them and handle each
7102          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
7103          * </li>
7104          * <li>When measure is complete and RecyclerView's
7105          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
7106          * whether it already did layout calculations during the measure pass and if so, it re-uses
7107          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
7108          * if the last measure spec was different from the final dimensions or adapter contents
7109          * have changed between the measure call and the layout call.</li>
7110          * <li>Finally, animations are calculated and run as usual.</li>
7111          * </ol>
7112          *
7113          * @param enabled <code>True</code> if the Layout should be measured by the
7114          *                             RecyclerView, <code>false</code> if the LayoutManager wants
7115          *                             to measure itself.
7116          *
7117          * @see #setMeasuredDimension(Rect, int, int)
7118          * @see #isAutoMeasureEnabled()
7119          */
setAutoMeasureEnabled(boolean enabled)7120         public void setAutoMeasureEnabled(boolean enabled) {
7121             mAutoMeasure = enabled;
7122         }
7123 
7124         /**
7125          * Returns whether the LayoutManager uses the automatic measurement API or not.
7126          *
7127          * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
7128          * <code>false</code> if it measures itself.
7129          *
7130          * @see #setAutoMeasureEnabled(boolean)
7131          */
isAutoMeasureEnabled()7132         public boolean isAutoMeasureEnabled() {
7133             return mAutoMeasure;
7134         }
7135 
7136         /**
7137          * Returns whether this LayoutManager supports automatic item animations.
7138          * A LayoutManager wishing to support item animations should obey certain
7139          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
7140          * The default return value is <code>false</code>, so subclasses of LayoutManager
7141          * will not get predictive item animations by default.
7142          *
7143          * <p>Whether item animations are enabled in a RecyclerView is determined both
7144          * by the return value from this method and the
7145          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
7146          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
7147          * method returns false, then simple item animations will be enabled, in which
7148          * views that are moving onto or off of the screen are simply faded in/out. If
7149          * the RecyclerView has a non-null ItemAnimator and this method returns true,
7150          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
7151          * setup up the information needed to more intelligently predict where appearing
7152          * and disappearing views should be animated from/to.</p>
7153          *
7154          * @return true if predictive item animations should be enabled, false otherwise
7155          */
supportsPredictiveItemAnimations()7156         public boolean supportsPredictiveItemAnimations() {
7157             return false;
7158         }
7159 
7160         /**
7161          * Sets whether the LayoutManager should be queried for views outside of
7162          * its viewport while the UI thread is idle between frames.
7163          *
7164          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
7165          * view system traversals on devices running API 21 or greater. Default value is true.</p>
7166          *
7167          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
7168          * to RenderThread and the starting up its next frame at the next VSync pulse. By
7169          * prefetching out of window views in this time period, delays from inflation and view
7170          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
7171          *
7172          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
7173          * size of the View cache to hold prefetched views.</p>
7174          *
7175          * @param enabled <code>True</code> if items should be prefetched in between traversals.
7176          *
7177          * @see #isItemPrefetchEnabled()
7178          */
setItemPrefetchEnabled(boolean enabled)7179         public final void setItemPrefetchEnabled(boolean enabled) {
7180             if (enabled != mItemPrefetchEnabled) {
7181                 mItemPrefetchEnabled = enabled;
7182                 mPrefetchMaxCountObserved = 0;
7183                 if (mRecyclerView != null) {
7184                     mRecyclerView.mRecycler.updateViewCacheSize();
7185                 }
7186             }
7187         }
7188 
7189         /**
7190          * Sets whether the LayoutManager should be queried for views outside of
7191          * its viewport while the UI thread is idle between frames.
7192          *
7193          * @see #setItemPrefetchEnabled(boolean)
7194          *
7195          * @return true if item prefetch is enabled, false otherwise
7196          */
isItemPrefetchEnabled()7197         public final boolean isItemPrefetchEnabled() {
7198             return mItemPrefetchEnabled;
7199         }
7200 
7201         /**
7202          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
7203          *
7204          * <p>If item prefetch is enabled, this method is called in between traversals to gather
7205          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
7206          * traversals.</p>
7207          *
7208          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7209          * each item to be prepared, and these positions will have their ViewHolders created and
7210          * bound, if there is sufficient time available, in advance of being needed by a
7211          * scroll or layout.</p>
7212          *
7213          * @param dx X movement component.
7214          * @param dy Y movement component.
7215          * @param state State of RecyclerView
7216          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7217          *
7218          * @see #isItemPrefetchEnabled()
7219          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
7220          */
collectAdjacentPrefetchPositions(int dx, int dy, State state, LayoutPrefetchRegistry layoutPrefetchRegistry)7221         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
7222                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7223 
7224         /**
7225          * Gather all positions from the LayoutManager to be prefetched in preperation for its
7226          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
7227          *
7228          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
7229          *
7230          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
7231          * LayoutManager, this method is called in between draw traversals to gather
7232          * which positions this LayoutManager will first need, once it appears on the screen.</p>
7233          *
7234          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
7235          * vertically scrolling LayoutManager, this method would be called when the horizontal list
7236          * is about to come onscreen.</p>
7237          *
7238          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
7239          * each item to be prepared, and these positions will have their ViewHolders created and
7240          * bound, if there is sufficient time available, in advance of being needed by a
7241          * scroll or layout.</p>
7242          *
7243          * @param adapterItemCount number of items in the associated adapter.
7244          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
7245          *
7246          * @see #isItemPrefetchEnabled()
7247          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
7248          */
collectInitialPrefetchPositions(int adapterItemCount, LayoutPrefetchRegistry layoutPrefetchRegistry)7249         public void collectInitialPrefetchPositions(int adapterItemCount,
7250                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
7251 
dispatchAttachedToWindow(RecyclerView view)7252         void dispatchAttachedToWindow(RecyclerView view) {
7253             mIsAttachedToWindow = true;
7254             onAttachedToWindow(view);
7255         }
7256 
dispatchDetachedFromWindow(RecyclerView view, Recycler recycler)7257         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
7258             mIsAttachedToWindow = false;
7259             onDetachedFromWindow(view, recycler);
7260         }
7261 
7262         /**
7263          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
7264          * to a window.
7265          *
7266          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
7267          * is attached to window.
7268          */
isAttachedToWindow()7269         public boolean isAttachedToWindow() {
7270             return mIsAttachedToWindow;
7271         }
7272 
7273         /**
7274          * Causes the Runnable to execute on the next animation time step.
7275          * The runnable will be run on the user interface thread.
7276          * <p>
7277          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7278          *
7279          * @param action The Runnable that will be executed.
7280          *
7281          * @see #removeCallbacks
7282          */
postOnAnimation(Runnable action)7283         public void postOnAnimation(Runnable action) {
7284             if (mRecyclerView != null) {
7285                 mRecyclerView.postOnAnimation(action);
7286             }
7287         }
7288 
7289         /**
7290          * Removes the specified Runnable from the message queue.
7291          * <p>
7292          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
7293          *
7294          * @param action The Runnable to remove from the message handling queue
7295          *
7296          * @return true if RecyclerView could ask the Handler to remove the Runnable,
7297          *         false otherwise. When the returned value is true, the Runnable
7298          *         may or may not have been actually removed from the message queue
7299          *         (for instance, if the Runnable was not in the queue already.)
7300          *
7301          * @see #postOnAnimation
7302          */
removeCallbacks(Runnable action)7303         public boolean removeCallbacks(Runnable action) {
7304             if (mRecyclerView != null) {
7305                 return mRecyclerView.removeCallbacks(action);
7306             }
7307             return false;
7308         }
7309         /**
7310          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
7311          * is attached to a window.
7312          * <p>
7313          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7314          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7315          * not requested on the RecyclerView while it was detached.
7316          * <p>
7317          * Subclass implementations should always call through to the superclass implementation.
7318          *
7319          * @param view The RecyclerView this LayoutManager is bound to
7320          *
7321          * @see #onDetachedFromWindow(RecyclerView, Recycler)
7322          */
7323         @CallSuper
onAttachedToWindow(RecyclerView view)7324         public void onAttachedToWindow(RecyclerView view) {
7325         }
7326 
7327         /**
7328          * @deprecated
7329          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
7330          */
7331         @Deprecated
onDetachedFromWindow(RecyclerView view)7332         public void onDetachedFromWindow(RecyclerView view) {
7333 
7334         }
7335 
7336         /**
7337          * Called when this LayoutManager is detached from its parent RecyclerView or when
7338          * its parent RecyclerView is detached from its window.
7339          * <p>
7340          * LayoutManager should clear all of its View references as another LayoutManager might be
7341          * assigned to the RecyclerView.
7342          * <p>
7343          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
7344          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
7345          * not requested on the RecyclerView while it was detached.
7346          * <p>
7347          * If your LayoutManager has View references that it cleans in on-detach, it should also
7348          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
7349          * RecyclerView is re-attached.
7350          * <p>
7351          * Subclass implementations should always call through to the superclass implementation.
7352          *
7353          * @param view The RecyclerView this LayoutManager is bound to
7354          * @param recycler The recycler to use if you prefer to recycle your children instead of
7355          *                 keeping them around.
7356          *
7357          * @see #onAttachedToWindow(RecyclerView)
7358          */
7359         @CallSuper
onDetachedFromWindow(RecyclerView view, Recycler recycler)7360         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
7361             onDetachedFromWindow(view);
7362         }
7363 
7364         /**
7365          * Check if the RecyclerView is configured to clip child views to its padding.
7366          *
7367          * @return true if this RecyclerView clips children to its padding, false otherwise
7368          */
getClipToPadding()7369         public boolean getClipToPadding() {
7370             return mRecyclerView != null && mRecyclerView.mClipToPadding;
7371         }
7372 
7373         /**
7374          * Lay out all relevant child views from the given adapter.
7375          *
7376          * The LayoutManager is in charge of the behavior of item animations. By default,
7377          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
7378          * item animations are enabled. This means that add/remove operations on the
7379          * adapter will result in animations to add new or appearing items, removed or
7380          * disappearing items, and moved items. If a LayoutManager returns false from
7381          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
7382          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
7383          * RecyclerView will have enough information to run those animations in a simple
7384          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
7385          * simply fade views in and out, whether they are actually added/removed or whether
7386          * they are moved on or off the screen due to other add/remove operations.
7387          *
7388          * <p>A LayoutManager wanting a better item animation experience, where items can be
7389          * animated onto and off of the screen according to where the items exist when they
7390          * are not on screen, then the LayoutManager should return true from
7391          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
7392          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
7393          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
7394          * once as a "pre" layout step to determine where items would have been prior to
7395          * a real layout, and again to do the "real" layout. In the pre-layout phase,
7396          * items will remember their pre-layout positions to allow them to be laid out
7397          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
7398          * be returned from the scrap to help determine correct placement of other items.
7399          * These removed items should not be added to the child list, but should be used
7400          * to help calculate correct positioning of other views, including views that
7401          * were not previously onscreen (referred to as APPEARING views), but whose
7402          * pre-layout offscreen position can be determined given the extra
7403          * information about the pre-layout removed views.</p>
7404          *
7405          * <p>The second layout pass is the real layout in which only non-removed views
7406          * will be used. The only additional requirement during this pass is, if
7407          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
7408          * views exist in the child list prior to layout and which are not there after
7409          * layout (referred to as DISAPPEARING views), and to position/layout those views
7410          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
7411          * the animation system to know the location to which to animate these disappearing
7412          * views.</p>
7413          *
7414          * <p>The default LayoutManager implementations for RecyclerView handle all of these
7415          * requirements for animations already. Clients of RecyclerView can either use one
7416          * of these layout managers directly or look at their implementations of
7417          * onLayoutChildren() to see how they account for the APPEARING and
7418          * DISAPPEARING views.</p>
7419          *
7420          * @param recycler         Recycler to use for fetching potentially cached views for a
7421          *                         position
7422          * @param state            Transient state of RecyclerView
7423          */
onLayoutChildren(Recycler recycler, State state)7424         public void onLayoutChildren(Recycler recycler, State state) {
7425             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
7426         }
7427 
7428         /**
7429          * Called after a full layout calculation is finished. The layout calculation may include
7430          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
7431          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
7432          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
7433          * <p>
7434          * This is a good place for the LayoutManager to do some cleanup like pending scroll
7435          * position, saved state etc.
7436          *
7437          * @param state Transient state of RecyclerView
7438          */
onLayoutCompleted(State state)7439         public void onLayoutCompleted(State state) {
7440         }
7441 
7442         /**
7443          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
7444          *
7445          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
7446          * to store extra information specific to the layout. Client code should subclass
7447          * {@link RecyclerView.LayoutParams} for this purpose.</p>
7448          *
7449          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7450          * you must also override
7451          * {@link #checkLayoutParams(LayoutParams)},
7452          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7453          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7454          *
7455          * @return A new LayoutParams for a child view
7456          */
7457         public abstract LayoutParams generateDefaultLayoutParams();
7458 
7459         /**
7460          * Determines the validity of the supplied LayoutParams object.
7461          *
7462          * <p>This should check to make sure that the object is of the correct type
7463          * and all values are within acceptable ranges. The default implementation
7464          * returns <code>true</code> for non-null params.</p>
7465          *
7466          * @param lp LayoutParams object to check
7467          * @return true if this LayoutParams object is valid, false otherwise
7468          */
checkLayoutParams(LayoutParams lp)7469         public boolean checkLayoutParams(LayoutParams lp) {
7470             return lp != null;
7471         }
7472 
7473         /**
7474          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
7475          * values from the supplied LayoutParams object if possible.
7476          *
7477          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7478          * you must also override
7479          * {@link #checkLayoutParams(LayoutParams)},
7480          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7481          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7482          *
7483          * @param lp Source LayoutParams object to copy values from
7484          * @return a new LayoutParams object
7485          */
generateLayoutParams(ViewGroup.LayoutParams lp)7486         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
7487             if (lp instanceof LayoutParams) {
7488                 return new LayoutParams((LayoutParams) lp);
7489             } else if (lp instanceof MarginLayoutParams) {
7490                 return new LayoutParams((MarginLayoutParams) lp);
7491             } else {
7492                 return new LayoutParams(lp);
7493             }
7494         }
7495 
7496         /**
7497          * Create a LayoutParams object suitable for this LayoutManager from
7498          * an inflated layout resource.
7499          *
7500          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
7501          * you must also override
7502          * {@link #checkLayoutParams(LayoutParams)},
7503          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
7504          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
7505          *
7506          * @param c Context for obtaining styled attributes
7507          * @param attrs AttributeSet describing the supplied arguments
7508          * @return a new LayoutParams object
7509          */
generateLayoutParams(Context c, AttributeSet attrs)7510         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
7511             return new LayoutParams(c, attrs);
7512         }
7513 
7514         /**
7515          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
7516          * The default implementation does nothing and returns 0.
7517          *
7518          * @param dx            distance to scroll by in pixels. X increases as scroll position
7519          *                      approaches the right.
7520          * @param recycler      Recycler to use for fetching potentially cached views for a
7521          *                      position
7522          * @param state         Transient state of RecyclerView
7523          * @return The actual distance scrolled. The return value will be negative if dx was
7524          * negative and scrolling proceeeded in that direction.
7525          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
7526          */
scrollHorizontallyBy(int dx, Recycler recycler, State state)7527         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
7528             return 0;
7529         }
7530 
7531         /**
7532          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
7533          * The default implementation does nothing and returns 0.
7534          *
7535          * @param dy            distance to scroll in pixels. Y increases as scroll position
7536          *                      approaches the bottom.
7537          * @param recycler      Recycler to use for fetching potentially cached views for a
7538          *                      position
7539          * @param state         Transient state of RecyclerView
7540          * @return The actual distance scrolled. The return value will be negative if dy was
7541          * negative and scrolling proceeeded in that direction.
7542          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
7543          */
scrollVerticallyBy(int dy, Recycler recycler, State state)7544         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
7545             return 0;
7546         }
7547 
7548         /**
7549          * Query if horizontal scrolling is currently supported. The default implementation
7550          * returns false.
7551          *
7552          * @return True if this LayoutManager can scroll the current contents horizontally
7553          */
canScrollHorizontally()7554         public boolean canScrollHorizontally() {
7555             return false;
7556         }
7557 
7558         /**
7559          * Query if vertical scrolling is currently supported. The default implementation
7560          * returns false.
7561          *
7562          * @return True if this LayoutManager can scroll the current contents vertically
7563          */
canScrollVertically()7564         public boolean canScrollVertically() {
7565             return false;
7566         }
7567 
7568         /**
7569          * Scroll to the specified adapter position.
7570          *
7571          * Actual position of the item on the screen depends on the LayoutManager implementation.
7572          * @param position Scroll to this adapter position.
7573          */
scrollToPosition(int position)7574         public void scrollToPosition(int position) {
7575             if (DEBUG) {
7576                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
7577             }
7578         }
7579 
7580         /**
7581          * <p>Smooth scroll to the specified adapter position.</p>
7582          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
7583          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
7584          * </p>
7585          * @param recyclerView The RecyclerView to which this layout manager is attached
7586          * @param state    Current State of RecyclerView
7587          * @param position Scroll to this adapter position.
7588          */
smoothScrollToPosition(RecyclerView recyclerView, State state, int position)7589         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
7590                 int position) {
7591             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
7592         }
7593 
7594         /**
7595          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
7596          * <p>Calling this method will cancel any previous smooth scroll request.</p>
7597          * @param smoothScroller Instance which defines how smooth scroll should be animated
7598          */
startSmoothScroll(SmoothScroller smoothScroller)7599         public void startSmoothScroll(SmoothScroller smoothScroller) {
7600             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
7601                     && mSmoothScroller.isRunning()) {
7602                 mSmoothScroller.stop();
7603             }
7604             mSmoothScroller = smoothScroller;
7605             mSmoothScroller.start(mRecyclerView, this);
7606         }
7607 
7608         /**
7609          * @return true if RecycylerView is currently in the state of smooth scrolling.
7610          */
isSmoothScrolling()7611         public boolean isSmoothScrolling() {
7612             return mSmoothScroller != null && mSmoothScroller.isRunning();
7613         }
7614 
7615 
7616         /**
7617          * Returns the resolved layout direction for this RecyclerView.
7618          *
7619          * @return {@link android.view.View#LAYOUT_DIRECTION_RTL} if the layout
7620          * direction is RTL or returns
7621          * {@link android.view.View#LAYOUT_DIRECTION_LTR} if the layout direction
7622          * is not RTL.
7623          */
getLayoutDirection()7624         public int getLayoutDirection() {
7625             return mRecyclerView.getLayoutDirection();
7626         }
7627 
7628         /**
7629          * Ends all animations on the view created by the {@link ItemAnimator}.
7630          *
7631          * @param view The View for which the animations should be ended.
7632          * @see RecyclerView.ItemAnimator#endAnimations()
7633          */
endAnimation(View view)7634         public void endAnimation(View view) {
7635             if (mRecyclerView.mItemAnimator != null) {
7636                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
7637             }
7638         }
7639 
7640         /**
7641          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7642          * to the layout that is known to be going away, either because it has been
7643          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7644          * visible portion of the container but is being laid out in order to inform RecyclerView
7645          * in how to animate the item out of view.
7646          * <p>
7647          * Views added via this method are going to be invisible to LayoutManager after the
7648          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7649          * or won't be included in {@link #getChildCount()} method.
7650          *
7651          * @param child View to add and then remove with animation.
7652          */
addDisappearingView(View child)7653         public void addDisappearingView(View child) {
7654             addDisappearingView(child, -1);
7655         }
7656 
7657         /**
7658          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
7659          * to the layout that is known to be going away, either because it has been
7660          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
7661          * visible portion of the container but is being laid out in order to inform RecyclerView
7662          * in how to animate the item out of view.
7663          * <p>
7664          * Views added via this method are going to be invisible to LayoutManager after the
7665          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
7666          * or won't be included in {@link #getChildCount()} method.
7667          *
7668          * @param child View to add and then remove with animation.
7669          * @param index Index of the view.
7670          */
addDisappearingView(View child, int index)7671         public void addDisappearingView(View child, int index) {
7672             addViewInt(child, index, true);
7673         }
7674 
7675         /**
7676          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7677          * use this method to add views obtained from a {@link Recycler} using
7678          * {@link Recycler#getViewForPosition(int)}.
7679          *
7680          * @param child View to add
7681          */
addView(View child)7682         public void addView(View child) {
7683             addView(child, -1);
7684         }
7685 
7686         /**
7687          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
7688          * use this method to add views obtained from a {@link Recycler} using
7689          * {@link Recycler#getViewForPosition(int)}.
7690          *
7691          * @param child View to add
7692          * @param index Index to add child at
7693          */
addView(View child, int index)7694         public void addView(View child, int index) {
7695             addViewInt(child, index, false);
7696         }
7697 
addViewInt(View child, int index, boolean disappearing)7698         private void addViewInt(View child, int index, boolean disappearing) {
7699             final ViewHolder holder = getChildViewHolderInt(child);
7700             if (disappearing || holder.isRemoved()) {
7701                 // these views will be hidden at the end of the layout pass.
7702                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
7703             } else {
7704                 // This may look like unnecessary but may happen if layout manager supports
7705                 // predictive layouts and adapter removed then re-added the same item.
7706                 // In this case, added version will be visible in the post layout (because add is
7707                 // deferred) but RV will still bind it to the same View.
7708                 // So if a View re-appears in post layout pass, remove it from disappearing list.
7709                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
7710             }
7711             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
7712             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
7713                 if (holder.isScrap()) {
7714                     holder.unScrap();
7715                 } else {
7716                     holder.clearReturnedFromScrapFlag();
7717                 }
7718                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
7719                 if (DISPATCH_TEMP_DETACH) {
7720                     child.dispatchFinishTemporaryDetach();
7721                 }
7722             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
7723                 // ensure in correct position
7724                 int currentIndex = mChildHelper.indexOfChild(child);
7725                 if (index == -1) {
7726                     index = mChildHelper.getChildCount();
7727                 }
7728                 if (currentIndex == -1) {
7729                     throw new IllegalStateException("Added View has RecyclerView as parent but"
7730                             + " view is not a real child. Unfiltered index:"
7731                             + mRecyclerView.indexOfChild(child));
7732                 }
7733                 if (currentIndex != index) {
7734                     mRecyclerView.mLayout.moveView(currentIndex, index);
7735                 }
7736             } else {
7737                 mChildHelper.addView(child, index, false);
7738                 lp.mInsetsDirty = true;
7739                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
7740                     mSmoothScroller.onChildAttachedToWindow(child);
7741                 }
7742             }
7743             if (lp.mPendingInvalidate) {
7744                 if (DEBUG) {
7745                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
7746                 }
7747                 holder.itemView.invalidate();
7748                 lp.mPendingInvalidate = false;
7749             }
7750         }
7751 
7752         /**
7753          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7754          * use this method to completely remove a child view that is no longer needed.
7755          * LayoutManagers should strongly consider recycling removed views using
7756          * {@link Recycler#recycleView(android.view.View)}.
7757          *
7758          * @param child View to remove
7759          */
removeView(View child)7760         public void removeView(View child) {
7761             mChildHelper.removeView(child);
7762         }
7763 
7764         /**
7765          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
7766          * use this method to completely remove a child view that is no longer needed.
7767          * LayoutManagers should strongly consider recycling removed views using
7768          * {@link Recycler#recycleView(android.view.View)}.
7769          *
7770          * @param index Index of the child view to remove
7771          */
removeViewAt(int index)7772         public void removeViewAt(int index) {
7773             final View child = getChildAt(index);
7774             if (child != null) {
7775                 mChildHelper.removeViewAt(index);
7776             }
7777         }
7778 
7779         /**
7780          * Remove all views from the currently attached RecyclerView. This will not recycle
7781          * any of the affected views; the LayoutManager is responsible for doing so if desired.
7782          */
removeAllViews()7783         public void removeAllViews() {
7784             // Only remove non-animating views
7785             final int childCount = getChildCount();
7786             for (int i = childCount - 1; i >= 0; i--) {
7787                 mChildHelper.removeViewAt(i);
7788             }
7789         }
7790 
7791         /**
7792          * Returns offset of the RecyclerView's text baseline from the its top boundary.
7793          *
7794          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
7795          * there is no baseline.
7796          */
getBaseline()7797         public int getBaseline() {
7798             return -1;
7799         }
7800 
7801         /**
7802          * Returns the adapter position of the item represented by the given View. This does not
7803          * contain any adapter changes that might have happened after the last layout.
7804          *
7805          * @param view The view to query
7806          * @return The adapter position of the item which is rendered by this View.
7807          */
getPosition(View view)7808         public int getPosition(View view) {
7809             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
7810         }
7811 
7812         /**
7813          * Returns the View type defined by the adapter.
7814          *
7815          * @param view The view to query
7816          * @return The type of the view assigned by the adapter.
7817          */
getItemViewType(View view)7818         public int getItemViewType(View view) {
7819             return getChildViewHolderInt(view).getItemViewType();
7820         }
7821 
7822         /**
7823          * Traverses the ancestors of the given view and returns the item view that contains it
7824          * and also a direct child of the LayoutManager.
7825          * <p>
7826          * Note that this method may return null if the view is a child of the RecyclerView but
7827          * not a child of the LayoutManager (e.g. running a disappear animation).
7828          *
7829          * @param view The view that is a descendant of the LayoutManager.
7830          *
7831          * @return The direct child of the LayoutManager which contains the given view or null if
7832          * the provided view is not a descendant of this LayoutManager.
7833          *
7834          * @see RecyclerView#getChildViewHolder(View)
7835          * @see RecyclerView#findContainingViewHolder(View)
7836          */
7837         @Nullable
findContainingItemView(View view)7838         public View findContainingItemView(View view) {
7839             if (mRecyclerView == null) {
7840                 return null;
7841             }
7842             View found = mRecyclerView.findContainingItemView(view);
7843             if (found == null) {
7844                 return null;
7845             }
7846             if (mChildHelper.isHidden(found)) {
7847                 return null;
7848             }
7849             return found;
7850         }
7851 
7852         /**
7853          * Finds the view which represents the given adapter position.
7854          * <p>
7855          * This method traverses each child since it has no information about child order.
7856          * Override this method to improve performance if your LayoutManager keeps data about
7857          * child views.
7858          * <p>
7859          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
7860          *
7861          * @param position Position of the item in adapter
7862          * @return The child view that represents the given position or null if the position is not
7863          * laid out
7864          */
findViewByPosition(int position)7865         public View findViewByPosition(int position) {
7866             final int childCount = getChildCount();
7867             for (int i = 0; i < childCount; i++) {
7868                 View child = getChildAt(i);
7869                 ViewHolder vh = getChildViewHolderInt(child);
7870                 if (vh == null) {
7871                     continue;
7872                 }
7873                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
7874                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
7875                     return child;
7876                 }
7877             }
7878             return null;
7879         }
7880 
7881         /**
7882          * Temporarily detach a child view.
7883          *
7884          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7885          * views currently attached to the RecyclerView. Generally LayoutManager implementations
7886          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7887          * so that the detached view may be rebound and reused.</p>
7888          *
7889          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7890          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7891          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7892          * before the LayoutManager entry point method called by RecyclerView returns.</p>
7893          *
7894          * @param child Child to detach
7895          */
detachView(View child)7896         public void detachView(View child) {
7897             final int ind = mChildHelper.indexOfChild(child);
7898             if (ind >= 0) {
7899                 detachViewInternal(ind, child);
7900             }
7901         }
7902 
7903         /**
7904          * Temporarily detach a child view.
7905          *
7906          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
7907          * views currently attached to the RecyclerView. Generally LayoutManager implementations
7908          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
7909          * so that the detached view may be rebound and reused.</p>
7910          *
7911          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
7912          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
7913          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
7914          * before the LayoutManager entry point method called by RecyclerView returns.</p>
7915          *
7916          * @param index Index of the child to detach
7917          */
detachViewAt(int index)7918         public void detachViewAt(int index) {
7919             detachViewInternal(index, getChildAt(index));
7920         }
7921 
detachViewInternal(int index, View view)7922         private void detachViewInternal(int index, View view) {
7923             if (DISPATCH_TEMP_DETACH) {
7924                 view.dispatchStartTemporaryDetach();
7925             }
7926             mChildHelper.detachViewFromParent(index);
7927         }
7928 
7929         /**
7930          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7931          * This method should not be used to reattach views that were previously
7932          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7933          *
7934          * @param child Child to reattach
7935          * @param index Intended child index for child
7936          * @param lp LayoutParams for child
7937          */
attachView(View child, int index, LayoutParams lp)7938         public void attachView(View child, int index, LayoutParams lp) {
7939             ViewHolder vh = getChildViewHolderInt(child);
7940             if (vh.isRemoved()) {
7941                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
7942             } else {
7943                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
7944             }
7945             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
7946             if (DISPATCH_TEMP_DETACH)  {
7947                 child.dispatchFinishTemporaryDetach();
7948             }
7949         }
7950 
7951         /**
7952          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7953          * This method should not be used to reattach views that were previously
7954          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7955          *
7956          * @param child Child to reattach
7957          * @param index Intended child index for child
7958          */
attachView(View child, int index)7959         public void attachView(View child, int index) {
7960             attachView(child, index, (LayoutParams) child.getLayoutParams());
7961         }
7962 
7963         /**
7964          * Reattach a previously {@link #detachView(android.view.View) detached} view.
7965          * This method should not be used to reattach views that were previously
7966          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
7967          *
7968          * @param child Child to reattach
7969          */
attachView(View child)7970         public void attachView(View child) {
7971             attachView(child, -1);
7972         }
7973 
7974         /**
7975          * Finish removing a view that was previously temporarily
7976          * {@link #detachView(android.view.View) detached}.
7977          *
7978          * @param child Detached child to remove
7979          */
removeDetachedView(View child)7980         public void removeDetachedView(View child) {
7981             mRecyclerView.removeDetachedView(child, false);
7982         }
7983 
7984         /**
7985          * Moves a View from one position to another.
7986          *
7987          * @param fromIndex The View's initial index
7988          * @param toIndex The View's target index
7989          */
moveView(int fromIndex, int toIndex)7990         public void moveView(int fromIndex, int toIndex) {
7991             View view = getChildAt(fromIndex);
7992             if (view == null) {
7993                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
7994                         + fromIndex);
7995             }
7996             detachViewAt(fromIndex);
7997             attachView(view, toIndex);
7998         }
7999 
8000         /**
8001          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8002          *
8003          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8004          * different data.</p>
8005          *
8006          * @param child Child to detach and scrap
8007          * @param recycler Recycler to deposit the new scrap view into
8008          */
detachAndScrapView(View child, Recycler recycler)8009         public void detachAndScrapView(View child, Recycler recycler) {
8010             int index = mChildHelper.indexOfChild(child);
8011             scrapOrRecycleView(recycler, index, child);
8012         }
8013 
8014         /**
8015          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
8016          *
8017          * <p>Scrapping a view allows it to be rebound and reused to show updated or
8018          * different data.</p>
8019          *
8020          * @param index Index of child to detach and scrap
8021          * @param recycler Recycler to deposit the new scrap view into
8022          */
detachAndScrapViewAt(int index, Recycler recycler)8023         public void detachAndScrapViewAt(int index, Recycler recycler) {
8024             final View child = getChildAt(index);
8025             scrapOrRecycleView(recycler, index, child);
8026         }
8027 
8028         /**
8029          * Remove a child view and recycle it using the given Recycler.
8030          *
8031          * @param child Child to remove and recycle
8032          * @param recycler Recycler to use to recycle child
8033          */
removeAndRecycleView(View child, Recycler recycler)8034         public void removeAndRecycleView(View child, Recycler recycler) {
8035             removeView(child);
8036             recycler.recycleView(child);
8037         }
8038 
8039         /**
8040          * Remove a child view and recycle it using the given Recycler.
8041          *
8042          * @param index Index of child to remove and recycle
8043          * @param recycler Recycler to use to recycle child
8044          */
removeAndRecycleViewAt(int index, Recycler recycler)8045         public void removeAndRecycleViewAt(int index, Recycler recycler) {
8046             final View view = getChildAt(index);
8047             removeViewAt(index);
8048             recycler.recycleView(view);
8049         }
8050 
8051         /**
8052          * Return the current number of child views attached to the parent RecyclerView.
8053          * This does not include child views that were temporarily detached and/or scrapped.
8054          *
8055          * @return Number of attached children
8056          */
getChildCount()8057         public int getChildCount() {
8058             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
8059         }
8060 
8061         /**
8062          * Return the child view at the given index
8063          * @param index Index of child to return
8064          * @return Child view at index
8065          */
getChildAt(int index)8066         public View getChildAt(int index) {
8067             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
8068         }
8069 
8070         /**
8071          * Return the width measurement spec mode of the RecyclerView.
8072          * <p>
8073          * This value is set only if the LayoutManager opts into the auto measure api via
8074          * {@link #setAutoMeasureEnabled(boolean)}.
8075          * <p>
8076          * When RecyclerView is running a layout, this value is always set to
8077          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8078          *
8079          * @return Width measure spec mode.
8080          *
8081          * @see View.MeasureSpec#getMode(int)
8082          * @see View#onMeasure(int, int)
8083          */
getWidthMode()8084         public int getWidthMode() {
8085             return mWidthMode;
8086         }
8087 
8088         /**
8089          * Return the height measurement spec mode of the RecyclerView.
8090          * <p>
8091          * This value is set only if the LayoutManager opts into the auto measure api via
8092          * {@link #setAutoMeasureEnabled(boolean)}.
8093          * <p>
8094          * When RecyclerView is running a layout, this value is always set to
8095          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
8096          *
8097          * @return Height measure spec mode.
8098          *
8099          * @see View.MeasureSpec#getMode(int)
8100          * @see View#onMeasure(int, int)
8101          */
getHeightMode()8102         public int getHeightMode() {
8103             return mHeightMode;
8104         }
8105 
8106         /**
8107          * Return the width of the parent RecyclerView
8108          *
8109          * @return Width in pixels
8110          */
getWidth()8111         public int getWidth() {
8112             return mWidth;
8113         }
8114 
8115         /**
8116          * Return the height of the parent RecyclerView
8117          *
8118          * @return Height in pixels
8119          */
getHeight()8120         public int getHeight() {
8121             return mHeight;
8122         }
8123 
8124         /**
8125          * Return the left padding of the parent RecyclerView
8126          *
8127          * @return Padding in pixels
8128          */
getPaddingLeft()8129         public int getPaddingLeft() {
8130             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
8131         }
8132 
8133         /**
8134          * Return the top padding of the parent RecyclerView
8135          *
8136          * @return Padding in pixels
8137          */
getPaddingTop()8138         public int getPaddingTop() {
8139             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
8140         }
8141 
8142         /**
8143          * Return the right padding of the parent RecyclerView
8144          *
8145          * @return Padding in pixels
8146          */
getPaddingRight()8147         public int getPaddingRight() {
8148             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
8149         }
8150 
8151         /**
8152          * Return the bottom padding of the parent RecyclerView
8153          *
8154          * @return Padding in pixels
8155          */
getPaddingBottom()8156         public int getPaddingBottom() {
8157             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
8158         }
8159 
8160         /**
8161          * Return the start padding of the parent RecyclerView
8162          *
8163          * @return Padding in pixels
8164          */
getPaddingStart()8165         public int getPaddingStart() {
8166             return mRecyclerView != null ? mRecyclerView.getPaddingStart() : 0;
8167         }
8168 
8169         /**
8170          * Return the end padding of the parent RecyclerView
8171          *
8172          * @return Padding in pixels
8173          */
getPaddingEnd()8174         public int getPaddingEnd() {
8175             return mRecyclerView != null ? mRecyclerView.getPaddingEnd() : 0;
8176         }
8177 
8178         /**
8179          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
8180          *
8181          * @return True if the RecyclerView has focus, false otherwise.
8182          * @see View#isFocused()
8183          */
isFocused()8184         public boolean isFocused() {
8185             return mRecyclerView != null && mRecyclerView.isFocused();
8186         }
8187 
8188         /**
8189          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
8190          *
8191          * @return true if the RecyclerView has or contains focus
8192          * @see View#hasFocus()
8193          */
hasFocus()8194         public boolean hasFocus() {
8195             return mRecyclerView != null && mRecyclerView.hasFocus();
8196         }
8197 
8198         /**
8199          * Returns the item View which has or contains focus.
8200          *
8201          * @return A direct child of RecyclerView which has focus or contains the focused child.
8202          */
getFocusedChild()8203         public View getFocusedChild() {
8204             if (mRecyclerView == null) {
8205                 return null;
8206             }
8207             final View focused = mRecyclerView.getFocusedChild();
8208             if (focused == null || mChildHelper.isHidden(focused)) {
8209                 return null;
8210             }
8211             return focused;
8212         }
8213 
8214         /**
8215          * Returns the number of items in the adapter bound to the parent RecyclerView.
8216          * <p>
8217          * Note that this number is not necessarily equal to
8218          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
8219          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
8220          * For more details, check the documentation for
8221          * {@link State#getItemCount() State#getItemCount()}.
8222          *
8223          * @return The number of items in the bound adapter
8224          * @see State#getItemCount()
8225          */
getItemCount()8226         public int getItemCount() {
8227             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
8228             return a != null ? a.getItemCount() : 0;
8229         }
8230 
8231         /**
8232          * Offset all child views attached to the parent RecyclerView by dx pixels along
8233          * the horizontal axis.
8234          *
8235          * @param dx Pixels to offset by
8236          */
offsetChildrenHorizontal(int dx)8237         public void offsetChildrenHorizontal(int dx) {
8238             if (mRecyclerView != null) {
8239                 mRecyclerView.offsetChildrenHorizontal(dx);
8240             }
8241         }
8242 
8243         /**
8244          * Offset all child views attached to the parent RecyclerView by dy pixels along
8245          * the vertical axis.
8246          *
8247          * @param dy Pixels to offset by
8248          */
offsetChildrenVertical(int dy)8249         public void offsetChildrenVertical(int dy) {
8250             if (mRecyclerView != null) {
8251                 mRecyclerView.offsetChildrenVertical(dy);
8252             }
8253         }
8254 
8255         /**
8256          * Flags a view so that it will not be scrapped or recycled.
8257          * <p>
8258          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
8259          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
8260          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
8261          * ignore the child.
8262          * <p>
8263          * Before this child can be recycled again, you have to call
8264          * {@link #stopIgnoringView(View)}.
8265          * <p>
8266          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8267          *
8268          * @param view View to ignore.
8269          * @see #stopIgnoringView(View)
8270          */
ignoreView(View view)8271         public void ignoreView(View view) {
8272             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
8273                 // checking this because calling this method on a recycled or detached view may
8274                 // cause loss of state.
8275                 throw new IllegalArgumentException("View should be fully attached to be ignored");
8276             }
8277             final ViewHolder vh = getChildViewHolderInt(view);
8278             vh.addFlags(ViewHolder.FLAG_IGNORE);
8279             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
8280         }
8281 
8282         /**
8283          * View can be scrapped and recycled again.
8284          * <p>
8285          * Note that calling this method removes all information in the view holder.
8286          * <p>
8287          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
8288          *
8289          * @param view View to ignore.
8290          */
stopIgnoringView(View view)8291         public void stopIgnoringView(View view) {
8292             final ViewHolder vh = getChildViewHolderInt(view);
8293             vh.stopIgnoring();
8294             vh.resetInternal();
8295             vh.addFlags(ViewHolder.FLAG_INVALID);
8296         }
8297 
8298         /**
8299          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
8300          * into the given Recycler. The Recycler may prefer to reuse scrap views before
8301          * other views that were previously recycled.
8302          *
8303          * @param recycler Recycler to scrap views into
8304          */
detachAndScrapAttachedViews(Recycler recycler)8305         public void detachAndScrapAttachedViews(Recycler recycler) {
8306             final int childCount = getChildCount();
8307             for (int i = childCount - 1; i >= 0; i--) {
8308                 final View v = getChildAt(i);
8309                 scrapOrRecycleView(recycler, i, v);
8310             }
8311         }
8312 
scrapOrRecycleView(Recycler recycler, int index, View view)8313         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
8314             final ViewHolder viewHolder = getChildViewHolderInt(view);
8315             if (viewHolder.shouldIgnore()) {
8316                 if (DEBUG) {
8317                     Log.d(TAG, "ignoring view " + viewHolder);
8318                 }
8319                 return;
8320             }
8321             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
8322                     && !mRecyclerView.mAdapter.hasStableIds()) {
8323                 removeViewAt(index);
8324                 recycler.recycleViewHolderInternal(viewHolder);
8325             } else {
8326                 detachViewAt(index);
8327                 recycler.scrapView(view);
8328                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
8329             }
8330         }
8331 
8332         /**
8333          * Recycles the scrapped views.
8334          * <p>
8335          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
8336          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
8337          * call remove and invalidate RecyclerView to ensure UI update.
8338          *
8339          * @param recycler Recycler
8340          */
removeAndRecycleScrapInt(Recycler recycler)8341         void removeAndRecycleScrapInt(Recycler recycler) {
8342             final int scrapCount = recycler.getScrapCount();
8343             // Loop backward, recycler might be changed by removeDetachedView()
8344             for (int i = scrapCount - 1; i >= 0; i--) {
8345                 final View scrap = recycler.getScrapViewAt(i);
8346                 final ViewHolder vh = getChildViewHolderInt(scrap);
8347                 if (vh.shouldIgnore()) {
8348                     continue;
8349                 }
8350                 // If the scrap view is animating, we need to cancel them first. If we cancel it
8351                 // here, ItemAnimator callback may recycle it which will cause double recycling.
8352                 // To avoid this, we mark it as not recycleable before calling the item animator.
8353                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
8354                 // the view) may recycle it too, so we guard it before we call user APIs.
8355                 vh.setIsRecyclable(false);
8356                 if (vh.isTmpDetached()) {
8357                     mRecyclerView.removeDetachedView(scrap, false);
8358                 }
8359                 if (mRecyclerView.mItemAnimator != null) {
8360                     mRecyclerView.mItemAnimator.endAnimation(vh);
8361                 }
8362                 vh.setIsRecyclable(true);
8363                 recycler.quickRecycleScrapView(scrap);
8364             }
8365             recycler.clearScrap();
8366             if (scrapCount > 0) {
8367                 mRecyclerView.invalidate();
8368             }
8369         }
8370 
8371 
8372         /**
8373          * Measure a child view using standard measurement policy, taking the padding
8374          * of the parent RecyclerView and any added item decorations into account.
8375          *
8376          * <p>If the RecyclerView can be scrolled in either dimension the caller may
8377          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8378          *
8379          * @param child Child view to measure
8380          * @param widthUsed Width in pixels currently consumed by other views, if relevant
8381          * @param heightUsed Height in pixels currently consumed by other views, if relevant
8382          */
measureChild(View child, int widthUsed, int heightUsed)8383         public void measureChild(View child, int widthUsed, int heightUsed) {
8384             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8385 
8386             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8387             widthUsed += insets.left + insets.right;
8388             heightUsed += insets.top + insets.bottom;
8389             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8390                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
8391                     canScrollHorizontally());
8392             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8393                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
8394                     canScrollVertically());
8395             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8396                 child.measure(widthSpec, heightSpec);
8397             }
8398         }
8399 
8400         /**
8401          * RecyclerView internally does its own View measurement caching which should help with
8402          * WRAP_CONTENT.
8403          * <p>
8404          * Use this method if the View is already measured once in this layout pass.
8405          */
shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8406         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8407             return !mMeasurementCacheEnabled
8408                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
8409                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
8410         }
8411 
8412         // we may consider making this public
8413         /**
8414          * RecyclerView internally does its own View measurement caching which should help with
8415          * WRAP_CONTENT.
8416          * <p>
8417          * Use this method if the View is not yet measured and you need to decide whether to
8418          * measure this View or not.
8419          */
shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp)8420         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
8421             return child.isLayoutRequested()
8422                     || !mMeasurementCacheEnabled
8423                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
8424                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
8425         }
8426 
8427         /**
8428          * In addition to the View Framework's measurement cache, RecyclerView uses its own
8429          * additional measurement cache for its children to avoid re-measuring them when not
8430          * necessary. It is on by default but it can be turned off via
8431          * {@link #setMeasurementCacheEnabled(boolean)}.
8432          *
8433          * @return True if measurement cache is enabled, false otherwise.
8434          *
8435          * @see #setMeasurementCacheEnabled(boolean)
8436          */
isMeasurementCacheEnabled()8437         public boolean isMeasurementCacheEnabled() {
8438             return mMeasurementCacheEnabled;
8439         }
8440 
8441         /**
8442          * Sets whether RecyclerView should use its own measurement cache for the children. This is
8443          * a more aggressive cache than the framework uses.
8444          *
8445          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
8446          *
8447          * @see #isMeasurementCacheEnabled()
8448          */
setMeasurementCacheEnabled(boolean measurementCacheEnabled)8449         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
8450             mMeasurementCacheEnabled = measurementCacheEnabled;
8451         }
8452 
isMeasurementUpToDate(int childSize, int spec, int dimension)8453         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
8454             final int specMode = MeasureSpec.getMode(spec);
8455             final int specSize = MeasureSpec.getSize(spec);
8456             if (dimension > 0 && childSize != dimension) {
8457                 return false;
8458             }
8459             switch (specMode) {
8460                 case MeasureSpec.UNSPECIFIED:
8461                     return true;
8462                 case MeasureSpec.AT_MOST:
8463                     return specSize >= childSize;
8464                 case MeasureSpec.EXACTLY:
8465                     return  specSize == childSize;
8466             }
8467             return false;
8468         }
8469 
8470         /**
8471          * Measure a child view using standard measurement policy, taking the padding
8472          * of the parent RecyclerView, any added item decorations and the child margins
8473          * into account.
8474          *
8475          * <p>If the RecyclerView can be scrolled in either dimension the caller may
8476          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
8477          *
8478          * @param child Child view to measure
8479          * @param widthUsed Width in pixels currently consumed by other views, if relevant
8480          * @param heightUsed Height in pixels currently consumed by other views, if relevant
8481          */
measureChildWithMargins(View child, int widthUsed, int heightUsed)8482         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
8483             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8484 
8485             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8486             widthUsed += insets.left + insets.right;
8487             heightUsed += insets.top + insets.bottom;
8488 
8489             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
8490                     getPaddingLeft() + getPaddingRight()
8491                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
8492                     canScrollHorizontally());
8493             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
8494                     getPaddingTop() + getPaddingBottom()
8495                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
8496                     canScrollVertically());
8497             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
8498                 child.measure(widthSpec, heightSpec);
8499             }
8500         }
8501 
8502         /**
8503          * Calculate a MeasureSpec value for measuring a child view in one dimension.
8504          *
8505          * @param parentSize Size of the parent view where the child will be placed
8506          * @param padding Total space currently consumed by other elements of the parent
8507          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8508          *                       Generally obtained from the child view's LayoutParams
8509          * @param canScroll true if the parent RecyclerView can scroll in this dimension
8510          *
8511          * @return a MeasureSpec value for the child view
8512          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
8513          */
8514         @Deprecated
getChildMeasureSpec(int parentSize, int padding, int childDimension, boolean canScroll)8515         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
8516                 boolean canScroll) {
8517             int size = Math.max(0, parentSize - padding);
8518             int resultSize = 0;
8519             int resultMode = 0;
8520             if (canScroll) {
8521                 if (childDimension >= 0) {
8522                     resultSize = childDimension;
8523                     resultMode = MeasureSpec.EXACTLY;
8524                 } else {
8525                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
8526                     // instead using UNSPECIFIED.
8527                     resultSize = 0;
8528                     resultMode = MeasureSpec.UNSPECIFIED;
8529                 }
8530             } else {
8531                 if (childDimension >= 0) {
8532                     resultSize = childDimension;
8533                     resultMode = MeasureSpec.EXACTLY;
8534                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8535                     resultSize = size;
8536                     // TODO this should be my spec.
8537                     resultMode = MeasureSpec.EXACTLY;
8538                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8539                     resultSize = size;
8540                     resultMode = MeasureSpec.AT_MOST;
8541                 }
8542             }
8543             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8544         }
8545 
8546         /**
8547          * Calculate a MeasureSpec value for measuring a child view in one dimension.
8548          *
8549          * @param parentSize Size of the parent view where the child will be placed
8550          * @param parentMode The measurement spec mode of the parent
8551          * @param padding Total space currently consumed by other elements of parent
8552          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
8553          *                       Generally obtained from the child view's LayoutParams
8554          * @param canScroll true if the parent RecyclerView can scroll in this dimension
8555          *
8556          * @return a MeasureSpec value for the child view
8557          */
getChildMeasureSpec(int parentSize, int parentMode, int padding, int childDimension, boolean canScroll)8558         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
8559                 int childDimension, boolean canScroll) {
8560             int size = Math.max(0, parentSize - padding);
8561             int resultSize = 0;
8562             int resultMode = 0;
8563             if (canScroll) {
8564                 if (childDimension >= 0) {
8565                     resultSize = childDimension;
8566                     resultMode = MeasureSpec.EXACTLY;
8567                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8568                     switch (parentMode) {
8569                         case MeasureSpec.AT_MOST:
8570                         case MeasureSpec.EXACTLY:
8571                             resultSize = size;
8572                             resultMode = parentMode;
8573                             break;
8574                         case MeasureSpec.UNSPECIFIED:
8575                             resultSize = 0;
8576                             resultMode = MeasureSpec.UNSPECIFIED;
8577                             break;
8578                     }
8579                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8580                     resultSize = 0;
8581                     resultMode = MeasureSpec.UNSPECIFIED;
8582                 }
8583             } else {
8584                 if (childDimension >= 0) {
8585                     resultSize = childDimension;
8586                     resultMode = MeasureSpec.EXACTLY;
8587                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
8588                     resultSize = size;
8589                     resultMode = parentMode;
8590                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
8591                     resultSize = size;
8592                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
8593                         resultMode = MeasureSpec.AT_MOST;
8594                     } else {
8595                         resultMode = MeasureSpec.UNSPECIFIED;
8596                     }
8597 
8598                 }
8599             }
8600             //noinspection WrongConstant
8601             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
8602         }
8603 
8604         /**
8605          * Returns the measured width of the given child, plus the additional size of
8606          * any insets applied by {@link ItemDecoration ItemDecorations}.
8607          *
8608          * @param child Child view to query
8609          * @return child's measured width plus <code>ItemDecoration</code> insets
8610          *
8611          * @see View#getMeasuredWidth()
8612          */
getDecoratedMeasuredWidth(View child)8613         public int getDecoratedMeasuredWidth(View child) {
8614             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8615             return child.getMeasuredWidth() + insets.left + insets.right;
8616         }
8617 
8618         /**
8619          * Returns the measured height of the given child, plus the additional size of
8620          * any insets applied by {@link ItemDecoration ItemDecorations}.
8621          *
8622          * @param child Child view to query
8623          * @return child's measured height plus <code>ItemDecoration</code> insets
8624          *
8625          * @see View#getMeasuredHeight()
8626          */
getDecoratedMeasuredHeight(View child)8627         public int getDecoratedMeasuredHeight(View child) {
8628             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8629             return child.getMeasuredHeight() + insets.top + insets.bottom;
8630         }
8631 
8632         /**
8633          * Lay out the given child view within the RecyclerView using coordinates that
8634          * include any current {@link ItemDecoration ItemDecorations}.
8635          *
8636          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8637          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8638          * ignore decoration insets within measurement and layout code. See the following
8639          * methods:</p>
8640          * <ul>
8641          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
8642          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
8643          *     <li>{@link #measureChild(View, int, int)}</li>
8644          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8645          *     <li>{@link #getDecoratedLeft(View)}</li>
8646          *     <li>{@link #getDecoratedTop(View)}</li>
8647          *     <li>{@link #getDecoratedRight(View)}</li>
8648          *     <li>{@link #getDecoratedBottom(View)}</li>
8649          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8650          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8651          * </ul>
8652          *
8653          * @param child Child to lay out
8654          * @param left Left edge, with item decoration insets included
8655          * @param top Top edge, with item decoration insets included
8656          * @param right Right edge, with item decoration insets included
8657          * @param bottom Bottom edge, with item decoration insets included
8658          *
8659          * @see View#layout(int, int, int, int)
8660          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
8661          */
layoutDecorated(View child, int left, int top, int right, int bottom)8662         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
8663             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8664             child.layout(left + insets.left, top + insets.top, right - insets.right,
8665                     bottom - insets.bottom);
8666         }
8667 
8668         /**
8669          * Lay out the given child view within the RecyclerView using coordinates that
8670          * include any current {@link ItemDecoration ItemDecorations} and margins.
8671          *
8672          * <p>LayoutManagers should prefer working in sizes and coordinates that include
8673          * item decoration insets whenever possible. This allows the LayoutManager to effectively
8674          * ignore decoration insets within measurement and layout code. See the following
8675          * methods:</p>
8676          * <ul>
8677          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
8678          *     <li>{@link #measureChild(View, int, int)}</li>
8679          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
8680          *     <li>{@link #getDecoratedLeft(View)}</li>
8681          *     <li>{@link #getDecoratedTop(View)}</li>
8682          *     <li>{@link #getDecoratedRight(View)}</li>
8683          *     <li>{@link #getDecoratedBottom(View)}</li>
8684          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
8685          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
8686          * </ul>
8687          *
8688          * @param child Child to lay out
8689          * @param left Left edge, with item decoration insets and left margin included
8690          * @param top Top edge, with item decoration insets and top margin included
8691          * @param right Right edge, with item decoration insets and right margin included
8692          * @param bottom Bottom edge, with item decoration insets and bottom margin included
8693          *
8694          * @see View#layout(int, int, int, int)
8695          * @see #layoutDecorated(View, int, int, int, int)
8696          */
layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom)8697         public void layoutDecoratedWithMargins(View child, int left, int top, int right,
8698                 int bottom) {
8699             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8700             final Rect insets = lp.mDecorInsets;
8701             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
8702                     right - insets.right - lp.rightMargin,
8703                     bottom - insets.bottom - lp.bottomMargin);
8704         }
8705 
8706         /**
8707          * Calculates the bounding box of the View while taking into account its matrix changes
8708          * (translation, scale etc) with respect to the RecyclerView.
8709          * <p>
8710          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
8711          * the View's matrix so that the decor offsets also go through the same transformation.
8712          *
8713          * @param child The ItemView whose bounding box should be calculated.
8714          * @param includeDecorInsets True if the decor insets should be included in the bounding box
8715          * @param out The rectangle into which the output will be written.
8716          */
getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out)8717         public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
8718             if (includeDecorInsets) {
8719                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
8720                 out.set(-insets.left, -insets.top,
8721                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
8722             } else {
8723                 out.set(0, 0, child.getWidth(), child.getHeight());
8724             }
8725 
8726             if (mRecyclerView != null) {
8727                 final Matrix childMatrix = child.getMatrix();
8728                 if (childMatrix != null && !childMatrix.isIdentity()) {
8729                     final RectF tempRectF = mRecyclerView.mTempRectF;
8730                     tempRectF.set(out);
8731                     childMatrix.mapRect(tempRectF);
8732                     out.set(
8733                             (int) Math.floor(tempRectF.left),
8734                             (int) Math.floor(tempRectF.top),
8735                             (int) Math.ceil(tempRectF.right),
8736                             (int) Math.ceil(tempRectF.bottom)
8737                     );
8738                 }
8739             }
8740             out.offset(child.getLeft(), child.getTop());
8741         }
8742 
8743         /**
8744          * Returns the bounds of the view including its decoration and margins.
8745          *
8746          * @param view The view element to check
8747          * @param outBounds A rect that will receive the bounds of the element including its
8748          *                  decoration and margins.
8749          */
getDecoratedBoundsWithMargins(View view, Rect outBounds)8750         public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
8751             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
8752         }
8753 
8754         /**
8755          * Returns the left edge of the given child view within its parent, offset by any applied
8756          * {@link ItemDecoration ItemDecorations}.
8757          *
8758          * @param child Child to query
8759          * @return Child left edge with offsets applied
8760          * @see #getLeftDecorationWidth(View)
8761          */
getDecoratedLeft(View child)8762         public int getDecoratedLeft(View child) {
8763             return child.getLeft() - getLeftDecorationWidth(child);
8764         }
8765 
8766         /**
8767          * Returns the top edge of the given child view within its parent, offset by any applied
8768          * {@link ItemDecoration ItemDecorations}.
8769          *
8770          * @param child Child to query
8771          * @return Child top edge with offsets applied
8772          * @see #getTopDecorationHeight(View)
8773          */
getDecoratedTop(View child)8774         public int getDecoratedTop(View child) {
8775             return child.getTop() - getTopDecorationHeight(child);
8776         }
8777 
8778         /**
8779          * Returns the right edge of the given child view within its parent, offset by any applied
8780          * {@link ItemDecoration ItemDecorations}.
8781          *
8782          * @param child Child to query
8783          * @return Child right edge with offsets applied
8784          * @see #getRightDecorationWidth(View)
8785          */
getDecoratedRight(View child)8786         public int getDecoratedRight(View child) {
8787             return child.getRight() + getRightDecorationWidth(child);
8788         }
8789 
8790         /**
8791          * Returns the bottom edge of the given child view within its parent, offset by any applied
8792          * {@link ItemDecoration ItemDecorations}.
8793          *
8794          * @param child Child to query
8795          * @return Child bottom edge with offsets applied
8796          * @see #getBottomDecorationHeight(View)
8797          */
getDecoratedBottom(View child)8798         public int getDecoratedBottom(View child) {
8799             return child.getBottom() + getBottomDecorationHeight(child);
8800         }
8801 
8802         /**
8803          * Calculates the item decor insets applied to the given child and updates the provided
8804          * Rect instance with the inset values.
8805          * <ul>
8806          *     <li>The Rect's left is set to the total width of left decorations.</li>
8807          *     <li>The Rect's top is set to the total height of top decorations.</li>
8808          *     <li>The Rect's right is set to the total width of right decorations.</li>
8809          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
8810          * </ul>
8811          * <p>
8812          * Note that item decorations are automatically calculated when one of the LayoutManager's
8813          * measure child methods is called. If you need to measure the child with custom specs via
8814          * {@link View#measure(int, int)}, you can use this method to get decorations.
8815          *
8816          * @param child The child view whose decorations should be calculated
8817          * @param outRect The Rect to hold result values
8818          */
calculateItemDecorationsForChild(View child, Rect outRect)8819         public void calculateItemDecorationsForChild(View child, Rect outRect) {
8820             if (mRecyclerView == null) {
8821                 outRect.set(0, 0, 0, 0);
8822                 return;
8823             }
8824             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
8825             outRect.set(insets);
8826         }
8827 
8828         /**
8829          * Returns the total height of item decorations applied to child's top.
8830          * <p>
8831          * Note that this value is not updated until the View is measured or
8832          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8833          *
8834          * @param child Child to query
8835          * @return The total height of item decorations applied to the child's top.
8836          * @see #getDecoratedTop(View)
8837          * @see #calculateItemDecorationsForChild(View, Rect)
8838          */
getTopDecorationHeight(View child)8839         public int getTopDecorationHeight(View child) {
8840             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
8841         }
8842 
8843         /**
8844          * Returns the total height of item decorations applied to child's bottom.
8845          * <p>
8846          * Note that this value is not updated until the View is measured or
8847          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8848          *
8849          * @param child Child to query
8850          * @return The total height of item decorations applied to the child's bottom.
8851          * @see #getDecoratedBottom(View)
8852          * @see #calculateItemDecorationsForChild(View, Rect)
8853          */
getBottomDecorationHeight(View child)8854         public int getBottomDecorationHeight(View child) {
8855             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
8856         }
8857 
8858         /**
8859          * Returns the total width of item decorations applied to child's left.
8860          * <p>
8861          * Note that this value is not updated until the View is measured or
8862          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8863          *
8864          * @param child Child to query
8865          * @return The total width of item decorations applied to the child's left.
8866          * @see #getDecoratedLeft(View)
8867          * @see #calculateItemDecorationsForChild(View, Rect)
8868          */
getLeftDecorationWidth(View child)8869         public int getLeftDecorationWidth(View child) {
8870             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
8871         }
8872 
8873         /**
8874          * Returns the total width of item decorations applied to child's right.
8875          * <p>
8876          * Note that this value is not updated until the View is measured or
8877          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
8878          *
8879          * @param child Child to query
8880          * @return The total width of item decorations applied to the child's right.
8881          * @see #getDecoratedRight(View)
8882          * @see #calculateItemDecorationsForChild(View, Rect)
8883          */
getRightDecorationWidth(View child)8884         public int getRightDecorationWidth(View child) {
8885             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
8886         }
8887 
8888         /**
8889          * Called when searching for a focusable view in the given direction has failed
8890          * for the current content of the RecyclerView.
8891          *
8892          * <p>This is the LayoutManager's opportunity to populate views in the given direction
8893          * to fulfill the request if it can. The LayoutManager should attach and return
8894          * the view to be focused. The default implementation returns null.</p>
8895          *
8896          * @param focused   The currently focused view
8897          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8898          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8899          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8900          *                  or 0 for not applicable
8901          * @param recycler  The recycler to use for obtaining views for currently offscreen items
8902          * @param state     Transient state of RecyclerView
8903          * @return The chosen view to be focused
8904          */
8905         @Nullable
onFocusSearchFailed(View focused, int direction, Recycler recycler, State state)8906         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
8907                 State state) {
8908             return null;
8909         }
8910 
8911         /**
8912          * This method gives a LayoutManager an opportunity to intercept the initial focus search
8913          * before the default behavior of {@link FocusFinder} is used. If this method returns
8914          * null FocusFinder will attempt to find a focusable child view. If it fails
8915          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
8916          * will be called to give the LayoutManager an opportunity to add new views for items
8917          * that did not have attached views representing them. The LayoutManager should not add
8918          * or remove views from this method.
8919          *
8920          * @param focused The currently focused view
8921          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
8922          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
8923          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
8924          * @return A descendant view to focus or null to fall back to default behavior.
8925          *         The default implementation returns null.
8926          */
onInterceptFocusSearch(View focused, int direction)8927         public View onInterceptFocusSearch(View focused, int direction) {
8928             return null;
8929         }
8930 
8931         /**
8932          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
8933          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
8934          * android.graphics.Rect, boolean)} for more details.
8935          *
8936          * <p>The base implementation will attempt to perform a standard programmatic scroll
8937          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
8938          *
8939          * @param child The direct child making the request.
8940          * @param rect  The rectangle in the child's coordinates the child
8941          *              wishes to be on the screen.
8942          * @param immediate True to forbid animated or delayed scrolling,
8943          *                  false otherwise
8944          * @return Whether the group scrolled to handle the operation
8945          */
requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect, boolean immediate)8946         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
8947                 boolean immediate) {
8948             final int parentLeft = getPaddingLeft();
8949             final int parentTop = getPaddingTop();
8950             final int parentRight = getWidth() - getPaddingRight();
8951             final int parentBottom = getHeight() - getPaddingBottom();
8952             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
8953             final int childTop = child.getTop() + rect.top - child.getScrollY();
8954             final int childRight = childLeft + rect.width();
8955             final int childBottom = childTop + rect.height();
8956 
8957             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
8958             final int offScreenTop = Math.min(0, childTop - parentTop);
8959             final int offScreenRight = Math.max(0, childRight - parentRight);
8960             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
8961 
8962             // Favor the "start" layout direction over the end when bringing one side or the other
8963             // of a large rect into view. If we decide to bring in end because start is already
8964             // visible, limit the scroll such that start won't go out of bounds.
8965             final int dx;
8966             if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
8967                 dx = offScreenRight != 0 ? offScreenRight
8968                         : Math.max(offScreenLeft, childRight - parentRight);
8969             } else {
8970                 dx = offScreenLeft != 0 ? offScreenLeft
8971                         : Math.min(childLeft - parentLeft, offScreenRight);
8972             }
8973 
8974             // Favor bringing the top into view over the bottom. If top is already visible and
8975             // we should scroll to make bottom visible, make sure top does not go out of bounds.
8976             final int dy = offScreenTop != 0 ? offScreenTop
8977                     : Math.min(childTop - parentTop, offScreenBottom);
8978 
8979             if (dx != 0 || dy != 0) {
8980                 if (immediate) {
8981                     parent.scrollBy(dx, dy);
8982                 } else {
8983                     parent.smoothScrollBy(dx, dy);
8984                 }
8985                 return true;
8986             }
8987             return false;
8988         }
8989 
8990         /**
8991          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
8992          */
8993         @Deprecated
onRequestChildFocus(RecyclerView parent, View child, View focused)8994         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
8995             // eat the request if we are in the middle of a scroll or layout
8996             return isSmoothScrolling() || parent.isComputingLayout();
8997         }
8998 
8999         /**
9000          * Called when a descendant view of the RecyclerView requests focus.
9001          *
9002          * <p>A LayoutManager wishing to keep focused views aligned in a specific
9003          * portion of the view may implement that behavior in an override of this method.</p>
9004          *
9005          * <p>If the LayoutManager executes different behavior that should override the default
9006          * behavior of scrolling the focused child on screen instead of running alongside it,
9007          * this method should return true.</p>
9008          *
9009          * @param parent  The RecyclerView hosting this LayoutManager
9010          * @param state   Current state of RecyclerView
9011          * @param child   Direct child of the RecyclerView containing the newly focused view
9012          * @param focused The newly focused view. This may be the same view as child or it may be
9013          *                null
9014          * @return true if the default scroll behavior should be suppressed
9015          */
onRequestChildFocus(RecyclerView parent, State state, View child, View focused)9016         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
9017                 View focused) {
9018             return onRequestChildFocus(parent, child, focused);
9019         }
9020 
9021         /**
9022          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
9023          * The LayoutManager may use this opportunity to clear caches and configure state such
9024          * that it can relayout appropriately with the new data and potentially new view types.
9025          *
9026          * <p>The default implementation removes all currently attached views.</p>
9027          *
9028          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
9029          *                   adapter.
9030          * @param newAdapter The new adapter instance. Might be null if
9031          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
9032          */
onAdapterChanged(Adapter oldAdapter, Adapter newAdapter)9033         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
9034         }
9035 
9036         /**
9037          * Called to populate focusable views within the RecyclerView.
9038          *
9039          * <p>The LayoutManager implementation should return <code>true</code> if the default
9040          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
9041          * suppressed.</p>
9042          *
9043          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
9044          * to fall back to the default ViewGroup behavior.</p>
9045          *
9046          * @param recyclerView The RecyclerView hosting this LayoutManager
9047          * @param views List of output views. This method should add valid focusable views
9048          *              to this list.
9049          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
9050          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
9051          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
9052          * @param focusableMode The type of focusables to be added.
9053          *
9054          * @return true to suppress the default behavior, false to add default focusables after
9055          *         this method returns.
9056          *
9057          * @see #FOCUSABLES_ALL
9058          * @see #FOCUSABLES_TOUCH_MODE
9059          */
onAddFocusables(RecyclerView recyclerView, ArrayList<View> views, int direction, int focusableMode)9060         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
9061                 int direction, int focusableMode) {
9062             return false;
9063         }
9064 
9065         /**
9066          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
9067          * detailed information on what has actually changed.
9068          *
9069          * @param recyclerView
9070          */
onItemsChanged(RecyclerView recyclerView)9071         public void onItemsChanged(RecyclerView recyclerView) {
9072         }
9073 
9074         /**
9075          * Called when items have been added to the adapter. The LayoutManager may choose to
9076          * requestLayout if the inserted items would require refreshing the currently visible set
9077          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
9078          *
9079          * @param recyclerView
9080          * @param positionStart
9081          * @param itemCount
9082          */
onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)9083         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
9084         }
9085 
9086         /**
9087          * Called when items have been removed from the adapter.
9088          *
9089          * @param recyclerView
9090          * @param positionStart
9091          * @param itemCount
9092          */
onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)9093         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
9094         }
9095 
9096         /**
9097          * Called when items have been changed in the adapter.
9098          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
9099          * instead, then this callback will not be invoked.
9100          *
9101          * @param recyclerView
9102          * @param positionStart
9103          * @param itemCount
9104          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)9105         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
9106         }
9107 
9108         /**
9109          * Called when items have been changed in the adapter and with optional payload.
9110          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
9111          *
9112          * @param recyclerView
9113          * @param positionStart
9114          * @param itemCount
9115          * @param payload
9116          */
onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount, Object payload)9117         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
9118                 Object payload) {
9119             onItemsUpdated(recyclerView, positionStart, itemCount);
9120         }
9121 
9122         /**
9123          * Called when an item is moved withing the adapter.
9124          * <p>
9125          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
9126          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
9127          * is called.
9128          *
9129          * @param recyclerView
9130          * @param from
9131          * @param to
9132          * @param itemCount
9133          */
onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount)9134         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
9135 
9136         }
9137 
9138 
9139         /**
9140          * <p>Override this method if you want to support scroll bars.</p>
9141          *
9142          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
9143          *
9144          * <p>Default implementation returns 0.</p>
9145          *
9146          * @param state Current state of RecyclerView
9147          * @return The horizontal extent of the scrollbar's thumb
9148          * @see RecyclerView#computeHorizontalScrollExtent()
9149          */
computeHorizontalScrollExtent(State state)9150         public int computeHorizontalScrollExtent(State state) {
9151             return 0;
9152         }
9153 
9154         /**
9155          * <p>Override this method if you want to support scroll bars.</p>
9156          *
9157          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
9158          *
9159          * <p>Default implementation returns 0.</p>
9160          *
9161          * @param state Current State of RecyclerView where you can find total item count
9162          * @return The horizontal offset of the scrollbar's thumb
9163          * @see RecyclerView#computeHorizontalScrollOffset()
9164          */
computeHorizontalScrollOffset(State state)9165         public int computeHorizontalScrollOffset(State state) {
9166             return 0;
9167         }
9168 
9169         /**
9170          * <p>Override this method if you want to support scroll bars.</p>
9171          *
9172          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
9173          *
9174          * <p>Default implementation returns 0.</p>
9175          *
9176          * @param state Current State of RecyclerView where you can find total item count
9177          * @return The total horizontal range represented by the vertical scrollbar
9178          * @see RecyclerView#computeHorizontalScrollRange()
9179          */
computeHorizontalScrollRange(State state)9180         public int computeHorizontalScrollRange(State state) {
9181             return 0;
9182         }
9183 
9184         /**
9185          * <p>Override this method if you want to support scroll bars.</p>
9186          *
9187          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
9188          *
9189          * <p>Default implementation returns 0.</p>
9190          *
9191          * @param state Current state of RecyclerView
9192          * @return The vertical extent of the scrollbar's thumb
9193          * @see RecyclerView#computeVerticalScrollExtent()
9194          */
computeVerticalScrollExtent(State state)9195         public int computeVerticalScrollExtent(State state) {
9196             return 0;
9197         }
9198 
9199         /**
9200          * <p>Override this method if you want to support scroll bars.</p>
9201          *
9202          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
9203          *
9204          * <p>Default implementation returns 0.</p>
9205          *
9206          * @param state Current State of RecyclerView where you can find total item count
9207          * @return The vertical offset of the scrollbar's thumb
9208          * @see RecyclerView#computeVerticalScrollOffset()
9209          */
computeVerticalScrollOffset(State state)9210         public int computeVerticalScrollOffset(State state) {
9211             return 0;
9212         }
9213 
9214         /**
9215          * <p>Override this method if you want to support scroll bars.</p>
9216          *
9217          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
9218          *
9219          * <p>Default implementation returns 0.</p>
9220          *
9221          * @param state Current State of RecyclerView where you can find total item count
9222          * @return The total vertical range represented by the vertical scrollbar
9223          * @see RecyclerView#computeVerticalScrollRange()
9224          */
computeVerticalScrollRange(State state)9225         public int computeVerticalScrollRange(State state) {
9226             return 0;
9227         }
9228 
9229         /**
9230          * Measure the attached RecyclerView. Implementations must call
9231          * {@link #setMeasuredDimension(int, int)} before returning.
9232          *
9233          * <p>The default implementation will handle EXACTLY measurements and respect
9234          * the minimum width and height properties of the host RecyclerView if measured
9235          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
9236          * will consume all available space.</p>
9237          *
9238          * @param recycler Recycler
9239          * @param state Transient state of RecyclerView
9240          * @param widthSpec Width {@link android.view.View.MeasureSpec}
9241          * @param heightSpec Height {@link android.view.View.MeasureSpec}
9242          */
onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec)9243         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
9244             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
9245         }
9246 
9247         /**
9248          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
9249          * host RecyclerView.
9250          *
9251          * @param widthSize Measured width
9252          * @param heightSize Measured height
9253          */
setMeasuredDimension(int widthSize, int heightSize)9254         public void setMeasuredDimension(int widthSize, int heightSize) {
9255             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
9256         }
9257 
9258         /**
9259          * @return The host RecyclerView's {@link View#getMinimumWidth()}
9260          */
getMinimumWidth()9261         public int getMinimumWidth() {
9262             return mRecyclerView.getMinimumWidth();
9263         }
9264 
9265         /**
9266          * @return The host RecyclerView's {@link View#getMinimumHeight()}
9267          */
getMinimumHeight()9268         public int getMinimumHeight() {
9269             return mRecyclerView.getMinimumHeight();
9270         }
9271         /**
9272          * <p>Called when the LayoutManager should save its state. This is a good time to save your
9273          * scroll position, configuration and anything else that may be required to restore the same
9274          * layout state if the LayoutManager is recreated.</p>
9275          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
9276          * restore. This will let you share information between your LayoutManagers but it is also
9277          * your responsibility to make sure they use the same parcelable class.</p>
9278          *
9279          * @return Necessary information for LayoutManager to be able to restore its state
9280          */
onSaveInstanceState()9281         public Parcelable onSaveInstanceState() {
9282             return null;
9283         }
9284 
9285 
onRestoreInstanceState(Parcelable state)9286         public void onRestoreInstanceState(Parcelable state) {
9287 
9288         }
9289 
stopSmoothScroller()9290         void stopSmoothScroller() {
9291             if (mSmoothScroller != null) {
9292                 mSmoothScroller.stop();
9293             }
9294         }
9295 
onSmoothScrollerStopped(SmoothScroller smoothScroller)9296         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
9297             if (mSmoothScroller == smoothScroller) {
9298                 mSmoothScroller = null;
9299             }
9300         }
9301 
9302         /**
9303          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
9304          *
9305          * @param state The new scroll state for RecyclerView
9306          */
onScrollStateChanged(int state)9307         public void onScrollStateChanged(int state) {
9308         }
9309 
9310         /**
9311          * Removes all views and recycles them using the given recycler.
9312          * <p>
9313          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
9314          * <p>
9315          * If a View is marked as "ignored", it is not removed nor recycled.
9316          *
9317          * @param recycler Recycler to use to recycle children
9318          * @see #removeAndRecycleView(View, Recycler)
9319          * @see #removeAndRecycleViewAt(int, Recycler)
9320          * @see #ignoreView(View)
9321          */
removeAndRecycleAllViews(Recycler recycler)9322         public void removeAndRecycleAllViews(Recycler recycler) {
9323             for (int i = getChildCount() - 1; i >= 0; i--) {
9324                 final View view = getChildAt(i);
9325                 if (!getChildViewHolderInt(view).shouldIgnore()) {
9326                     removeAndRecycleViewAt(i, recycler);
9327                 }
9328             }
9329         }
9330 
9331         // called by accessibility delegate
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)9332         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
9333             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
9334         }
9335 
9336         /**
9337          * Called by the AccessibilityDelegate when the information about the current layout should
9338          * be populated.
9339          * <p>
9340          * Default implementation adds a {@link
9341          * android.view.accessibility.AccessibilityNodeInfo.CollectionInfo}.
9342          * <p>
9343          * You should override
9344          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9345          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
9346          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
9347          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
9348          * more accurate accessibility information.
9349          *
9350          * @param recycler The Recycler that can be used to convert view positions into adapter
9351          *                 positions
9352          * @param state    The current state of RecyclerView
9353          * @param info     The info that should be filled by the LayoutManager
9354          * @see View#onInitializeAccessibilityNodeInfo(
9355          *android.view.accessibility.AccessibilityNodeInfo)
9356          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9357          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9358          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
9359          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
9360          */
onInitializeAccessibilityNodeInfo(Recycler recycler, State state, AccessibilityNodeInfo info)9361         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
9362                 AccessibilityNodeInfo info) {
9363             if (mRecyclerView.canScrollVertically(-1)
9364                     || mRecyclerView.canScrollHorizontally(-1)) {
9365                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
9366                 info.setScrollable(true);
9367             }
9368             if (mRecyclerView.canScrollVertically(1)
9369                     || mRecyclerView.canScrollHorizontally(1)) {
9370                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
9371                 info.setScrollable(true);
9372             }
9373             final AccessibilityNodeInfo.CollectionInfo collectionInfo =
9374                     AccessibilityNodeInfo.CollectionInfo
9375                             .obtain(getRowCountForAccessibility(recycler, state),
9376                                     getColumnCountForAccessibility(recycler, state),
9377                                     isLayoutHierarchical(recycler, state),
9378                                     getSelectionModeForAccessibility(recycler, state));
9379             info.setCollectionInfo(collectionInfo);
9380         }
9381 
9382         // called by accessibility delegate
onInitializeAccessibilityEvent(AccessibilityEvent event)9383         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
9384             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
9385         }
9386 
9387         /**
9388          * Called by the accessibility delegate to initialize an accessibility event.
9389          * <p>
9390          * Default implementation adds item count and scroll information to the event.
9391          *
9392          * @param recycler The Recycler that can be used to convert view positions into adapter
9393          *                 positions
9394          * @param state    The current state of RecyclerView
9395          * @param event    The event instance to initialize
9396          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
9397          */
onInitializeAccessibilityEvent(Recycler recycler, State state, AccessibilityEvent event)9398         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
9399                 AccessibilityEvent event) {
9400             if (mRecyclerView == null || event == null) {
9401                 return;
9402             }
9403             event.setScrollable(mRecyclerView.canScrollVertically(1)
9404                     || mRecyclerView.canScrollVertically(-1)
9405                     || mRecyclerView.canScrollHorizontally(-1)
9406                     || mRecyclerView.canScrollHorizontally(1));
9407 
9408             if (mRecyclerView.mAdapter != null) {
9409                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
9410             }
9411         }
9412 
9413         // called by accessibility delegate
onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info)9414         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info) {
9415             final ViewHolder vh = getChildViewHolderInt(host);
9416             // avoid trying to create accessibility node info for removed children
9417             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
9418                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
9419                         mRecyclerView.mState, host, info);
9420             }
9421         }
9422 
9423         /**
9424          * Called by the AccessibilityDelegate when the accessibility information for a specific
9425          * item should be populated.
9426          * <p>
9427          * Default implementation adds basic positioning information about the item.
9428          *
9429          * @param recycler The Recycler that can be used to convert view positions into adapter
9430          *                 positions
9431          * @param state    The current state of RecyclerView
9432          * @param host     The child for which accessibility node info should be populated
9433          * @param info     The info to fill out about the item
9434          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
9435          * android.view.accessibility.AccessibilityNodeInfo)
9436          */
onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, View host, AccessibilityNodeInfo info)9437         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
9438                 View host, AccessibilityNodeInfo info) {
9439             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
9440             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
9441             final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
9442                     AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndexGuess, 1,
9443                             columnIndexGuess, 1, false, false);
9444             info.setCollectionItemInfo(itemInfo);
9445         }
9446 
9447         /**
9448          * A LayoutManager can call this method to force RecyclerView to run simple animations in
9449          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
9450          * change).
9451          * <p>
9452          * Note that, calling this method will not guarantee that RecyclerView will run animations
9453          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
9454          * not run any animations but will still clear this flag after the layout is complete.
9455          *
9456          */
requestSimpleAnimationsInNextLayout()9457         public void requestSimpleAnimationsInNextLayout() {
9458             mRequestedSimpleAnimations = true;
9459         }
9460 
9461         /**
9462          * Returns the selection mode for accessibility. Should be
9463          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE},
9464          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_SINGLE} or
9465          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_MULTIPLE}.
9466          * <p>
9467          * Default implementation returns
9468          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
9469          *
9470          * @param recycler The Recycler that can be used to convert view positions into adapter
9471          *                 positions
9472          * @param state    The current state of RecyclerView
9473          * @return Selection mode for accessibility. Default implementation returns
9474          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
9475          */
getSelectionModeForAccessibility(Recycler recycler, State state)9476         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
9477             return AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE;
9478         }
9479 
9480         /**
9481          * Returns the number of rows for accessibility.
9482          * <p>
9483          * Default implementation returns the number of items in the adapter if LayoutManager
9484          * supports vertical scrolling or 1 if LayoutManager does not support vertical
9485          * scrolling.
9486          *
9487          * @param recycler The Recycler that can be used to convert view positions into adapter
9488          *                 positions
9489          * @param state    The current state of RecyclerView
9490          * @return The number of rows in LayoutManager for accessibility.
9491          */
getRowCountForAccessibility(Recycler recycler, State state)9492         public int getRowCountForAccessibility(Recycler recycler, State state) {
9493             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9494                 return 1;
9495             }
9496             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
9497         }
9498 
9499         /**
9500          * Returns the number of columns for accessibility.
9501          * <p>
9502          * Default implementation returns the number of items in the adapter if LayoutManager
9503          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
9504          * scrolling.
9505          *
9506          * @param recycler The Recycler that can be used to convert view positions into adapter
9507          *                 positions
9508          * @param state    The current state of RecyclerView
9509          * @return The number of rows in LayoutManager for accessibility.
9510          */
getColumnCountForAccessibility(Recycler recycler, State state)9511         public int getColumnCountForAccessibility(Recycler recycler, State state) {
9512             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
9513                 return 1;
9514             }
9515             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
9516         }
9517 
9518         /**
9519          * Returns whether layout is hierarchical or not to be used for accessibility.
9520          * <p>
9521          * Default implementation returns false.
9522          *
9523          * @param recycler The Recycler that can be used to convert view positions into adapter
9524          *                 positions
9525          * @param state    The current state of RecyclerView
9526          * @return True if layout is hierarchical.
9527          */
isLayoutHierarchical(Recycler recycler, State state)9528         public boolean isLayoutHierarchical(Recycler recycler, State state) {
9529             return false;
9530         }
9531 
9532         // called by accessibility delegate
performAccessibilityAction(int action, Bundle args)9533         boolean performAccessibilityAction(int action, Bundle args) {
9534             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
9535                     action, args);
9536         }
9537 
9538         /**
9539          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
9540          *
9541          * @param recycler  The Recycler that can be used to convert view positions into adapter
9542          *                  positions
9543          * @param state     The current state of RecyclerView
9544          * @param action    The action to perform
9545          * @param args      Optional action arguments
9546          * @see View#performAccessibilityAction(int, android.os.Bundle)
9547          */
performAccessibilityAction(Recycler recycler, State state, int action, Bundle args)9548         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
9549                 Bundle args) {
9550             if (mRecyclerView == null) {
9551                 return false;
9552             }
9553             int vScroll = 0, hScroll = 0;
9554             switch (action) {
9555                 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
9556                     if (mRecyclerView.canScrollVertically(-1)) {
9557                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
9558                     }
9559                     if (mRecyclerView.canScrollHorizontally(-1)) {
9560                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
9561                     }
9562                     break;
9563                 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
9564                     if (mRecyclerView.canScrollVertically(1)) {
9565                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
9566                     }
9567                     if (mRecyclerView.canScrollHorizontally(1)) {
9568                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
9569                     }
9570                     break;
9571             }
9572             if (vScroll == 0 && hScroll == 0) {
9573                 return false;
9574             }
9575             mRecyclerView.smoothScrollBy(hScroll, vScroll);
9576             return true;
9577         }
9578 
9579         // called by accessibility delegate
performAccessibilityActionForItem(View view, int action, Bundle args)9580         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
9581             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
9582                     view, action, args);
9583         }
9584 
9585         /**
9586          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
9587          * children of LayoutManager.
9588          * <p>
9589          * Default implementation does not do anything.
9590          *
9591          * @param recycler The Recycler that can be used to convert view positions into adapter
9592          *                 positions
9593          * @param state    The current state of RecyclerView
9594          * @param view     The child view on which the action is performed
9595          * @param action   The action to perform
9596          * @param args     Optional action arguments
9597          * @return true if action is handled
9598          * @see View#performAccessibilityAction(int, android.os.Bundle)
9599          */
performAccessibilityActionForItem(Recycler recycler, State state, View view, int action, Bundle args)9600         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
9601                 int action, Bundle args) {
9602             return false;
9603         }
9604 
9605         /**
9606          * Parse the xml attributes to get the most common properties used by layout managers.
9607          *
9608          * @return an object containing the properties as specified in the attrs.
9609          */
getProperties(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)9610         public static Properties getProperties(Context context, AttributeSet attrs,
9611                 int defStyleAttr, int defStyleRes) {
9612             Properties properties = new Properties();
9613             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
9614                     defStyleAttr, defStyleRes);
9615             properties.orientation = a.getInt(R.styleable.RecyclerView_orientation, VERTICAL);
9616             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
9617             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
9618             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
9619             a.recycle();
9620             return properties;
9621         }
9622 
setExactMeasureSpecsFrom(RecyclerView recyclerView)9623         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
9624             setMeasureSpecs(
9625                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
9626                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
9627             );
9628         }
9629 
9630         /**
9631          * Internal API to allow LayoutManagers to be measured twice.
9632          * <p>
9633          * This is not public because LayoutManagers should be able to handle their layouts in one
9634          * pass but it is very convenient to make existing LayoutManagers support wrapping content
9635          * when both orientations are undefined.
9636          * <p>
9637          * This API will be removed after default LayoutManagers properly implement wrap content in
9638          * non-scroll orientation.
9639          */
shouldMeasureTwice()9640         boolean shouldMeasureTwice() {
9641             return false;
9642         }
9643 
hasFlexibleChildInBothOrientations()9644         boolean hasFlexibleChildInBothOrientations() {
9645             final int childCount = getChildCount();
9646             for (int i = 0; i < childCount; i++) {
9647                 final View child = getChildAt(i);
9648                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
9649                 if (lp.width < 0 && lp.height < 0) {
9650                     return true;
9651                 }
9652             }
9653             return false;
9654         }
9655 
9656         /**
9657          * Some general properties that a LayoutManager may want to use.
9658          */
9659         public static class Properties {
9660             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
9661             public int orientation;
9662             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
9663             public int spanCount;
9664             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
9665             public boolean reverseLayout;
9666             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
9667             public boolean stackFromEnd;
9668         }
9669     }
9670 
9671     /**
9672      * An ItemDecoration allows the application to add a special drawing and layout offset
9673      * to specific item views from the adapter's data set. This can be useful for drawing dividers
9674      * between items, highlights, visual grouping boundaries and more.
9675      *
9676      * <p>All ItemDecorations are drawn in the order they were added, before the item
9677      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
9678      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
9679      * RecyclerView.State)}.</p>
9680      */
9681     public abstract static class ItemDecoration {
9682         /**
9683          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9684          * Any content drawn by this method will be drawn before the item views are drawn,
9685          * and will thus appear underneath the views.
9686          *
9687          * @param c Canvas to draw into
9688          * @param parent RecyclerView this ItemDecoration is drawing into
9689          * @param state The current state of RecyclerView
9690          */
onDraw(Canvas c, RecyclerView parent, State state)9691         public void onDraw(Canvas c, RecyclerView parent, State state) {
9692             onDraw(c, parent);
9693         }
9694 
9695         /**
9696          * @deprecated
9697          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
9698          */
9699         @Deprecated
onDraw(Canvas c, RecyclerView parent)9700         public void onDraw(Canvas c, RecyclerView parent) {
9701         }
9702 
9703         /**
9704          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
9705          * Any content drawn by this method will be drawn after the item views are drawn
9706          * and will thus appear over the views.
9707          *
9708          * @param c Canvas to draw into
9709          * @param parent RecyclerView this ItemDecoration is drawing into
9710          * @param state The current state of RecyclerView.
9711          */
onDrawOver(Canvas c, RecyclerView parent, State state)9712         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
9713             onDrawOver(c, parent);
9714         }
9715 
9716         /**
9717          * @deprecated
9718          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
9719          */
9720         @Deprecated
onDrawOver(Canvas c, RecyclerView parent)9721         public void onDrawOver(Canvas c, RecyclerView parent) {
9722         }
9723 
9724 
9725         /**
9726          * @deprecated
9727          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
9728          */
9729         @Deprecated
getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)9730         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
9731             outRect.set(0, 0, 0, 0);
9732         }
9733 
9734         /**
9735          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
9736          * the number of pixels that the item view should be inset by, similar to padding or margin.
9737          * The default implementation sets the bounds of outRect to 0 and returns.
9738          *
9739          * <p>
9740          * If this ItemDecoration does not affect the positioning of item views, it should set
9741          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
9742          * before returning.
9743          *
9744          * <p>
9745          * If you need to access Adapter for additional data, you can call
9746          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
9747          * View.
9748          *
9749          * @param outRect Rect to receive the output.
9750          * @param view    The child view to decorate
9751          * @param parent  RecyclerView this ItemDecoration is decorating
9752          * @param state   The current state of RecyclerView.
9753          */
getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)9754         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
9755             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
9756                     parent);
9757         }
9758     }
9759 
9760     /**
9761      * An OnItemTouchListener allows the application to intercept touch events in progress at the
9762      * view hierarchy level of the RecyclerView before those touch events are considered for
9763      * RecyclerView's own scrolling behavior.
9764      *
9765      * <p>This can be useful for applications that wish to implement various forms of gestural
9766      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
9767      * a touch interaction already in progress even if the RecyclerView is already handling that
9768      * gesture stream itself for the purposes of scrolling.</p>
9769      *
9770      * @see SimpleOnItemTouchListener
9771      */
9772     public interface OnItemTouchListener {
9773         /**
9774          * Silently observe and/or take over touch events sent to the RecyclerView
9775          * before they are handled by either the RecyclerView itself or its child views.
9776          *
9777          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
9778          * in the order in which each listener was added, before any other touch processing
9779          * by the RecyclerView itself or child views occurs.</p>
9780          *
9781          * @param e MotionEvent describing the touch event. All coordinates are in
9782          *          the RecyclerView's coordinate system.
9783          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
9784          *         to continue with the current behavior and continue observing future events in
9785          *         the gesture.
9786          */
9787         boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
9788 
9789         /**
9790          * Process a touch event as part of a gesture that was claimed by returning true from
9791          * a previous call to {@link #onInterceptTouchEvent}.
9792          *
9793          * @param e MotionEvent describing the touch event. All coordinates are in
9794          *          the RecyclerView's coordinate system.
9795          */
9796         void onTouchEvent(RecyclerView rv, MotionEvent e);
9797 
9798         /**
9799          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
9800          * intercept touch events with
9801          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
9802          *
9803          * @param disallowIntercept True if the child does not want the parent to
9804          *            intercept touch events.
9805          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
9806          */
9807         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
9808     }
9809 
9810     /**
9811      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
9812      * and default return values.
9813      * <p>
9814      * You may prefer to extend this class if you don't need to override all methods. Another
9815      * benefit of using this class is future compatibility. As the interface may change, we'll
9816      * always provide a default implementation on this class so that your code won't break when
9817      * you update to a new version of the support library.
9818      */
9819     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
9820         @Override
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)9821         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
9822             return false;
9823         }
9824 
9825         @Override
onTouchEvent(RecyclerView rv, MotionEvent e)9826         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
9827         }
9828 
9829         @Override
onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)9830         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
9831         }
9832     }
9833 
9834 
9835     /**
9836      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
9837      * has occurred on that RecyclerView.
9838      * <p>
9839      * @see RecyclerView#addOnScrollListener(OnScrollListener)
9840      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
9841      *
9842      */
9843     public abstract static class OnScrollListener {
9844         /**
9845          * Callback method to be invoked when RecyclerView's scroll state changes.
9846          *
9847          * @param recyclerView The RecyclerView whose scroll state has changed.
9848          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
9849          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
9850          */
onScrollStateChanged(RecyclerView recyclerView, int newState)9851         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
9852 
9853         /**
9854          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
9855          * called after the scroll has completed.
9856          * <p>
9857          * This callback will also be called if visible item range changes after a layout
9858          * calculation. In that case, dx and dy will be 0.
9859          *
9860          * @param recyclerView The RecyclerView which scrolled.
9861          * @param dx The amount of horizontal scroll.
9862          * @param dy The amount of vertical scroll.
9863          */
onScrolled(RecyclerView recyclerView, int dx, int dy)9864         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
9865     }
9866 
9867     /**
9868      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
9869      * a view is recycled.
9870      *
9871      * @see RecyclerView#setRecyclerListener(RecyclerListener)
9872      */
9873     public interface RecyclerListener {
9874 
9875         /**
9876          * This method is called whenever the view in the ViewHolder is recycled.
9877          *
9878          * RecyclerView calls this method right before clearing ViewHolder's internal data and
9879          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
9880          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
9881          * its adapter position.
9882          *
9883          * @param holder The ViewHolder containing the view that was recycled
9884          */
9885         void onViewRecycled(ViewHolder holder);
9886     }
9887 
9888     /**
9889      * A Listener interface that can be attached to a RecylcerView to get notified
9890      * whenever a ViewHolder is attached to or detached from RecyclerView.
9891      */
9892     public interface OnChildAttachStateChangeListener {
9893 
9894         /**
9895          * Called when a view is attached to the RecyclerView.
9896          *
9897          * @param view The View which is attached to the RecyclerView
9898          */
9899         void onChildViewAttachedToWindow(View view);
9900 
9901         /**
9902          * Called when a view is detached from RecyclerView.
9903          *
9904          * @param view The View which is being detached from the RecyclerView
9905          */
9906         void onChildViewDetachedFromWindow(View view);
9907     }
9908 
9909     /**
9910      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
9911      *
9912      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
9913      * potentially expensive {@link View#findViewById(int)} results.</p>
9914      *
9915      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
9916      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
9917      * their own custom ViewHolder implementations to store data that makes binding view contents
9918      * easier. Implementations should assume that individual item views will hold strong references
9919      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
9920      * strong references to extra off-screen item views for caching purposes</p>
9921      */
9922     public abstract static class ViewHolder {
9923         public final View itemView;
9924         WeakReference<RecyclerView> mNestedRecyclerView;
9925         int mPosition = NO_POSITION;
9926         int mOldPosition = NO_POSITION;
9927         long mItemId = NO_ID;
9928         int mItemViewType = INVALID_TYPE;
9929         int mPreLayoutPosition = NO_POSITION;
9930 
9931         // The item that this holder is shadowing during an item change event/animation
9932         ViewHolder mShadowedHolder = null;
9933         // The item that is shadowing this holder during an item change event/animation
9934         ViewHolder mShadowingHolder = null;
9935 
9936         /**
9937          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
9938          * are all valid.
9939          */
9940         static final int FLAG_BOUND = 1 << 0;
9941 
9942         /**
9943          * The data this ViewHolder's view reflects is stale and needs to be rebound
9944          * by the adapter. mPosition and mItemId are consistent.
9945          */
9946         static final int FLAG_UPDATE = 1 << 1;
9947 
9948         /**
9949          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
9950          * are not to be trusted and may no longer match the item view type.
9951          * This ViewHolder must be fully rebound to different data.
9952          */
9953         static final int FLAG_INVALID = 1 << 2;
9954 
9955         /**
9956          * This ViewHolder points at data that represents an item previously removed from the
9957          * data set. Its view may still be used for things like outgoing animations.
9958          */
9959         static final int FLAG_REMOVED = 1 << 3;
9960 
9961         /**
9962          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
9963          * and is intended to keep views around during animations.
9964          */
9965         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
9966 
9967         /**
9968          * This ViewHolder is returned from scrap which means we are expecting an addView call
9969          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
9970          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
9971          * the RecyclerView.
9972          */
9973         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
9974 
9975         /**
9976          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
9977          * it unless LayoutManager is replaced.
9978          * It is still fully visible to the LayoutManager.
9979          */
9980         static final int FLAG_IGNORE = 1 << 7;
9981 
9982         /**
9983          * When the View is detached form the parent, we set this flag so that we can take correct
9984          * action when we need to remove it or add it back.
9985          */
9986         static final int FLAG_TMP_DETACHED = 1 << 8;
9987 
9988         /**
9989          * Set when we can no longer determine the adapter position of this ViewHolder until it is
9990          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
9991          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
9992          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
9993          * re-calculated.
9994          */
9995         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
9996 
9997         /**
9998          * Set when a addChangePayload(null) is called
9999          */
10000         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
10001 
10002         /**
10003          * Used by ItemAnimator when a ViewHolder's position changes
10004          */
10005         static final int FLAG_MOVED = 1 << 11;
10006 
10007         /**
10008          * Used by ItemAnimator when a ViewHolder appears in pre-layout
10009          */
10010         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
10011 
10012         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
10013 
10014         /**
10015          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
10016          * hidden list (as if it was scrap) without being recycled in between.
10017          *
10018          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
10019          *   a) Animation ends, view is recycled and used from the recycle pool.
10020          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
10021          *
10022          * This flag is used to represent "case b" where the ViewHolder is reused without being
10023          * recycled (thus "bounced" from the hidden list). This state requires special handling
10024          * because the ViewHolder must be added to pre layout maps for animations as if it was
10025          * already there.
10026          */
10027         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
10028 
10029         private int mFlags;
10030 
10031         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
10032 
10033         List<Object> mPayloads = null;
10034         List<Object> mUnmodifiedPayloads = null;
10035 
10036         private int mIsRecyclableCount = 0;
10037 
10038         // If non-null, view is currently considered scrap and may be reused for other data by the
10039         // scrap container.
10040         private Recycler mScrapContainer = null;
10041         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
10042         private boolean mInChangeScrap = false;
10043 
10044         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
10045         // marked as unimportant for accessibility.
10046         private int mWasImportantForAccessibilityBeforeHidden =
10047                 View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10048         // set if we defer the accessibility state change of the view holder
10049         @VisibleForTesting
10050         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10051 
10052         /**
10053          * Is set when VH is bound from the adapter and cleaned right before it is sent to
10054          * {@link RecycledViewPool}.
10055          */
10056         RecyclerView mOwnerRecyclerView;
10057 
ViewHolder(View itemView)10058         public ViewHolder(View itemView) {
10059             if (itemView == null) {
10060                 throw new IllegalArgumentException("itemView may not be null");
10061             }
10062             this.itemView = itemView;
10063         }
10064 
flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout)10065         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
10066             addFlags(ViewHolder.FLAG_REMOVED);
10067             offsetPosition(offset, applyToPreLayout);
10068             mPosition = mNewPosition;
10069         }
10070 
offsetPosition(int offset, boolean applyToPreLayout)10071         void offsetPosition(int offset, boolean applyToPreLayout) {
10072             if (mOldPosition == NO_POSITION) {
10073                 mOldPosition = mPosition;
10074             }
10075             if (mPreLayoutPosition == NO_POSITION) {
10076                 mPreLayoutPosition = mPosition;
10077             }
10078             if (applyToPreLayout) {
10079                 mPreLayoutPosition += offset;
10080             }
10081             mPosition += offset;
10082             if (itemView.getLayoutParams() != null) {
10083                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
10084             }
10085         }
10086 
clearOldPosition()10087         void clearOldPosition() {
10088             mOldPosition = NO_POSITION;
10089             mPreLayoutPosition = NO_POSITION;
10090         }
10091 
saveOldPosition()10092         void saveOldPosition() {
10093             if (mOldPosition == NO_POSITION) {
10094                 mOldPosition = mPosition;
10095             }
10096         }
10097 
shouldIgnore()10098         boolean shouldIgnore() {
10099             return (mFlags & FLAG_IGNORE) != 0;
10100         }
10101 
10102         /**
10103          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
10104          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
10105          * {@link #getAdapterPosition()} depending on your use case.
10106          *
10107          * @see #getLayoutPosition()
10108          * @see #getAdapterPosition()
10109          */
10110         @Deprecated
getPosition()10111         public final int getPosition() {
10112             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10113         }
10114 
10115         /**
10116          * Returns the position of the ViewHolder in terms of the latest layout pass.
10117          * <p>
10118          * This position is mostly used by RecyclerView components to be consistent while
10119          * RecyclerView lazily processes adapter updates.
10120          * <p>
10121          * For performance and animation reasons, RecyclerView batches all adapter updates until the
10122          * next layout pass. This may cause mismatches between the Adapter position of the item and
10123          * the position it had in the latest layout calculations.
10124          * <p>
10125          * LayoutManagers should always call this method while doing calculations based on item
10126          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
10127          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
10128          * of the item.
10129          * <p>
10130          * If LayoutManager needs to call an external method that requires the adapter position of
10131          * the item, it can use {@link #getAdapterPosition()} or
10132          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
10133          *
10134          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
10135          * @see #getAdapterPosition()
10136          */
getLayoutPosition()10137         public final int getLayoutPosition() {
10138             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
10139         }
10140 
10141         /**
10142          * Returns the Adapter position of the item represented by this ViewHolder.
10143          * <p>
10144          * Note that this might be different than the {@link #getLayoutPosition()} if there are
10145          * pending adapter updates but a new layout pass has not happened yet.
10146          * <p>
10147          * RecyclerView does not handle any adapter updates until the next layout traversal. This
10148          * may create temporary inconsistencies between what user sees on the screen and what
10149          * adapter contents have. This inconsistency is not important since it will be less than
10150          * 16ms but it might be a problem if you want to use ViewHolder position to access the
10151          * adapter. Sometimes, you may need to get the exact adapter position to do
10152          * some actions in response to user events. In that case, you should use this method which
10153          * will calculate the Adapter position of the ViewHolder.
10154          * <p>
10155          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
10156          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
10157          *
10158          * @return The adapter position of the item if it still exists in the adapter.
10159          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
10160          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
10161          * layout pass or the ViewHolder has already been recycled.
10162          */
getAdapterPosition()10163         public final int getAdapterPosition() {
10164             if (mOwnerRecyclerView == null) {
10165                 return NO_POSITION;
10166             }
10167             return mOwnerRecyclerView.getAdapterPositionFor(this);
10168         }
10169 
10170         /**
10171          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
10172          * to perform animations.
10173          * <p>
10174          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
10175          * adapter index in the previous layout.
10176          *
10177          * @return The previous adapter index of the Item represented by this ViewHolder or
10178          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
10179          * complete).
10180          */
getOldPosition()10181         public final int getOldPosition() {
10182             return mOldPosition;
10183         }
10184 
10185         /**
10186          * Returns The itemId represented by this ViewHolder.
10187          *
10188          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
10189          * otherwise
10190          */
getItemId()10191         public final long getItemId() {
10192             return mItemId;
10193         }
10194 
10195         /**
10196          * @return The view type of this ViewHolder.
10197          */
getItemViewType()10198         public final int getItemViewType() {
10199             return mItemViewType;
10200         }
10201 
isScrap()10202         boolean isScrap() {
10203             return mScrapContainer != null;
10204         }
10205 
unScrap()10206         void unScrap() {
10207             mScrapContainer.unscrapView(this);
10208         }
10209 
wasReturnedFromScrap()10210         boolean wasReturnedFromScrap() {
10211             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
10212         }
10213 
clearReturnedFromScrapFlag()10214         void clearReturnedFromScrapFlag() {
10215             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
10216         }
10217 
clearTmpDetachFlag()10218         void clearTmpDetachFlag() {
10219             mFlags = mFlags & ~FLAG_TMP_DETACHED;
10220         }
10221 
stopIgnoring()10222         void stopIgnoring() {
10223             mFlags = mFlags & ~FLAG_IGNORE;
10224         }
10225 
setScrapContainer(Recycler recycler, boolean isChangeScrap)10226         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
10227             mScrapContainer = recycler;
10228             mInChangeScrap = isChangeScrap;
10229         }
10230 
isInvalid()10231         boolean isInvalid() {
10232             return (mFlags & FLAG_INVALID) != 0;
10233         }
10234 
needsUpdate()10235         boolean needsUpdate() {
10236             return (mFlags & FLAG_UPDATE) != 0;
10237         }
10238 
isBound()10239         boolean isBound() {
10240             return (mFlags & FLAG_BOUND) != 0;
10241         }
10242 
isRemoved()10243         boolean isRemoved() {
10244             return (mFlags & FLAG_REMOVED) != 0;
10245         }
10246 
hasAnyOfTheFlags(int flags)10247         boolean hasAnyOfTheFlags(int flags) {
10248             return (mFlags & flags) != 0;
10249         }
10250 
isTmpDetached()10251         boolean isTmpDetached() {
10252             return (mFlags & FLAG_TMP_DETACHED) != 0;
10253         }
10254 
isAdapterPositionUnknown()10255         boolean isAdapterPositionUnknown() {
10256             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
10257         }
10258 
setFlags(int flags, int mask)10259         void setFlags(int flags, int mask) {
10260             mFlags = (mFlags & ~mask) | (flags & mask);
10261         }
10262 
addFlags(int flags)10263         void addFlags(int flags) {
10264             mFlags |= flags;
10265         }
10266 
addChangePayload(Object payload)10267         void addChangePayload(Object payload) {
10268             if (payload == null) {
10269                 addFlags(FLAG_ADAPTER_FULLUPDATE);
10270             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10271                 createPayloadsIfNeeded();
10272                 mPayloads.add(payload);
10273             }
10274         }
10275 
createPayloadsIfNeeded()10276         private void createPayloadsIfNeeded() {
10277             if (mPayloads == null) {
10278                 mPayloads = new ArrayList<Object>();
10279                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
10280             }
10281         }
10282 
clearPayload()10283         void clearPayload() {
10284             if (mPayloads != null) {
10285                 mPayloads.clear();
10286             }
10287             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
10288         }
10289 
getUnmodifiedPayloads()10290         List<Object> getUnmodifiedPayloads() {
10291             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
10292                 if (mPayloads == null || mPayloads.size() == 0) {
10293                     // Initial state,  no update being called.
10294                     return FULLUPDATE_PAYLOADS;
10295                 }
10296                 // there are none-null payloads
10297                 return mUnmodifiedPayloads;
10298             } else {
10299                 // a full update has been called.
10300                 return FULLUPDATE_PAYLOADS;
10301             }
10302         }
10303 
resetInternal()10304         void resetInternal() {
10305             mFlags = 0;
10306             mPosition = NO_POSITION;
10307             mOldPosition = NO_POSITION;
10308             mItemId = NO_ID;
10309             mPreLayoutPosition = NO_POSITION;
10310             mIsRecyclableCount = 0;
10311             mShadowedHolder = null;
10312             mShadowingHolder = null;
10313             clearPayload();
10314             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10315             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
10316             clearNestedRecyclerViewIfNotNested(this);
10317         }
10318 
10319         /**
10320          * Called when the child view enters the hidden state
10321          */
onEnteredHiddenState(RecyclerView parent)10322         private void onEnteredHiddenState(RecyclerView parent) {
10323             // While the view item is in hidden state, make it invisible for the accessibility.
10324             mWasImportantForAccessibilityBeforeHidden =
10325                     itemView.getImportantForAccessibility();
10326             parent.setChildImportantForAccessibilityInternal(this,
10327                     View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
10328         }
10329 
10330         /**
10331          * Called when the child view leaves the hidden state
10332          */
onLeftHiddenState(RecyclerView parent)10333         private void onLeftHiddenState(RecyclerView parent) {
10334             parent.setChildImportantForAccessibilityInternal(this,
10335                     mWasImportantForAccessibilityBeforeHidden);
10336             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
10337         }
10338 
10339         @Override
toString()10340         public String toString() {
10341             final StringBuilder sb = new StringBuilder("ViewHolder{"
10342                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
10343                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
10344             if (isScrap()) {
10345                 sb.append(" scrap ")
10346                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
10347             }
10348             if (isInvalid()) sb.append(" invalid");
10349             if (!isBound()) sb.append(" unbound");
10350             if (needsUpdate()) sb.append(" update");
10351             if (isRemoved()) sb.append(" removed");
10352             if (shouldIgnore()) sb.append(" ignored");
10353             if (isTmpDetached()) sb.append(" tmpDetached");
10354             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
10355             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
10356 
10357             if (itemView.getParent() == null) sb.append(" no parent");
10358             sb.append("}");
10359             return sb.toString();
10360         }
10361 
10362         /**
10363          * Informs the recycler whether this item can be recycled. Views which are not
10364          * recyclable will not be reused for other items until setIsRecyclable() is
10365          * later set to true. Calls to setIsRecyclable() should always be paired (one
10366          * call to setIsRecyclabe(false) should always be matched with a later call to
10367          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
10368          * reference-counted.
10369          *
10370          * @param recyclable Whether this item is available to be recycled. Default value
10371          * is true.
10372          *
10373          * @see #isRecyclable()
10374          */
setIsRecyclable(boolean recyclable)10375         public final void setIsRecyclable(boolean recyclable) {
10376             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
10377             if (mIsRecyclableCount < 0) {
10378                 mIsRecyclableCount = 0;
10379                 if (DEBUG) {
10380                     throw new RuntimeException("isRecyclable decremented below 0: "
10381                             + "unmatched pair of setIsRecyable() calls for " + this);
10382                 }
10383                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
10384                         + "unmatched pair of setIsRecyable() calls for " + this);
10385             } else if (!recyclable && mIsRecyclableCount == 1) {
10386                 mFlags |= FLAG_NOT_RECYCLABLE;
10387             } else if (recyclable && mIsRecyclableCount == 0) {
10388                 mFlags &= ~FLAG_NOT_RECYCLABLE;
10389             }
10390             if (DEBUG) {
10391                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
10392             }
10393         }
10394 
10395         /**
10396          * @return true if this item is available to be recycled, false otherwise.
10397          *
10398          * @see #setIsRecyclable(boolean)
10399          */
isRecyclable()10400         public final boolean isRecyclable() {
10401             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
10402                     && !itemView.hasTransientState();
10403         }
10404 
10405         /**
10406          * Returns whether we have animations referring to this view holder or not.
10407          * This is similar to isRecyclable flag but does not check transient state.
10408          */
shouldBeKeptAsChild()10409         private boolean shouldBeKeptAsChild() {
10410             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
10411         }
10412 
10413         /**
10414          * @return True if ViewHolder is not referenced by RecyclerView animations but has
10415          * transient state which will prevent it from being recycled.
10416          */
doesTransientStatePreventRecycling()10417         private boolean doesTransientStatePreventRecycling() {
10418             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && itemView.hasTransientState();
10419         }
10420 
isUpdated()10421         boolean isUpdated() {
10422             return (mFlags & FLAG_UPDATE) != 0;
10423         }
10424     }
10425 
10426     /**
10427      * This method is here so that we can control the important for a11y changes and test it.
10428      */
10429     @VisibleForTesting
setChildImportantForAccessibilityInternal(ViewHolder viewHolder, int importantForAccessibility)10430     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
10431             int importantForAccessibility) {
10432         if (isComputingLayout()) {
10433             viewHolder.mPendingAccessibilityState = importantForAccessibility;
10434             mPendingAccessibilityImportanceChange.add(viewHolder);
10435             return false;
10436         }
10437         viewHolder.itemView.setImportantForAccessibility(importantForAccessibility);
10438         return true;
10439     }
10440 
dispatchPendingImportantForAccessibilityChanges()10441     void dispatchPendingImportantForAccessibilityChanges() {
10442         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
10443             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
10444             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
10445                 continue;
10446             }
10447             int state = viewHolder.mPendingAccessibilityState;
10448             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
10449                 //noinspection WrongConstant
10450                 viewHolder.itemView.setImportantForAccessibility(state);
10451                 viewHolder.mPendingAccessibilityState =
10452                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
10453             }
10454         }
10455         mPendingAccessibilityImportanceChange.clear();
10456     }
10457 
getAdapterPositionFor(ViewHolder viewHolder)10458     int getAdapterPositionFor(ViewHolder viewHolder) {
10459         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
10460                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
10461                 || !viewHolder.isBound()) {
10462             return RecyclerView.NO_POSITION;
10463         }
10464         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
10465     }
10466 
10467     /**
10468      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
10469      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
10470      * to create their own subclass of this <code>LayoutParams</code> class
10471      * to store any additional required per-child view metadata about the layout.
10472      */
10473     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
10474         ViewHolder mViewHolder;
10475         final Rect mDecorInsets = new Rect();
10476         boolean mInsetsDirty = true;
10477         // Flag is set to true if the view is bound while it is detached from RV.
10478         // In this case, we need to manually call invalidate after view is added to guarantee that
10479         // invalidation is populated through the View hierarchy
10480         boolean mPendingInvalidate = false;
10481 
LayoutParams(Context c, AttributeSet attrs)10482         public LayoutParams(Context c, AttributeSet attrs) {
10483             super(c, attrs);
10484         }
10485 
LayoutParams(int width, int height)10486         public LayoutParams(int width, int height) {
10487             super(width, height);
10488         }
10489 
LayoutParams(MarginLayoutParams source)10490         public LayoutParams(MarginLayoutParams source) {
10491             super(source);
10492         }
10493 
LayoutParams(ViewGroup.LayoutParams source)10494         public LayoutParams(ViewGroup.LayoutParams source) {
10495             super(source);
10496         }
10497 
LayoutParams(LayoutParams source)10498         public LayoutParams(LayoutParams source) {
10499             super((ViewGroup.LayoutParams) source);
10500         }
10501 
10502         /**
10503          * Returns true if the view this LayoutParams is attached to needs to have its content
10504          * updated from the corresponding adapter.
10505          *
10506          * @return true if the view should have its content updated
10507          */
viewNeedsUpdate()10508         public boolean viewNeedsUpdate() {
10509             return mViewHolder.needsUpdate();
10510         }
10511 
10512         /**
10513          * Returns true if the view this LayoutParams is attached to is now representing
10514          * potentially invalid data. A LayoutManager should scrap/recycle it.
10515          *
10516          * @return true if the view is invalid
10517          */
isViewInvalid()10518         public boolean isViewInvalid() {
10519             return mViewHolder.isInvalid();
10520         }
10521 
10522         /**
10523          * Returns true if the adapter data item corresponding to the view this LayoutParams
10524          * is attached to has been removed from the data set. A LayoutManager may choose to
10525          * treat it differently in order to animate its outgoing or disappearing state.
10526          *
10527          * @return true if the item the view corresponds to was removed from the data set
10528          */
isItemRemoved()10529         public boolean isItemRemoved() {
10530             return mViewHolder.isRemoved();
10531         }
10532 
10533         /**
10534          * Returns true if the adapter data item corresponding to the view this LayoutParams
10535          * is attached to has been changed in the data set. A LayoutManager may choose to
10536          * treat it differently in order to animate its changing state.
10537          *
10538          * @return true if the item the view corresponds to was changed in the data set
10539          */
isItemChanged()10540         public boolean isItemChanged() {
10541             return mViewHolder.isUpdated();
10542         }
10543 
10544         /**
10545          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
10546          */
10547         @Deprecated
getViewPosition()10548         public int getViewPosition() {
10549             return mViewHolder.getPosition();
10550         }
10551 
10552         /**
10553          * Returns the adapter position that the view this LayoutParams is attached to corresponds
10554          * to as of latest layout calculation.
10555          *
10556          * @return the adapter position this view as of latest layout pass
10557          */
getViewLayoutPosition()10558         public int getViewLayoutPosition() {
10559             return mViewHolder.getLayoutPosition();
10560         }
10561 
10562         /**
10563          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
10564          * corresponds to.
10565          *
10566          * @return the up-to-date adapter position this view. It may return
10567          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
10568          * its up-to-date position cannot be calculated.
10569          */
getViewAdapterPosition()10570         public int getViewAdapterPosition() {
10571             return mViewHolder.getAdapterPosition();
10572         }
10573     }
10574 
10575     /**
10576      * Observer base class for watching changes to an {@link Adapter}.
10577      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
10578      */
10579     public abstract static class AdapterDataObserver {
onChanged()10580         public void onChanged() {
10581             // Do nothing
10582         }
10583 
onItemRangeChanged(int positionStart, int itemCount)10584         public void onItemRangeChanged(int positionStart, int itemCount) {
10585             // do nothing
10586         }
10587 
onItemRangeChanged(int positionStart, int itemCount, Object payload)10588         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
10589             // fallback to onItemRangeChanged(positionStart, itemCount) if app
10590             // does not override this method.
10591             onItemRangeChanged(positionStart, itemCount);
10592         }
10593 
onItemRangeInserted(int positionStart, int itemCount)10594         public void onItemRangeInserted(int positionStart, int itemCount) {
10595             // do nothing
10596         }
10597 
onItemRangeRemoved(int positionStart, int itemCount)10598         public void onItemRangeRemoved(int positionStart, int itemCount) {
10599             // do nothing
10600         }
10601 
onItemRangeMoved(int fromPosition, int toPosition, int itemCount)10602         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
10603             // do nothing
10604         }
10605     }
10606 
10607     /**
10608      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
10609      * provides methods to trigger a programmatic scroll.</p>
10610      *
10611      * @see LinearSmoothScroller
10612      */
10613     public abstract static class SmoothScroller {
10614 
10615         private int mTargetPosition = RecyclerView.NO_POSITION;
10616 
10617         private RecyclerView mRecyclerView;
10618 
10619         private LayoutManager mLayoutManager;
10620 
10621         private boolean mPendingInitialRun;
10622 
10623         private boolean mRunning;
10624 
10625         private View mTargetView;
10626 
10627         private final Action mRecyclingAction;
10628 
SmoothScroller()10629         public SmoothScroller() {
10630             mRecyclingAction = new Action(0, 0);
10631         }
10632 
10633         /**
10634          * Starts a smooth scroll for the given target position.
10635          * <p>In each animation step, {@link RecyclerView} will check
10636          * for the target view and call either
10637          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10638          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
10639          * SmoothScroller is stopped.</p>
10640          *
10641          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
10642          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
10643          * stop calling SmoothScroller in each animation step.</p>
10644          */
start(RecyclerView recyclerView, LayoutManager layoutManager)10645         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
10646             mRecyclerView = recyclerView;
10647             mLayoutManager = layoutManager;
10648             if (mTargetPosition == RecyclerView.NO_POSITION) {
10649                 throw new IllegalArgumentException("Invalid target position");
10650             }
10651             mRecyclerView.mState.mTargetPosition = mTargetPosition;
10652             mRunning = true;
10653             mPendingInitialRun = true;
10654             mTargetView = findViewByPosition(getTargetPosition());
10655             onStart();
10656             mRecyclerView.mViewFlinger.postOnAnimation();
10657         }
10658 
setTargetPosition(int targetPosition)10659         public void setTargetPosition(int targetPosition) {
10660             mTargetPosition = targetPosition;
10661         }
10662 
10663         /**
10664          * @return The LayoutManager to which this SmoothScroller is attached. Will return
10665          * <code>null</code> after the SmoothScroller is stopped.
10666          */
10667         @Nullable
getLayoutManager()10668         public LayoutManager getLayoutManager() {
10669             return mLayoutManager;
10670         }
10671 
10672         /**
10673          * Stops running the SmoothScroller in each animation callback. Note that this does not
10674          * cancel any existing {@link Action} updated by
10675          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
10676          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
10677          */
stop()10678         protected final void stop() {
10679             if (!mRunning) {
10680                 return;
10681             }
10682             onStop();
10683             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
10684             mTargetView = null;
10685             mTargetPosition = RecyclerView.NO_POSITION;
10686             mPendingInitialRun = false;
10687             mRunning = false;
10688             // trigger a cleanup
10689             mLayoutManager.onSmoothScrollerStopped(this);
10690             // clear references to avoid any potential leak by a custom smooth scroller
10691             mLayoutManager = null;
10692             mRecyclerView = null;
10693         }
10694 
10695         /**
10696          * Returns true if SmoothScroller has been started but has not received the first
10697          * animation
10698          * callback yet.
10699          *
10700          * @return True if this SmoothScroller is waiting to start
10701          */
isPendingInitialRun()10702         public boolean isPendingInitialRun() {
10703             return mPendingInitialRun;
10704         }
10705 
10706 
10707         /**
10708          * @return True if SmoothScroller is currently active
10709          */
isRunning()10710         public boolean isRunning() {
10711             return mRunning;
10712         }
10713 
10714         /**
10715          * Returns the adapter position of the target item
10716          *
10717          * @return Adapter position of the target item or
10718          * {@link RecyclerView#NO_POSITION} if no target view is set.
10719          */
getTargetPosition()10720         public int getTargetPosition() {
10721             return mTargetPosition;
10722         }
10723 
onAnimation(int dx, int dy)10724         private void onAnimation(int dx, int dy) {
10725             final RecyclerView recyclerView = mRecyclerView;
10726             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
10727                 stop();
10728             }
10729             mPendingInitialRun = false;
10730             if (mTargetView != null) {
10731                 // verify target position
10732                 if (getChildPosition(mTargetView) == mTargetPosition) {
10733                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
10734                     mRecyclingAction.runIfNecessary(recyclerView);
10735                     stop();
10736                 } else {
10737                     Log.e(TAG, "Passed over target position while smooth scrolling.");
10738                     mTargetView = null;
10739                 }
10740             }
10741             if (mRunning) {
10742                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
10743                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
10744                 mRecyclingAction.runIfNecessary(recyclerView);
10745                 if (hadJumpTarget) {
10746                     // It is not stopped so needs to be restarted
10747                     if (mRunning) {
10748                         mPendingInitialRun = true;
10749                         recyclerView.mViewFlinger.postOnAnimation();
10750                     } else {
10751                         stop(); // done
10752                     }
10753                 }
10754             }
10755         }
10756 
10757         /**
10758          * @see RecyclerView#getChildLayoutPosition(android.view.View)
10759          */
getChildPosition(View view)10760         public int getChildPosition(View view) {
10761             return mRecyclerView.getChildLayoutPosition(view);
10762         }
10763 
10764         /**
10765          * @see RecyclerView.LayoutManager#getChildCount()
10766          */
getChildCount()10767         public int getChildCount() {
10768             return mRecyclerView.mLayout.getChildCount();
10769         }
10770 
10771         /**
10772          * @see RecyclerView.LayoutManager#findViewByPosition(int)
10773          */
findViewByPosition(int position)10774         public View findViewByPosition(int position) {
10775             return mRecyclerView.mLayout.findViewByPosition(position);
10776         }
10777 
10778         /**
10779          * @see RecyclerView#scrollToPosition(int)
10780          * @deprecated Use {@link Action#jumpTo(int)}.
10781          */
10782         @Deprecated
instantScrollToPosition(int position)10783         public void instantScrollToPosition(int position) {
10784             mRecyclerView.scrollToPosition(position);
10785         }
10786 
onChildAttachedToWindow(View child)10787         protected void onChildAttachedToWindow(View child) {
10788             if (getChildPosition(child) == getTargetPosition()) {
10789                 mTargetView = child;
10790                 if (DEBUG) {
10791                     Log.d(TAG, "smooth scroll target view has been attached");
10792                 }
10793             }
10794         }
10795 
10796         /**
10797          * Normalizes the vector.
10798          * @param scrollVector The vector that points to the target scroll position
10799          */
normalize(PointF scrollVector)10800         protected void normalize(PointF scrollVector) {
10801             final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
10802                     * scrollVector.y);
10803             scrollVector.x /= magnitude;
10804             scrollVector.y /= magnitude;
10805         }
10806 
10807         /**
10808          * Called when smooth scroll is started. This might be a good time to do setup.
10809          */
10810         protected abstract void onStart();
10811 
10812         /**
10813          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
10814          * @see #stop()
10815          */
10816         protected abstract void onStop();
10817 
10818         /**
10819          * <p>RecyclerView will call this method each time it scrolls until it can find the target
10820          * position in the layout.</p>
10821          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
10822          * provided {@link Action} to define the next scroll.</p>
10823          *
10824          * @param dx        Last scroll amount horizontally
10825          * @param dy        Last scroll amount vertically
10826          * @param state     Transient state of RecyclerView
10827          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
10828          *                  update this object.
10829          */
10830         protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
10831 
10832         /**
10833          * Called when the target position is laid out. This is the last callback SmoothScroller
10834          * will receive and it should update the provided {@link Action} to define the scroll
10835          * details towards the target view.
10836          * @param targetView    The view element which render the target position.
10837          * @param state         Transient state of RecyclerView
10838          * @param action        Action instance that you should update to define final scroll action
10839          *                      towards the targetView
10840          */
10841         protected abstract void onTargetFound(View targetView, State state, Action action);
10842 
10843         /**
10844          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
10845          */
10846         public static class Action {
10847 
10848             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
10849 
10850             private int mDx;
10851 
10852             private int mDy;
10853 
10854             private int mDuration;
10855 
10856             private int mJumpToPosition = NO_POSITION;
10857 
10858             private Interpolator mInterpolator;
10859 
10860             private boolean mChanged = false;
10861 
10862             // we track this variable to inform custom implementer if they are updating the action
10863             // in every animation callback
10864             private int mConsecutiveUpdates = 0;
10865 
10866             /**
10867              * @param dx Pixels to scroll horizontally
10868              * @param dy Pixels to scroll vertically
10869              */
Action(int dx, int dy)10870             public Action(int dx, int dy) {
10871                 this(dx, dy, UNDEFINED_DURATION, null);
10872             }
10873 
10874             /**
10875              * @param dx       Pixels to scroll horizontally
10876              * @param dy       Pixels to scroll vertically
10877              * @param duration Duration of the animation in milliseconds
10878              */
Action(int dx, int dy, int duration)10879             public Action(int dx, int dy, int duration) {
10880                 this(dx, dy, duration, null);
10881             }
10882 
10883             /**
10884              * @param dx           Pixels to scroll horizontally
10885              * @param dy           Pixels to scroll vertically
10886              * @param duration     Duration of the animation in milliseconds
10887              * @param interpolator Interpolator to be used when calculating scroll position in each
10888              *                     animation step
10889              */
Action(int dx, int dy, int duration, Interpolator interpolator)10890             public Action(int dx, int dy, int duration, Interpolator interpolator) {
10891                 mDx = dx;
10892                 mDy = dy;
10893                 mDuration = duration;
10894                 mInterpolator = interpolator;
10895             }
10896 
10897             /**
10898              * Instead of specifying pixels to scroll, use the target position to jump using
10899              * {@link RecyclerView#scrollToPosition(int)}.
10900              * <p>
10901              * You may prefer using this method if scroll target is really far away and you prefer
10902              * to jump to a location and smooth scroll afterwards.
10903              * <p>
10904              * Note that calling this method takes priority over other update methods such as
10905              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
10906              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
10907              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
10908              * frame.
10909              *
10910              * @param targetPosition The target item position to scroll to using instant scrolling.
10911              */
jumpTo(int targetPosition)10912             public void jumpTo(int targetPosition) {
10913                 mJumpToPosition = targetPosition;
10914             }
10915 
hasJumpTarget()10916             boolean hasJumpTarget() {
10917                 return mJumpToPosition >= 0;
10918             }
10919 
runIfNecessary(RecyclerView recyclerView)10920             void runIfNecessary(RecyclerView recyclerView) {
10921                 if (mJumpToPosition >= 0) {
10922                     final int position = mJumpToPosition;
10923                     mJumpToPosition = NO_POSITION;
10924                     recyclerView.jumpToPositionForSmoothScroller(position);
10925                     mChanged = false;
10926                     return;
10927                 }
10928                 if (mChanged) {
10929                     validate();
10930                     if (mInterpolator == null) {
10931                         if (mDuration == UNDEFINED_DURATION) {
10932                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
10933                         } else {
10934                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
10935                         }
10936                     } else {
10937                         recyclerView.mViewFlinger.smoothScrollBy(
10938                                 mDx, mDy, mDuration, mInterpolator);
10939                     }
10940                     mConsecutiveUpdates++;
10941                     if (mConsecutiveUpdates > 10) {
10942                         // A new action is being set in every animation step. This looks like a bad
10943                         // implementation. Inform developer.
10944                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
10945                                 + " you are not changing it unless necessary");
10946                     }
10947                     mChanged = false;
10948                 } else {
10949                     mConsecutiveUpdates = 0;
10950                 }
10951             }
10952 
validate()10953             private void validate() {
10954                 if (mInterpolator != null && mDuration < 1) {
10955                     throw new IllegalStateException("If you provide an interpolator, you must"
10956                             + " set a positive duration");
10957                 } else if (mDuration < 1) {
10958                     throw new IllegalStateException("Scroll duration must be a positive number");
10959                 }
10960             }
10961 
getDx()10962             public int getDx() {
10963                 return mDx;
10964             }
10965 
setDx(int dx)10966             public void setDx(int dx) {
10967                 mChanged = true;
10968                 mDx = dx;
10969             }
10970 
getDy()10971             public int getDy() {
10972                 return mDy;
10973             }
10974 
setDy(int dy)10975             public void setDy(int dy) {
10976                 mChanged = true;
10977                 mDy = dy;
10978             }
10979 
getDuration()10980             public int getDuration() {
10981                 return mDuration;
10982             }
10983 
setDuration(int duration)10984             public void setDuration(int duration) {
10985                 mChanged = true;
10986                 mDuration = duration;
10987             }
10988 
getInterpolator()10989             public Interpolator getInterpolator() {
10990                 return mInterpolator;
10991             }
10992 
10993             /**
10994              * Sets the interpolator to calculate scroll steps
10995              * @param interpolator The interpolator to use. If you specify an interpolator, you must
10996              *                     also set the duration.
10997              * @see #setDuration(int)
10998              */
setInterpolator(Interpolator interpolator)10999             public void setInterpolator(Interpolator interpolator) {
11000                 mChanged = true;
11001                 mInterpolator = interpolator;
11002             }
11003 
11004             /**
11005              * Updates the action with given parameters.
11006              * @param dx Pixels to scroll horizontally
11007              * @param dy Pixels to scroll vertically
11008              * @param duration Duration of the animation in milliseconds
11009              * @param interpolator Interpolator to be used when calculating scroll position in each
11010              *                     animation step
11011              */
update(int dx, int dy, int duration, Interpolator interpolator)11012             public void update(int dx, int dy, int duration, Interpolator interpolator) {
11013                 mDx = dx;
11014                 mDy = dy;
11015                 mDuration = duration;
11016                 mInterpolator = interpolator;
11017                 mChanged = true;
11018             }
11019         }
11020 
11021         /**
11022          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
11023          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
11024          */
11025         public interface ScrollVectorProvider {
11026             /**
11027              * Should calculate the vector that points to the direction where the target position
11028              * can be found.
11029              * <p>
11030              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
11031              * the target position.
11032              * <p>
11033              * The magnitude of the vector is not important. It is always normalized before being
11034              * used by the {@link LinearSmoothScroller}.
11035              * <p>
11036              * LayoutManager should not check whether the position exists in the adapter or not.
11037              *
11038              * @param targetPosition the target position to which the returned vector should point
11039              *
11040              * @return the scroll vector for a given position.
11041              */
11042             PointF computeScrollVectorForPosition(int targetPosition);
11043         }
11044     }
11045 
11046     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
hasObservers()11047         public boolean hasObservers() {
11048             return !mObservers.isEmpty();
11049         }
11050 
notifyChanged()11051         public void notifyChanged() {
11052             // since onChanged() is implemented by the app, it could do anything, including
11053             // removing itself from {@link mObservers} - and that could cause problems if
11054             // an iterator is used on the ArrayList {@link mObservers}.
11055             // to avoid such problems, just march thru the list in the reverse order.
11056             for (int i = mObservers.size() - 1; i >= 0; i--) {
11057                 mObservers.get(i).onChanged();
11058             }
11059         }
11060 
notifyItemRangeChanged(int positionStart, int itemCount)11061         public void notifyItemRangeChanged(int positionStart, int itemCount) {
11062             notifyItemRangeChanged(positionStart, itemCount, null);
11063         }
11064 
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)11065         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
11066             // since onItemRangeChanged() is implemented by the app, it could do anything, including
11067             // removing itself from {@link mObservers} - and that could cause problems if
11068             // an iterator is used on the ArrayList {@link mObservers}.
11069             // to avoid such problems, just march thru the list in the reverse order.
11070             for (int i = mObservers.size() - 1; i >= 0; i--) {
11071                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
11072             }
11073         }
11074 
notifyItemRangeInserted(int positionStart, int itemCount)11075         public void notifyItemRangeInserted(int positionStart, int itemCount) {
11076             // since onItemRangeInserted() is implemented by the app, it could do anything,
11077             // including removing itself from {@link mObservers} - and that could cause problems if
11078             // an iterator is used on the ArrayList {@link mObservers}.
11079             // to avoid such problems, just march thru the list in the reverse order.
11080             for (int i = mObservers.size() - 1; i >= 0; i--) {
11081                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
11082             }
11083         }
11084 
notifyItemRangeRemoved(int positionStart, int itemCount)11085         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
11086             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
11087             // removing itself from {@link mObservers} - and that could cause problems if
11088             // an iterator is used on the ArrayList {@link mObservers}.
11089             // to avoid such problems, just march thru the list in the reverse order.
11090             for (int i = mObservers.size() - 1; i >= 0; i--) {
11091                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
11092             }
11093         }
11094 
notifyItemMoved(int fromPosition, int toPosition)11095         public void notifyItemMoved(int fromPosition, int toPosition) {
11096             for (int i = mObservers.size() - 1; i >= 0; i--) {
11097                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
11098             }
11099         }
11100     }
11101 
11102     /**
11103      * This is public so that the CREATOR can be access on cold launch.
11104      * @hide
11105      */
11106     public static class SavedState extends AbsSavedState {
11107 
11108         Parcelable mLayoutState;
11109 
11110         /**
11111          * called by CREATOR
11112          */
SavedState(Parcel in)11113         SavedState(Parcel in) {
11114             super(in);
11115             mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
11116         }
11117 
11118         /**
11119          * Called by onSaveInstanceState
11120          */
SavedState(Parcelable superState)11121         SavedState(Parcelable superState) {
11122             super(superState);
11123         }
11124 
11125         @Override
writeToParcel(Parcel dest, int flags)11126         public void writeToParcel(Parcel dest, int flags) {
11127             super.writeToParcel(dest, flags);
11128             dest.writeParcelable(mLayoutState, 0);
11129         }
11130 
copyFrom(SavedState other)11131         void copyFrom(SavedState other) {
11132             mLayoutState = other.mLayoutState;
11133         }
11134 
11135         public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
11136                     @Override
11137                     public SavedState createFromParcel(Parcel in) {
11138                         return new SavedState(in);
11139                     }
11140 
11141                     @Override
11142                     public SavedState[] newArray(int size) {
11143                         return new SavedState[size];
11144                     }
11145                 };
11146     }
11147     /**
11148      * <p>Contains useful information about the current RecyclerView state like target scroll
11149      * position or view focus. State object can also keep arbitrary data, identified by resource
11150      * ids.</p>
11151      * <p>Often times, RecyclerView components will need to pass information between each other.
11152      * To provide a well defined data bus between components, RecyclerView passes the same State
11153      * object to component callbacks and these components can use it to exchange data.</p>
11154      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
11155      * data between your components without needing to manage their lifecycles.</p>
11156      */
11157     public static class State {
11158         static final int STEP_START = 1;
11159         static final int STEP_LAYOUT = 1 << 1;
11160         static final int STEP_ANIMATIONS = 1 << 2;
11161 
assertLayoutStep(int accepted)11162         void assertLayoutStep(int accepted) {
11163             if ((accepted & mLayoutStep) == 0) {
11164                 throw new IllegalStateException("Layout state should be one of "
11165                         + Integer.toBinaryString(accepted) + " but it is "
11166                         + Integer.toBinaryString(mLayoutStep));
11167             }
11168         }
11169 
11170 
11171         /** Owned by SmoothScroller */
11172         private int mTargetPosition = RecyclerView.NO_POSITION;
11173 
11174         private SparseArray<Object> mData;
11175 
11176         ////////////////////////////////////////////////////////////////////////////////////////////
11177         // Fields below are carried from one layout pass to the next
11178         ////////////////////////////////////////////////////////////////////////////////////////////
11179 
11180         /**
11181          * Number of items adapter had in the previous layout.
11182          */
11183         int mPreviousLayoutItemCount = 0;
11184 
11185         /**
11186          * Number of items that were NOT laid out but has been deleted from the adapter after the
11187          * previous layout.
11188          */
11189         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
11190 
11191         ////////////////////////////////////////////////////////////////////////////////////////////
11192         // Fields below must be updated or cleared before they are used (generally before a pass)
11193         ////////////////////////////////////////////////////////////////////////////////////////////
11194 
11195         @IntDef(flag = true, value = {
11196                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
11197         })
11198         @Retention(RetentionPolicy.SOURCE)
11199         @interface LayoutState {}
11200 
11201         @LayoutState
11202         int mLayoutStep = STEP_START;
11203 
11204         /**
11205          * Number of items adapter has.
11206          */
11207         int mItemCount = 0;
11208 
11209         boolean mStructureChanged = false;
11210 
11211         boolean mInPreLayout = false;
11212 
11213         boolean mTrackOldChangeHolders = false;
11214 
11215         boolean mIsMeasuring = false;
11216 
11217         ////////////////////////////////////////////////////////////////////////////////////////////
11218         // Fields below are always reset outside of the pass (or passes) that use them
11219         ////////////////////////////////////////////////////////////////////////////////////////////
11220 
11221         boolean mRunSimpleAnimations = false;
11222 
11223         boolean mRunPredictiveAnimations = false;
11224 
11225         /**
11226          * This data is saved before a layout calculation happens. After the layout is finished,
11227          * if the previously focused view has been replaced with another view for the same item, we
11228          * move the focus to the new item automatically.
11229          */
11230         int mFocusedItemPosition;
11231         long mFocusedItemId;
11232         // when a sub child has focus, record its id and see if we can directly request focus on
11233         // that one instead
11234         int mFocusedSubChildId;
11235 
11236         ////////////////////////////////////////////////////////////////////////////////////////////
11237 
reset()11238         State reset() {
11239             mTargetPosition = RecyclerView.NO_POSITION;
11240             if (mData != null) {
11241                 mData.clear();
11242             }
11243             mItemCount = 0;
11244             mStructureChanged = false;
11245             mIsMeasuring = false;
11246             return this;
11247         }
11248 
11249         /**
11250          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
11251          * prior to any layout passes.
11252          *
11253          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
11254          * that Recycler#getViewForPosition() can function safely.</p>
11255          */
prepareForNestedPrefetch(Adapter adapter)11256         void prepareForNestedPrefetch(Adapter adapter) {
11257             mLayoutStep = STEP_START;
11258             mItemCount = adapter.getItemCount();
11259             mStructureChanged = false;
11260             mInPreLayout = false;
11261             mTrackOldChangeHolders = false;
11262             mIsMeasuring = false;
11263         }
11264 
11265         /**
11266          * Returns true if the RecyclerView is currently measuring the layout. This value is
11267          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
11268          * has non-exact measurement specs.
11269          * <p>
11270          * Note that if the LayoutManager supports predictive animations and it is calculating the
11271          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
11272          * {@code onMeasure} call. This is because pre-layout means the previous state of the
11273          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
11274          * LayoutManager is always guaranteed to receive another call to
11275          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
11276          *
11277          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
11278          */
isMeasuring()11279         public boolean isMeasuring() {
11280             return mIsMeasuring;
11281         }
11282 
11283         /**
11284          * Returns true if
11285          * @return
11286          */
isPreLayout()11287         public boolean isPreLayout() {
11288             return mInPreLayout;
11289         }
11290 
11291         /**
11292          * Returns whether RecyclerView will run predictive animations in this layout pass
11293          * or not.
11294          *
11295          * @return true if RecyclerView is calculating predictive animations to be run at the end
11296          *         of the layout pass.
11297          */
willRunPredictiveAnimations()11298         public boolean willRunPredictiveAnimations() {
11299             return mRunPredictiveAnimations;
11300         }
11301 
11302         /**
11303          * Returns whether RecyclerView will run simple animations in this layout pass
11304          * or not.
11305          *
11306          * @return true if RecyclerView is calculating simple animations to be run at the end of
11307          *         the layout pass.
11308          */
willRunSimpleAnimations()11309         public boolean willRunSimpleAnimations() {
11310             return mRunSimpleAnimations;
11311         }
11312 
11313         /**
11314          * Removes the mapping from the specified id, if there was any.
11315          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
11316          *                   preserve cross functionality and avoid conflicts.
11317          */
remove(int resourceId)11318         public void remove(int resourceId) {
11319             if (mData == null) {
11320                 return;
11321             }
11322             mData.remove(resourceId);
11323         }
11324 
11325         /**
11326          * Gets the Object mapped from the specified id, or <code>null</code>
11327          * if no such data exists.
11328          *
11329          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
11330          *                   to
11331          *                   preserve cross functionality and avoid conflicts.
11332          */
get(int resourceId)11333         public <T> T get(int resourceId) {
11334             if (mData == null) {
11335                 return null;
11336             }
11337             return (T) mData.get(resourceId);
11338         }
11339 
11340         /**
11341          * Adds a mapping from the specified id to the specified value, replacing the previous
11342          * mapping from the specified key if there was one.
11343          *
11344          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
11345          *                   preserve cross functionality and avoid conflicts.
11346          * @param data       The data you want to associate with the resourceId.
11347          */
put(int resourceId, Object data)11348         public void put(int resourceId, Object data) {
11349             if (mData == null) {
11350                 mData = new SparseArray<Object>();
11351             }
11352             mData.put(resourceId, data);
11353         }
11354 
11355         /**
11356          * If scroll is triggered to make a certain item visible, this value will return the
11357          * adapter index of that item.
11358          * @return Adapter index of the target item or
11359          * {@link RecyclerView#NO_POSITION} if there is no target
11360          * position.
11361          */
getTargetScrollPosition()11362         public int getTargetScrollPosition() {
11363             return mTargetPosition;
11364         }
11365 
11366         /**
11367          * Returns if current scroll has a target position.
11368          * @return true if scroll is being triggered to make a certain position visible
11369          * @see #getTargetScrollPosition()
11370          */
hasTargetScrollPosition()11371         public boolean hasTargetScrollPosition() {
11372             return mTargetPosition != RecyclerView.NO_POSITION;
11373         }
11374 
11375         /**
11376          * @return true if the structure of the data set has changed since the last call to
11377          *         onLayoutChildren, false otherwise
11378          */
didStructureChange()11379         public boolean didStructureChange() {
11380             return mStructureChanged;
11381         }
11382 
11383         /**
11384          * Returns the total number of items that can be laid out. Note that this number is not
11385          * necessarily equal to the number of items in the adapter, so you should always use this
11386          * number for your position calculations and never access the adapter directly.
11387          * <p>
11388          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
11389          * data changes on existing Views. These calculations are used to decide which animations
11390          * should be run.
11391          * <p>
11392          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
11393          * present the correct state to LayoutManager in pre-layout pass.
11394          * <p>
11395          * For example, a newly added item is not included in pre-layout item count because
11396          * pre-layout reflects the contents of the adapter before the item is added. Behind the
11397          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
11398          * LayoutManager does not know about the new item's existence in pre-layout. The item will
11399          * be available in second layout pass and will be included in the item count. Similar
11400          * adjustments are made for moved and removed items as well.
11401          * <p>
11402          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
11403          *
11404          * @return The number of items currently available
11405          * @see LayoutManager#getItemCount()
11406          */
getItemCount()11407         public int getItemCount() {
11408             return mInPreLayout
11409                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
11410                     : mItemCount;
11411         }
11412 
11413         @Override
toString()11414         public String toString() {
11415             return "State{"
11416                     + "mTargetPosition=" + mTargetPosition
11417                     + ", mData=" + mData
11418                     + ", mItemCount=" + mItemCount
11419                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
11420                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
11421                     + mDeletedInvisibleItemCountSincePreviousLayout
11422                     + ", mStructureChanged=" + mStructureChanged
11423                     + ", mInPreLayout=" + mInPreLayout
11424                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
11425                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
11426                     + '}';
11427         }
11428     }
11429 
11430     /**
11431      * This class defines the behavior of fling if the developer wishes to handle it.
11432      * <p>
11433      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
11434      *
11435      * @see #setOnFlingListener(OnFlingListener)
11436      */
11437     public abstract static class OnFlingListener {
11438 
11439         /**
11440          * Override this to handle a fling given the velocities in both x and y directions.
11441          * Note that this method will only be called if the associated {@link LayoutManager}
11442          * supports scrolling and the fling is not handled by nested scrolls first.
11443          *
11444          * @param velocityX the fling velocity on the X axis
11445          * @param velocityY the fling velocity on the Y axis
11446          *
11447          * @return true if the fling washandled, false otherwise.
11448          */
11449         public abstract boolean onFling(int velocityX, int velocityY);
11450     }
11451 
11452     /**
11453      * Internal listener that manages items after animations finish. This is how items are
11454      * retained (not recycled) during animations, but allowed to be recycled afterwards.
11455      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
11456      * method on the animator's listener when it is done animating any item.
11457      */
11458     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
11459 
ItemAnimatorRestoreListener()11460         ItemAnimatorRestoreListener() {
11461         }
11462 
11463         @Override
onAnimationFinished(ViewHolder item)11464         public void onAnimationFinished(ViewHolder item) {
11465             item.setIsRecyclable(true);
11466             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
11467                 item.mShadowedHolder = null;
11468             }
11469             // always null this because an OldViewHolder can never become NewViewHolder w/o being
11470             // recycled.
11471             item.mShadowingHolder = null;
11472             if (!item.shouldBeKeptAsChild()) {
11473                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
11474                     removeDetachedView(item.itemView, false);
11475                 }
11476             }
11477         }
11478     }
11479 
11480     /**
11481      * This class defines the animations that take place on items as changes are made
11482      * to the adapter.
11483      *
11484      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
11485      * ViewHolder items. The RecyclerView will manage retaining these items while they
11486      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
11487      * when a ViewHolder's animation is finished. In other words, there must be a matching
11488      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
11489      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
11490      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11491      * animateChange()}
11492      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
11493      * and
11494      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11495      * animateDisappearance()} call.
11496      *
11497      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
11498      *
11499      * @see #setItemAnimator(ItemAnimator)
11500      */
11501     @SuppressWarnings("UnusedParameters")
11502     public abstract static class ItemAnimator {
11503 
11504         /**
11505          * The Item represented by this ViewHolder is updated.
11506          * <p>
11507          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11508          */
11509         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
11510 
11511         /**
11512          * The Item represented by this ViewHolder is removed from the adapter.
11513          * <p>
11514          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11515          */
11516         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
11517 
11518         /**
11519          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
11520          * represented by this ViewHolder is invalid.
11521          * <p>
11522          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11523          */
11524         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
11525 
11526         /**
11527          * The position of the Item represented by this ViewHolder has been changed. This flag is
11528          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
11529          * any adapter change that may have a side effect on this item. (e.g. The item before this
11530          * one has been removed from the Adapter).
11531          * <p>
11532          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11533          */
11534         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
11535 
11536         /**
11537          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
11538          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
11539          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
11540          * to add new items in pre-layout to specify their virtual location when they are invisible
11541          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
11542          * <p>
11543          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11544          */
11545         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
11546                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
11547 
11548         /**
11549          * The set of flags that might be passed to
11550          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11551          */
11552         @IntDef(flag = true, value = {
11553                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
11554                 FLAG_APPEARED_IN_PRE_LAYOUT
11555         })
11556         @Retention(RetentionPolicy.SOURCE)
11557         public @interface AdapterChanges {}
11558         private ItemAnimatorListener mListener = null;
11559         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
11560                 new ArrayList<ItemAnimatorFinishedListener>();
11561 
11562         private long mAddDuration = 120;
11563         private long mRemoveDuration = 120;
11564         private long mMoveDuration = 250;
11565         private long mChangeDuration = 250;
11566 
11567         /**
11568          * Gets the current duration for which all move animations will run.
11569          *
11570          * @return The current move duration
11571          */
getMoveDuration()11572         public long getMoveDuration() {
11573             return mMoveDuration;
11574         }
11575 
11576         /**
11577          * Sets the duration for which all move animations will run.
11578          *
11579          * @param moveDuration The move duration
11580          */
setMoveDuration(long moveDuration)11581         public void setMoveDuration(long moveDuration) {
11582             mMoveDuration = moveDuration;
11583         }
11584 
11585         /**
11586          * Gets the current duration for which all add animations will run.
11587          *
11588          * @return The current add duration
11589          */
getAddDuration()11590         public long getAddDuration() {
11591             return mAddDuration;
11592         }
11593 
11594         /**
11595          * Sets the duration for which all add animations will run.
11596          *
11597          * @param addDuration The add duration
11598          */
setAddDuration(long addDuration)11599         public void setAddDuration(long addDuration) {
11600             mAddDuration = addDuration;
11601         }
11602 
11603         /**
11604          * Gets the current duration for which all remove animations will run.
11605          *
11606          * @return The current remove duration
11607          */
getRemoveDuration()11608         public long getRemoveDuration() {
11609             return mRemoveDuration;
11610         }
11611 
11612         /**
11613          * Sets the duration for which all remove animations will run.
11614          *
11615          * @param removeDuration The remove duration
11616          */
setRemoveDuration(long removeDuration)11617         public void setRemoveDuration(long removeDuration) {
11618             mRemoveDuration = removeDuration;
11619         }
11620 
11621         /**
11622          * Gets the current duration for which all change animations will run.
11623          *
11624          * @return The current change duration
11625          */
getChangeDuration()11626         public long getChangeDuration() {
11627             return mChangeDuration;
11628         }
11629 
11630         /**
11631          * Sets the duration for which all change animations will run.
11632          *
11633          * @param changeDuration The change duration
11634          */
setChangeDuration(long changeDuration)11635         public void setChangeDuration(long changeDuration) {
11636             mChangeDuration = changeDuration;
11637         }
11638 
11639         /**
11640          * Internal only:
11641          * Sets the listener that must be called when the animator is finished
11642          * animating the item (or immediately if no animation happens). This is set
11643          * internally and is not intended to be set by external code.
11644          *
11645          * @param listener The listener that must be called.
11646          */
setListener(ItemAnimatorListener listener)11647         void setListener(ItemAnimatorListener listener) {
11648             mListener = listener;
11649         }
11650 
11651         /**
11652          * Called by the RecyclerView before the layout begins. Item animator should record
11653          * necessary information about the View before it is potentially rebound, moved or removed.
11654          * <p>
11655          * The data returned from this method will be passed to the related <code>animate**</code>
11656          * methods.
11657          * <p>
11658          * Note that this method may be called after pre-layout phase if LayoutManager adds new
11659          * Views to the layout in pre-layout pass.
11660          * <p>
11661          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11662          * the View and the adapter change flags.
11663          *
11664          * @param state       The current State of RecyclerView which includes some useful data
11665          *                    about the layout that will be calculated.
11666          * @param viewHolder  The ViewHolder whose information should be recorded.
11667          * @param changeFlags Additional information about what changes happened in the Adapter
11668          *                    about the Item represented by this ViewHolder. For instance, if
11669          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
11670          * @param payloads    The payload list that was previously passed to
11671          *                    {@link Adapter#notifyItemChanged(int, Object)} or
11672          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
11673          *
11674          * @return An ItemHolderInfo instance that preserves necessary information about the
11675          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
11676          * after layout is complete.
11677          *
11678          * @see #recordPostLayoutInformation(State, ViewHolder)
11679          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11680          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11681          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11682          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11683          */
recordPreLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags, @NonNull List<Object> payloads)11684         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
11685                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
11686                 @NonNull List<Object> payloads) {
11687             return obtainHolderInfo().setFrom(viewHolder);
11688         }
11689 
11690         /**
11691          * Called by the RecyclerView after the layout is complete. Item animator should record
11692          * necessary information about the View's final state.
11693          * <p>
11694          * The data returned from this method will be passed to the related <code>animate**</code>
11695          * methods.
11696          * <p>
11697          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
11698          * the View.
11699          *
11700          * @param state      The current State of RecyclerView which includes some useful data about
11701          *                   the layout that will be calculated.
11702          * @param viewHolder The ViewHolder whose information should be recorded.
11703          *
11704          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
11705          * This object will be passed back to related <code>animate**</code> methods when
11706          * RecyclerView decides how items should be animated.
11707          *
11708          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
11709          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11710          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11711          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11712          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11713          */
recordPostLayoutInformation(@onNull State state, @NonNull ViewHolder viewHolder)11714         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
11715                 @NonNull ViewHolder viewHolder) {
11716             return obtainHolderInfo().setFrom(viewHolder);
11717         }
11718 
11719         /**
11720          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
11721          * <p>
11722          * This means that the View was a child of the LayoutManager when layout started but has
11723          * been removed by the LayoutManager. It might have been removed from the adapter or simply
11724          * become invisible due to other factors. You can distinguish these two cases by checking
11725          * the change flags that were passed to
11726          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11727          * <p>
11728          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11729          * animation callback method which will be called by the RecyclerView depends on the
11730          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11731          * LayoutManager's decision whether to layout the changed version of a disappearing
11732          * ViewHolder or not. RecyclerView will call
11733          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11734          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
11735          * returns {@code false} from
11736          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11737          * LayoutManager lays out a new disappearing view that holds the updated information.
11738          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11739          * <p>
11740          * If LayoutManager supports predictive animations, it might provide a target disappear
11741          * location for the View by laying it out in that location. When that happens,
11742          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
11743          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
11744          * <p>
11745          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11746          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11747          * decides not to animate the view).
11748          *
11749          * @param viewHolder    The ViewHolder which should be animated
11750          * @param preLayoutInfo The information that was returned from
11751          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11752          * @param postLayoutInfo The information that was returned from
11753          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
11754          *                       null if the LayoutManager did not layout the item.
11755          *
11756          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11757          * false otherwise.
11758          */
11759         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
11760                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
11761 
11762         /**
11763          * Called by the RecyclerView when a ViewHolder is added to the layout.
11764          * <p>
11765          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
11766          * but has  been added by the LayoutManager. It might be newly added to the adapter or
11767          * simply become visible due to other factors.
11768          * <p>
11769          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11770          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11771          * decides not to animate the view).
11772          *
11773          * @param viewHolder     The ViewHolder which should be animated
11774          * @param preLayoutInfo  The information that was returned from
11775          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11776          *                       Might be null if Item was just added to the adapter or
11777          *                       LayoutManager does not support predictive animations or it could
11778          *                       not predict that this ViewHolder will become visible.
11779          * @param postLayoutInfo The information that was returned from {@link
11780          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11781          *
11782          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11783          * false otherwise.
11784          */
11785         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
11786                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11787 
11788         /**
11789          * Called by the RecyclerView when a ViewHolder is present in both before and after the
11790          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
11791          * for it or a {@link Adapter#notifyDataSetChanged()} call.
11792          * <p>
11793          * This ViewHolder still represents the same data that it was representing when the layout
11794          * started but its position / size may be changed by the LayoutManager.
11795          * <p>
11796          * If the Item's layout position didn't change, RecyclerView still calls this method because
11797          * it does not track this information (or does not necessarily know that an animation is
11798          * not required). Your ItemAnimator should handle this case and if there is nothing to
11799          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
11800          * <code>false</code>.
11801          * <p>
11802          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
11803          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
11804          * decides not to animate the view).
11805          *
11806          * @param viewHolder     The ViewHolder which should be animated
11807          * @param preLayoutInfo  The information that was returned from
11808          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11809          * @param postLayoutInfo The information that was returned from {@link
11810          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11811          *
11812          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11813          * false otherwise.
11814          */
11815         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
11816                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11817 
11818         /**
11819          * Called by the RecyclerView when an adapter item is present both before and after the
11820          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
11821          * for it. This method may also be called when
11822          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
11823          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
11824          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
11825          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
11826          * called for the new ViewHolder and the old one will be recycled.
11827          * <p>
11828          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
11829          * a good possibility that item contents didn't really change but it is rebound from the
11830          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
11831          * screen didn't change and your animator should handle this case as well and avoid creating
11832          * unnecessary animations.
11833          * <p>
11834          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
11835          * previous presentation of the item as-is and supply a new ViewHolder for the updated
11836          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
11837          * This is useful if you don't know the contents of the Item and would like
11838          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
11839          * <p>
11840          * When you are writing a custom item animator for your layout, it might be more performant
11841          * and elegant to re-use the same ViewHolder and animate the content changes manually.
11842          * <p>
11843          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
11844          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
11845          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
11846          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
11847          * which represent the same Item. In that case, only the new ViewHolder is visible
11848          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
11849          * <p>
11850          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
11851          * ViewHolder when their animation is complete
11852          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
11853          * animate the view).
11854          * <p>
11855          *  If oldHolder and newHolder are the same instance, you should call
11856          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
11857          * <p>
11858          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
11859          * animation callback method which will be called by the RecyclerView depends on the
11860          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
11861          * LayoutManager's decision whether to layout the changed version of a disappearing
11862          * ViewHolder or not. RecyclerView will call
11863          * {@code animateChange} instead of
11864          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11865          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
11866          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
11867          * LayoutManager lays out a new disappearing view that holds the updated information.
11868          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
11869          *
11870          * @param oldHolder     The ViewHolder before the layout is started, might be the same
11871          *                      instance with newHolder.
11872          * @param newHolder     The ViewHolder after the layout is finished, might be the same
11873          *                      instance with oldHolder.
11874          * @param preLayoutInfo  The information that was returned from
11875          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11876          * @param postLayoutInfo The information that was returned from {@link
11877          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
11878          *
11879          * @return true if a later call to {@link #runPendingAnimations()} is requested,
11880          * false otherwise.
11881          */
11882         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
11883                 @NonNull ViewHolder newHolder,
11884                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
11885 
buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder)11886         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
11887             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
11888             if (viewHolder.isInvalid()) {
11889                 return FLAG_INVALIDATED;
11890             }
11891             if ((flags & FLAG_INVALIDATED) == 0) {
11892                 final int oldPos = viewHolder.getOldPosition();
11893                 final int pos = viewHolder.getAdapterPosition();
11894                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
11895                     flags |= FLAG_MOVED;
11896                 }
11897             }
11898             return flags;
11899         }
11900 
11901         /**
11902          * Called when there are pending animations waiting to be started. This state
11903          * is governed by the return values from
11904          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11905          * animateAppearance()},
11906          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11907          * animateChange()}
11908          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11909          * animatePersistence()}, and
11910          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11911          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
11912          * called later to start the associated animations. runPendingAnimations() will be scheduled
11913          * to be run on the next frame.
11914          */
11915         public abstract void runPendingAnimations();
11916 
11917         /**
11918          * Method called when an animation on a view should be ended immediately.
11919          * This could happen when other events, like scrolling, occur, so that
11920          * animating views can be quickly put into their proper end locations.
11921          * Implementations should ensure that any animations running on the item
11922          * are canceled and affected properties are set to their end values.
11923          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11924          * animation since the animations are effectively done when this method is called.
11925          *
11926          * @param item The item for which an animation should be stopped.
11927          */
11928         public abstract void endAnimation(ViewHolder item);
11929 
11930         /**
11931          * Method called when all item animations should be ended immediately.
11932          * This could happen when other events, like scrolling, occur, so that
11933          * animating views can be quickly put into their proper end locations.
11934          * Implementations should ensure that any animations running on any items
11935          * are canceled and affected properties are set to their end values.
11936          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
11937          * animation since the animations are effectively done when this method is called.
11938          */
11939         public abstract void endAnimations();
11940 
11941         /**
11942          * Method which returns whether there are any item animations currently running.
11943          * This method can be used to determine whether to delay other actions until
11944          * animations end.
11945          *
11946          * @return true if there are any item animations currently running, false otherwise.
11947          */
11948         public abstract boolean isRunning();
11949 
11950         /**
11951          * Method to be called by subclasses when an animation is finished.
11952          * <p>
11953          * For each call RecyclerView makes to
11954          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11955          * animateAppearance()},
11956          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11957          * animatePersistence()}, or
11958          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11959          * animateDisappearance()}, there
11960          * should
11961          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
11962          * <p>
11963          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
11964          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
11965          * and <code>newHolder</code>  (if they are not the same instance).
11966          *
11967          * @param viewHolder The ViewHolder whose animation is finished.
11968          * @see #onAnimationFinished(ViewHolder)
11969          */
dispatchAnimationFinished(ViewHolder viewHolder)11970         public final void dispatchAnimationFinished(ViewHolder viewHolder) {
11971             onAnimationFinished(viewHolder);
11972             if (mListener != null) {
11973                 mListener.onAnimationFinished(viewHolder);
11974             }
11975         }
11976 
11977         /**
11978          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
11979          * ItemAnimator.
11980          *
11981          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
11982          *                   animations running on this ViewHolder.
11983          * @see #dispatchAnimationFinished(ViewHolder)
11984          */
onAnimationFinished(ViewHolder viewHolder)11985         public void onAnimationFinished(ViewHolder viewHolder) {
11986         }
11987 
11988         /**
11989          * Method to be called by subclasses when an animation is started.
11990          * <p>
11991          * For each call RecyclerView makes to
11992          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11993          * animateAppearance()},
11994          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11995          * animatePersistence()}, or
11996          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
11997          * animateDisappearance()}, there should be a matching
11998          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
11999          * <p>
12000          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
12001          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
12002          * and <code>newHolder</code> (if they are not the same instance).
12003          * <p>
12004          * If your ItemAnimator decides not to animate a ViewHolder, it should call
12005          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
12006          * {@link #dispatchAnimationStarted(ViewHolder)}.
12007          *
12008          * @param viewHolder The ViewHolder whose animation is starting.
12009          * @see #onAnimationStarted(ViewHolder)
12010          */
dispatchAnimationStarted(ViewHolder viewHolder)12011         public final void dispatchAnimationStarted(ViewHolder viewHolder) {
12012             onAnimationStarted(viewHolder);
12013         }
12014 
12015         /**
12016          * Called when a new animation is started on the given ViewHolder.
12017          *
12018          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
12019          *                   might already be animating and this might be another animation.
12020          * @see #dispatchAnimationStarted(ViewHolder)
12021          */
onAnimationStarted(ViewHolder viewHolder)12022         public void onAnimationStarted(ViewHolder viewHolder) {
12023 
12024         }
12025 
12026         /**
12027          * Like {@link #isRunning()}, this method returns whether there are any item
12028          * animations currently running. Additionally, the listener passed in will be called
12029          * when there are no item animations running, either immediately (before the method
12030          * returns) if no animations are currently running, or when the currently running
12031          * animations are {@link #dispatchAnimationsFinished() finished}.
12032          *
12033          * <p>Note that the listener is transient - it is either called immediately and not
12034          * stored at all, or stored only until it is called when running animations
12035          * are finished sometime later.</p>
12036          *
12037          * @param listener A listener to be called immediately if no animations are running
12038          * or later when currently-running animations have finished. A null listener is
12039          * equivalent to calling {@link #isRunning()}.
12040          * @return true if there are any item animations currently running, false otherwise.
12041          */
isRunning(ItemAnimatorFinishedListener listener)12042         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
12043             boolean running = isRunning();
12044             if (listener != null) {
12045                 if (!running) {
12046                     listener.onAnimationsFinished();
12047                 } else {
12048                     mFinishedListeners.add(listener);
12049                 }
12050             }
12051             return running;
12052         }
12053 
12054         /**
12055          * When an item is changed, ItemAnimator can decide whether it wants to re-use
12056          * the same ViewHolder for animations or RecyclerView should create a copy of the
12057          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12058          * <p>
12059          * Note that this method will only be called if the {@link ViewHolder} still has the same
12060          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12061          * both {@link ViewHolder}s in the
12062          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12063          * <p>
12064          * If your application is using change payloads, you can override
12065          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
12066          *
12067          * @param viewHolder The ViewHolder which represents the changed item's old content.
12068          *
12069          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12070          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12071          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
12072          *
12073          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
12074          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder)12075         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
12076             return true;
12077         }
12078 
12079         /**
12080          * When an item is changed, ItemAnimator can decide whether it wants to re-use
12081          * the same ViewHolder for animations or RecyclerView should create a copy of the
12082          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
12083          * <p>
12084          * Note that this method will only be called if the {@link ViewHolder} still has the same
12085          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
12086          * both {@link ViewHolder}s in the
12087          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
12088          *
12089          * @param viewHolder The ViewHolder which represents the changed item's old content.
12090          * @param payloads A non-null list of merged payloads that were sent with change
12091          *                 notifications. Can be empty if the adapter is invalidated via
12092          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
12093          *                 payloads will be passed into
12094          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
12095          *                 method <b>if</b> this method returns <code>true</code>.
12096          *
12097          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
12098          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
12099          *         ItemAnimator to animate. Default implementation calls
12100          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
12101          *
12102          * @see #canReuseUpdatedViewHolder(ViewHolder)
12103          */
canReuseUpdatedViewHolder(@onNull ViewHolder viewHolder, @NonNull List<Object> payloads)12104         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
12105                 @NonNull List<Object> payloads) {
12106             return canReuseUpdatedViewHolder(viewHolder);
12107         }
12108 
12109         /**
12110          * This method should be called by ItemAnimator implementations to notify
12111          * any listeners that all pending and active item animations are finished.
12112          */
dispatchAnimationsFinished()12113         public final void dispatchAnimationsFinished() {
12114             final int count = mFinishedListeners.size();
12115             for (int i = 0; i < count; ++i) {
12116                 mFinishedListeners.get(i).onAnimationsFinished();
12117             }
12118             mFinishedListeners.clear();
12119         }
12120 
12121         /**
12122          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
12123          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
12124          * <p>
12125          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
12126          * your own instances.
12127          *
12128          * @return A new {@link ItemHolderInfo}.
12129          */
obtainHolderInfo()12130         public ItemHolderInfo obtainHolderInfo() {
12131             return new ItemHolderInfo();
12132         }
12133 
12134         /**
12135          * The interface to be implemented by listeners to animation events from this
12136          * ItemAnimator. This is used internally and is not intended for developers to
12137          * create directly.
12138          */
12139         interface ItemAnimatorListener {
12140             void onAnimationFinished(ViewHolder item);
12141         }
12142 
12143         /**
12144          * This interface is used to inform listeners when all pending or running animations
12145          * in an ItemAnimator are finished. This can be used, for example, to delay an action
12146          * in a data set until currently-running animations are complete.
12147          *
12148          * @see #isRunning(ItemAnimatorFinishedListener)
12149          */
12150         public interface ItemAnimatorFinishedListener {
12151             /**
12152              * Notifies when all pending or running animations in an ItemAnimator are finished.
12153              */
12154             void onAnimationsFinished();
12155         }
12156 
12157         /**
12158          * A simple data structure that holds information about an item's bounds.
12159          * This information is used in calculating item animations. Default implementation of
12160          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
12161          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
12162          * structure. You can extend this class if you would like to keep more information about
12163          * the Views.
12164          * <p>
12165          * If you want to provide your own implementation but still use `super` methods to record
12166          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
12167          * instances.
12168          */
12169         public static class ItemHolderInfo {
12170 
12171             /**
12172              * The left edge of the View (excluding decorations)
12173              */
12174             public int left;
12175 
12176             /**
12177              * The top edge of the View (excluding decorations)
12178              */
12179             public int top;
12180 
12181             /**
12182              * The right edge of the View (excluding decorations)
12183              */
12184             public int right;
12185 
12186             /**
12187              * The bottom edge of the View (excluding decorations)
12188              */
12189             public int bottom;
12190 
12191             /**
12192              * The change flags that were passed to
12193              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
12194              */
12195             @AdapterChanges
12196             public int changeFlags;
12197 
ItemHolderInfo()12198             public ItemHolderInfo() {
12199             }
12200 
12201             /**
12202              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12203              * the given ViewHolder. Clears all {@link #changeFlags}.
12204              *
12205              * @param holder The ViewHolder whose bounds should be copied.
12206              * @return This {@link ItemHolderInfo}
12207              */
setFrom(RecyclerView.ViewHolder holder)12208             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
12209                 return setFrom(holder, 0);
12210             }
12211 
12212             /**
12213              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
12214              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
12215              *
12216              * @param holder The ViewHolder whose bounds should be copied.
12217              * @param flags  The adapter change flags that were passed into
12218              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
12219              *               List)}.
12220              * @return This {@link ItemHolderInfo}
12221              */
setFrom(RecyclerView.ViewHolder holder, @AdapterChanges int flags)12222             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
12223                     @AdapterChanges int flags) {
12224                 final View view = holder.itemView;
12225                 this.left = view.getLeft();
12226                 this.top = view.getTop();
12227                 this.right = view.getRight();
12228                 this.bottom = view.getBottom();
12229                 return this;
12230             }
12231         }
12232     }
12233 
12234     @Override
getChildDrawingOrder(int childCount, int i)12235     protected int getChildDrawingOrder(int childCount, int i) {
12236         if (mChildDrawingOrderCallback == null) {
12237             return super.getChildDrawingOrder(childCount, i);
12238         } else {
12239             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
12240         }
12241     }
12242 
12243     /**
12244      * A callback interface that can be used to alter the drawing order of RecyclerView children.
12245      * <p>
12246      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
12247      * that applies to that method also applies to this callback. For example, changing the drawing
12248      * order of two views will not have any effect if their elevation values are different since
12249      * elevation overrides the result of this callback.
12250      */
12251     public interface ChildDrawingOrderCallback {
12252         /**
12253          * Returns the index of the child to draw for this iteration. Override this
12254          * if you want to change the drawing order of children. By default, it
12255          * returns i.
12256          *
12257          * @param i The current iteration.
12258          * @return The index of the child to draw this iteration.
12259          *
12260          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
12261          */
12262         int onGetChildDrawingOrder(int childCount, int i);
12263     }
12264 }
12265