Home | History | Annotate | Download | only in widget
      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.content.Context;
     24 import android.content.res.TypedArray;
     25 import android.database.Observable;
     26 import android.graphics.Canvas;
     27 import android.graphics.Matrix;
     28 import android.graphics.PointF;
     29 import android.graphics.Rect;
     30 import android.graphics.RectF;
     31 import android.os.Build;
     32 import android.os.Bundle;
     33 import android.os.Parcel;
     34 import android.os.Parcelable;
     35 import android.os.SystemClock;
     36 import android.os.Trace;
     37 import android.util.AttributeSet;
     38 import android.util.Log;
     39 import android.util.SparseArray;
     40 import android.util.TypedValue;
     41 import android.view.AbsSavedState;
     42 import android.view.Display;
     43 import android.view.FocusFinder;
     44 import android.view.InputDevice;
     45 import android.view.MotionEvent;
     46 import android.view.VelocityTracker;
     47 import android.view.View;
     48 import android.view.ViewConfiguration;
     49 import android.view.ViewGroup;
     50 import android.view.ViewParent;
     51 import android.view.accessibility.AccessibilityEvent;
     52 import android.view.accessibility.AccessibilityManager;
     53 import android.view.accessibility.AccessibilityNodeInfo;
     54 import android.view.animation.Interpolator;
     55 import android.widget.EdgeEffect;
     56 import android.widget.OverScroller;
     57 
     58 import com.android.internal.R;
     59 import com.android.internal.annotations.VisibleForTesting;
     60 import com.android.internal.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
     61 
     62 import java.lang.annotation.Retention;
     63 import java.lang.annotation.RetentionPolicy;
     64 import java.lang.ref.WeakReference;
     65 import java.lang.reflect.Constructor;
     66 import java.lang.reflect.InvocationTargetException;
     67 import java.util.ArrayList;
     68 import java.util.Collections;
     69 import java.util.List;
     70 
     71 /**
     72  * A flexible view for providing a limited window into a large data set.
     73  *
     74  * <h3>Glossary of terms:</h3>
     75  *
     76  * <ul>
     77  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
     78  *     that represent items in a data set.</li>
     79  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
     80  *     <li><em>Index:</em> The index of an attached child view as used in a call to
     81  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
     82  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
     83  *     to a <em>position</em> within the adapter.</li>
     84  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
     85  *     position may be placed in a cache for later reuse to display the same type of data again
     86  *     later. This can drastically improve performance by skipping initial layout inflation
     87  *     or construction.</li>
     88  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
     89  *     state during layout. Scrap views may be reused without becoming fully detached
     90  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
     91  *     by the adapter if the view was considered <em>dirty</em>.</li>
     92  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
     93  *     being displayed.</li>
     94  * </ul>
     95  *
     96  * <h4>Positions in RecyclerView:</h4>
     97  * <p>
     98  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
     99  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
    100  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
    101  * It also helps with performance because all view bindings happen at the same time and unnecessary
    102  * bindings are avoided.
    103  * <p>
    104  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
    105  * <ul>
    106  *     <li>layout position: Position of an item in the latest layout calculation. This is the
    107  *     position from the LayoutManager's perspective.</li>
    108  *     <li>adapter position: Position of an item in the adapter. This is the position from
    109  *     the Adapter's perspective.</li>
    110  * </ul>
    111  * <p>
    112  * These two positions are the same except the time between dispatching <code>adapter.notify*
    113  * </code> events and calculating the updated layout.
    114  * <p>
    115  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
    116  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
    117  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
    118  * last layout calculation. You can rely on these positions to be consistent with what user is
    119  * currently seeing on the screen. For example, if you have a list of items on the screen and user
    120  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
    121  * is seeing.
    122  * <p>
    123  * The other set of position related methods are in the form of
    124  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
    125  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
    126  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
    127  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
    128  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
    129  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
    130  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
    131  * <code>null</code> results from these methods.
    132  * <p>
    133  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
    134  * writing an {@link Adapter}, you probably want to use adapter positions.
    135  *
    136  * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
    137  */
    138 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
    139 
    140     static final String TAG = "RecyclerView";
    141 
    142     static final boolean DEBUG = false;
    143 
    144     private static final int[]  NESTED_SCROLLING_ATTRS = { android.R.attr.nestedScrollingEnabled };
    145 
    146     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
    147 
    148     /**
    149      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
    150      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
    151      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
    152      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
    153      * this criteria.
    154      */
    155     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
    156             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
    157     /**
    158      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
    159      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
    160      * 0 when mode is unspecified.
    161      */
    162     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
    163 
    164     static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
    165 
    166     /**
    167      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
    168      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
    169      */
    170     private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
    171 
    172     /**
    173      * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
    174      * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
    175      */
    176     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
    177 
    178     /**
    179      * on API 15-, a focused child can still be considered a focused child of RV even after
    180      * it's being removed or its focusable flag is set to false. This is because when this focused
    181      * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
    182      * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
    183      * to request focus on a new child, which will clear the focus on the old (detached) child as a
    184      * side-effect.
    185      */
    186     private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
    187 
    188     static final boolean DISPATCH_TEMP_DETACH = false;
    189     public static final int HORIZONTAL = 0;
    190     public static final int VERTICAL = 1;
    191 
    192     public static final int NO_POSITION = -1;
    193     public static final long NO_ID = -1;
    194     public static final int INVALID_TYPE = -1;
    195 
    196     /**
    197      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    198      * that the RecyclerView should use the standard touch slop for smooth,
    199      * continuous scrolling.
    200      */
    201     public static final int TOUCH_SLOP_DEFAULT = 0;
    202 
    203     /**
    204      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    205      * that the RecyclerView should use the standard touch slop for scrolling
    206      * widgets that snap to a page or other coarse-grained barrier.
    207      */
    208     public static final int TOUCH_SLOP_PAGING = 1;
    209 
    210     static final int MAX_SCROLL_DURATION = 2000;
    211 
    212     /**
    213      * RecyclerView is calculating a scroll.
    214      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
    215      * it. Try to avoid using EditText, focusable views or handle them with care.
    216      */
    217     static final String TRACE_SCROLL_TAG = "RV Scroll";
    218 
    219     /**
    220      * OnLayout has been called by the View system.
    221      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
    222      * update themselves directly. This will cause a full re-layout but when it happens via the
    223      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
    224      */
    225     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
    226 
    227     /**
    228      * NotifyDataSetChanged or equal has been called.
    229      * If this is taking a long time, try sending granular notify adapter changes instead of just
    230      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
    231      * might help.
    232      */
    233     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
    234 
    235     /**
    236      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
    237      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
    238      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
    239      * methods.
    240      */
    241     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
    242 
    243     /**
    244      * RecyclerView is rebinding a View.
    245      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
    246      * doing extra operations in onBindViewHolder call.
    247      */
    248     static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
    249 
    250     /**
    251      * RecyclerView is attempting to pre-populate off screen views.
    252      */
    253     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
    254 
    255     /**
    256      * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
    257      * RecyclerView.
    258      */
    259     static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
    260 
    261     /**
    262      * RecyclerView is creating a new View.
    263      * If too many of these present in Systrace:
    264      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
    265      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
    266      * > Adapter#onFailedToRecycleView(ViewHolder)})
    267      *
    268      * - There might be too many item view types.
    269      * > Try merging them
    270      *
    271      * - There might be too many itemChange animations and not enough space in RecyclerPool.
    272      * >Try increasing your pool size and item cache size.
    273      */
    274     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
    275     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
    276             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
    277 
    278     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
    279 
    280     final Recycler mRecycler = new Recycler();
    281 
    282     private SavedState mPendingSavedState;
    283 
    284     /**
    285      * Handles adapter updates
    286      */
    287     AdapterHelper mAdapterHelper;
    288 
    289     /**
    290      * Handles abstraction between LayoutManager children and RecyclerView children
    291      */
    292     ChildHelper mChildHelper;
    293 
    294     /**
    295      * Keeps data about views to be used for animations
    296      */
    297     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
    298 
    299     /**
    300      * Prior to L, there is no way to query this variable which is why we override the setter and
    301      * track it here.
    302      */
    303     boolean mClipToPadding;
    304 
    305     /**
    306      * Note: this Runnable is only ever posted if:
    307      * 1) We've been through first layout
    308      * 2) We know we have a fixed size (mHasFixedSize)
    309      * 3) We're attached
    310      */
    311     final Runnable mUpdateChildViewsRunnable = new Runnable() {
    312         @Override
    313         public void run() {
    314             if (!mFirstLayoutComplete || isLayoutRequested()) {
    315                 // a layout request will happen, we should not do layout here.
    316                 return;
    317             }
    318             if (!mIsAttached) {
    319                 requestLayout();
    320                 // if we are not attached yet, mark us as requiring layout and skip
    321                 return;
    322             }
    323             if (mLayoutFrozen) {
    324                 mLayoutRequestEaten = true;
    325                 return; //we'll process updates when ice age ends.
    326             }
    327             consumePendingUpdateOperations();
    328         }
    329     };
    330 
    331     final Rect mTempRect = new Rect();
    332     private final Rect mTempRect2 = new Rect();
    333     final RectF mTempRectF = new RectF();
    334     Adapter mAdapter;
    335     @VisibleForTesting LayoutManager mLayout;
    336     RecyclerListener mRecyclerListener;
    337     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
    338     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
    339             new ArrayList<>();
    340     private OnItemTouchListener mActiveOnItemTouchListener;
    341     boolean mIsAttached;
    342     boolean mHasFixedSize;
    343     @VisibleForTesting boolean mFirstLayoutComplete;
    344 
    345     // Counting lock to control whether we should ignore requestLayout calls from children or not.
    346     private int mEatRequestLayout = 0;
    347 
    348     boolean mLayoutRequestEaten;
    349     boolean mLayoutFrozen;
    350     private boolean mIgnoreMotionEventTillDown;
    351 
    352     // binary OR of change events that were eaten during a layout or scroll.
    353     private int mEatenAccessibilityChangeFlags;
    354     boolean mAdapterUpdateDuringMeasure;
    355 
    356     private final AccessibilityManager mAccessibilityManager;
    357     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
    358 
    359     /**
    360      * Set to true when an adapter data set changed notification is received.
    361      * In that case, we cannot run any animations since we don't know what happened until layout.
    362      *
    363      * Attached items are invalid until next layout, at which point layout will animate/replace
    364      * items as necessary, building up content from the (effectively) new adapter from scratch.
    365      *
    366      * Cached items must be discarded when setting this to true, so that the cache may be freely
    367      * used by prefetching until the next layout occurs.
    368      *
    369      * @see #setDataSetChangedAfterLayout()
    370      */
    371     boolean mDataSetHasChangedAfterLayout = false;
    372 
    373     /**
    374      * This variable is incremented during a dispatchLayout and/or scroll.
    375      * Some methods should not be called during these periods (e.g. adapter data change).
    376      * Doing so will create hard to find bugs so we better check it and throw an exception.
    377      *
    378      * @see #assertInLayoutOrScroll(String)
    379      * @see #assertNotInLayoutOrScroll(String)
    380      */
    381     private int mLayoutOrScrollCounter = 0;
    382 
    383     /**
    384      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
    385      * (for API compatibility).
    386      * <p>
    387      * It is a bad practice for a developer to update the data in a scroll callback since it is
    388      * potentially called during a layout.
    389      */
    390     private int mDispatchScrollCounter = 0;
    391 
    392     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
    393 
    394     ItemAnimator mItemAnimator = new DefaultItemAnimator();
    395 
    396     private static final int INVALID_POINTER = -1;
    397 
    398     /**
    399      * The RecyclerView is not currently scrolling.
    400      * @see #getScrollState()
    401      */
    402     public static final int SCROLL_STATE_IDLE = 0;
    403 
    404     /**
    405      * The RecyclerView is currently being dragged by outside input such as user touch input.
    406      * @see #getScrollState()
    407      */
    408     public static final int SCROLL_STATE_DRAGGING = 1;
    409 
    410     /**
    411      * The RecyclerView is currently animating to a final position while not under
    412      * outside control.
    413      * @see #getScrollState()
    414      */
    415     public static final int SCROLL_STATE_SETTLING = 2;
    416 
    417     static final long FOREVER_NS = Long.MAX_VALUE;
    418 
    419     // Touch/scrolling handling
    420 
    421     private int mScrollState = SCROLL_STATE_IDLE;
    422     private int mScrollPointerId = INVALID_POINTER;
    423     private VelocityTracker mVelocityTracker;
    424     private int mInitialTouchX;
    425     private int mInitialTouchY;
    426     private int mLastTouchX;
    427     private int mLastTouchY;
    428     private int mTouchSlop;
    429     private OnFlingListener mOnFlingListener;
    430     private final int mMinFlingVelocity;
    431     private final int mMaxFlingVelocity;
    432     // This value is used when handling generic motion events.
    433     private float mScrollFactor = Float.MIN_VALUE;
    434     private boolean mPreserveFocusAfterLayout = true;
    435 
    436     final ViewFlinger mViewFlinger = new ViewFlinger();
    437 
    438     GapWorker mGapWorker;
    439     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
    440             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
    441 
    442     final State mState = new State();
    443 
    444     private OnScrollListener mScrollListener;
    445     private List<OnScrollListener> mScrollListeners;
    446 
    447     // For use in item animations
    448     boolean mItemsAddedOrRemoved = false;
    449     boolean mItemsChanged = false;
    450     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
    451             new ItemAnimatorRestoreListener();
    452     boolean mPostedAnimatorRunner = false;
    453     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
    454     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
    455 
    456     // simple array to keep min and max child position during a layout calculation
    457     // preserved not to create a new one in each layout pass
    458     private final int[] mMinMaxLayoutPositions = new int[2];
    459 
    460     private final int[] mScrollOffset = new int[2];
    461     private final int[] mScrollConsumed = new int[2];
    462     private final int[] mNestedOffsets = new int[2];
    463 
    464     /**
    465      * These are views that had their a11y importance changed during a layout. We defer these events
    466      * until the end of the layout because a11y service may make sync calls back to the RV while
    467      * the View's state is undefined.
    468      */
    469     @VisibleForTesting
    470     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
    471 
    472     private Runnable mItemAnimatorRunner = new Runnable() {
    473         @Override
    474         public void run() {
    475             if (mItemAnimator != null) {
    476                 mItemAnimator.runPendingAnimations();
    477             }
    478             mPostedAnimatorRunner = false;
    479         }
    480     };
    481 
    482     static final Interpolator sQuinticInterpolator = new Interpolator() {
    483         @Override
    484         public float getInterpolation(float t) {
    485             t -= 1.0f;
    486             return t * t * t * t * t + 1.0f;
    487         }
    488     };
    489 
    490     /**
    491      * The callback to convert view info diffs into animations.
    492      */
    493     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
    494             new ViewInfoStore.ProcessCallback() {
    495         @Override
    496         public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
    497                 @Nullable ItemHolderInfo postInfo) {
    498             mRecycler.unscrapView(viewHolder);
    499             animateDisappearance(viewHolder, info, postInfo);
    500         }
    501         @Override
    502         public void processAppeared(ViewHolder viewHolder,
    503                 ItemHolderInfo preInfo, ItemHolderInfo info) {
    504             animateAppearance(viewHolder, preInfo, info);
    505         }
    506 
    507         @Override
    508         public void processPersistent(ViewHolder viewHolder,
    509                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
    510             viewHolder.setIsRecyclable(false);
    511             if (mDataSetHasChangedAfterLayout) {
    512                 // since it was rebound, use change instead as we'll be mapping them from
    513                 // stable ids. If stable ids were false, we would not be running any
    514                 // animations
    515                 if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
    516                     postAnimationRunner();
    517                 }
    518             } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
    519                 postAnimationRunner();
    520             }
    521         }
    522         @Override
    523         public void unused(ViewHolder viewHolder) {
    524             mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
    525         }
    526     };
    527 
    528     public RecyclerView(Context context) {
    529         this(context, null);
    530     }
    531 
    532     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
    533         this(context, attrs, 0);
    534     }
    535 
    536     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    537         super(context, attrs, defStyle);
    538         if (attrs != null) {
    539             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
    540             mClipToPadding = a.getBoolean(0, true);
    541             a.recycle();
    542         } else {
    543             mClipToPadding = true;
    544         }
    545         setScrollContainer(true);
    546         setFocusableInTouchMode(true);
    547 
    548         final ViewConfiguration vc = ViewConfiguration.get(context);
    549         mTouchSlop = vc.getScaledTouchSlop();
    550         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
    551         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
    552         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
    553 
    554         mItemAnimator.setListener(mItemAnimatorListener);
    555         initAdapterManager();
    556         initChildrenHelper();
    557         // If not explicitly specified this view is important for accessibility.
    558         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    559             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    560         }
    561         mAccessibilityManager = (AccessibilityManager) getContext()
    562                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
    563         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
    564         // Create the layoutManager if specified.
    565 
    566         boolean nestedScrollingEnabled = true;
    567 
    568         if (attrs != null) {
    569             int defStyleRes = 0;
    570             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
    571                     defStyle, defStyleRes);
    572             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
    573             int descendantFocusability = a.getInt(
    574                     R.styleable.RecyclerView_descendantFocusability, -1);
    575             if (descendantFocusability == -1) {
    576                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    577             }
    578             a.recycle();
    579             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
    580 
    581             if (Build.VERSION.SDK_INT >= 21) {
    582                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
    583                         defStyle, defStyleRes);
    584                 nestedScrollingEnabled = a.getBoolean(0, true);
    585                 a.recycle();
    586             }
    587         } else {
    588             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    589         }
    590 
    591         // Re-set whether nested scrolling is enabled so that it is set on all API levels
    592         setNestedScrollingEnabled(nestedScrollingEnabled);
    593     }
    594 
    595     /**
    596      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
    597      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
    598      */
    599     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
    600         return mAccessibilityDelegate;
    601     }
    602 
    603     /**
    604      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
    605      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
    606      */
    607     public void setAccessibilityDelegateCompat(
    608             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
    609         mAccessibilityDelegate = accessibilityDelegate;
    610         setAccessibilityDelegate(mAccessibilityDelegate);
    611     }
    612 
    613     /**
    614      * Instantiate and set a LayoutManager, if specified in the attributes.
    615      */
    616     private void createLayoutManager(Context context, String className, AttributeSet attrs,
    617             int defStyleAttr, int defStyleRes) {
    618         if (className != null) {
    619             className = className.trim();
    620             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
    621                 className = getFullClassName(context, className);
    622                 try {
    623                     ClassLoader classLoader;
    624                     if (isInEditMode()) {
    625                         // Stupid layoutlib cannot handle simple class loaders.
    626                         classLoader = this.getClass().getClassLoader();
    627                     } else {
    628                         classLoader = context.getClassLoader();
    629                     }
    630                     Class<? extends LayoutManager> layoutManagerClass =
    631                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
    632                     Constructor<? extends LayoutManager> constructor;
    633                     Object[] constructorArgs = null;
    634                     try {
    635                         constructor = layoutManagerClass
    636                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
    637                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
    638                     } catch (NoSuchMethodException e) {
    639                         try {
    640                             constructor = layoutManagerClass.getConstructor();
    641                         } catch (NoSuchMethodException e1) {
    642                             e1.initCause(e);
    643                             throw new IllegalStateException(attrs.getPositionDescription()
    644                                     + ": Error creating LayoutManager " + className, e1);
    645                         }
    646                     }
    647                     constructor.setAccessible(true);
    648                     setLayoutManager(constructor.newInstance(constructorArgs));
    649                 } catch (ClassNotFoundException e) {
    650                     throw new IllegalStateException(attrs.getPositionDescription()
    651                             + ": Unable to find LayoutManager " + className, e);
    652                 } catch (InvocationTargetException e) {
    653                     throw new IllegalStateException(attrs.getPositionDescription()
    654                             + ": Could not instantiate the LayoutManager: " + className, e);
    655                 } catch (InstantiationException e) {
    656                     throw new IllegalStateException(attrs.getPositionDescription()
    657                             + ": Could not instantiate the LayoutManager: " + className, e);
    658                 } catch (IllegalAccessException e) {
    659                     throw new IllegalStateException(attrs.getPositionDescription()
    660                             + ": Cannot access non-public constructor " + className, e);
    661                 } catch (ClassCastException e) {
    662                     throw new IllegalStateException(attrs.getPositionDescription()
    663                             + ": Class is not a LayoutManager " + className, e);
    664                 }
    665             }
    666         }
    667     }
    668 
    669     private String getFullClassName(Context context, String className) {
    670         if (className.charAt(0) == '.') {
    671             return context.getPackageName() + className;
    672         }
    673         if (className.contains(".")) {
    674             return className;
    675         }
    676         return RecyclerView.class.getPackage().getName() + '.' + className;
    677     }
    678 
    679     private void initChildrenHelper() {
    680         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
    681             @Override
    682             public int getChildCount() {
    683                 return RecyclerView.this.getChildCount();
    684             }
    685 
    686             @Override
    687             public void addView(View child, int index) {
    688                 RecyclerView.this.addView(child, index);
    689                 dispatchChildAttached(child);
    690             }
    691 
    692             @Override
    693             public int indexOfChild(View view) {
    694                 return RecyclerView.this.indexOfChild(view);
    695             }
    696 
    697             @Override
    698             public void removeViewAt(int index) {
    699                 final View child = RecyclerView.this.getChildAt(index);
    700                 if (child != null) {
    701                     dispatchChildDetached(child);
    702                 }
    703                 RecyclerView.this.removeViewAt(index);
    704             }
    705 
    706             @Override
    707             public View getChildAt(int offset) {
    708                 return RecyclerView.this.getChildAt(offset);
    709             }
    710 
    711             @Override
    712             public void removeAllViews() {
    713                 final int count = getChildCount();
    714                 for (int i = 0; i < count; i++) {
    715                     dispatchChildDetached(getChildAt(i));
    716                 }
    717                 RecyclerView.this.removeAllViews();
    718             }
    719 
    720             @Override
    721             public ViewHolder getChildViewHolder(View view) {
    722                 return getChildViewHolderInt(view);
    723             }
    724 
    725             @Override
    726             public void attachViewToParent(View child, int index,
    727                     ViewGroup.LayoutParams layoutParams) {
    728                 final ViewHolder vh = getChildViewHolderInt(child);
    729                 if (vh != null) {
    730                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
    731                         throw new IllegalArgumentException("Called attach on a child which is not"
    732                                 + " detached: " + vh);
    733                     }
    734                     if (DEBUG) {
    735                         Log.d(TAG, "reAttach " + vh);
    736                     }
    737                     vh.clearTmpDetachFlag();
    738                 }
    739                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
    740             }
    741 
    742             @Override
    743             public void detachViewFromParent(int offset) {
    744                 final View view = getChildAt(offset);
    745                 if (view != null) {
    746                     final ViewHolder vh = getChildViewHolderInt(view);
    747                     if (vh != null) {
    748                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
    749                             throw new IllegalArgumentException("called detach on an already"
    750                                     + " detached child " + vh);
    751                         }
    752                         if (DEBUG) {
    753                             Log.d(TAG, "tmpDetach " + vh);
    754                         }
    755                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
    756                     }
    757                 }
    758                 RecyclerView.this.detachViewFromParent(offset);
    759             }
    760 
    761             @Override
    762             public void onEnteredHiddenState(View child) {
    763                 final ViewHolder vh = getChildViewHolderInt(child);
    764                 if (vh != null) {
    765                     vh.onEnteredHiddenState(RecyclerView.this);
    766                 }
    767             }
    768 
    769             @Override
    770             public void onLeftHiddenState(View child) {
    771                 final ViewHolder vh = getChildViewHolderInt(child);
    772                 if (vh != null) {
    773                     vh.onLeftHiddenState(RecyclerView.this);
    774                 }
    775             }
    776         });
    777     }
    778 
    779     void initAdapterManager() {
    780         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
    781             @Override
    782             public ViewHolder findViewHolder(int position) {
    783                 final ViewHolder vh = findViewHolderForPosition(position, true);
    784                 if (vh == null) {
    785                     return null;
    786                 }
    787                 // ensure it is not hidden because for adapter helper, the only thing matter is that
    788                 // LM thinks view is a child.
    789                 if (mChildHelper.isHidden(vh.itemView)) {
    790                     if (DEBUG) {
    791                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
    792                     }
    793                     return null;
    794                 }
    795                 return vh;
    796             }
    797 
    798             @Override
    799             public void offsetPositionsForRemovingInvisible(int start, int count) {
    800                 offsetPositionRecordsForRemove(start, count, true);
    801                 mItemsAddedOrRemoved = true;
    802                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
    803             }
    804 
    805             @Override
    806             public void offsetPositionsForRemovingLaidOutOrNewView(
    807                     int positionStart, int itemCount) {
    808                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
    809                 mItemsAddedOrRemoved = true;
    810             }
    811 
    812             @Override
    813             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
    814                 viewRangeUpdate(positionStart, itemCount, payload);
    815                 mItemsChanged = true;
    816             }
    817 
    818             @Override
    819             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
    820                 dispatchUpdate(op);
    821             }
    822 
    823             void dispatchUpdate(AdapterHelper.UpdateOp op) {
    824                 switch (op.cmd) {
    825                     case AdapterHelper.UpdateOp.ADD:
    826                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
    827                         break;
    828                     case AdapterHelper.UpdateOp.REMOVE:
    829                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
    830                         break;
    831                     case AdapterHelper.UpdateOp.UPDATE:
    832                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
    833                                 op.payload);
    834                         break;
    835                     case AdapterHelper.UpdateOp.MOVE:
    836                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
    837                         break;
    838                 }
    839             }
    840 
    841             @Override
    842             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
    843                 dispatchUpdate(op);
    844             }
    845 
    846             @Override
    847             public void offsetPositionsForAdd(int positionStart, int itemCount) {
    848                 offsetPositionRecordsForInsert(positionStart, itemCount);
    849                 mItemsAddedOrRemoved = true;
    850             }
    851 
    852             @Override
    853             public void offsetPositionsForMove(int from, int to) {
    854                 offsetPositionRecordsForMove(from, to);
    855                 // should we create mItemsMoved ?
    856                 mItemsAddedOrRemoved = true;
    857             }
    858         });
    859     }
    860 
    861     /**
    862      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
    863      * size is not affected by the adapter contents. RecyclerView can still change its size based
    864      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
    865      * size of its children or contents of its adapter (except the number of items in the adapter).
    866      * <p>
    867      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
    868      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
    869      *
    870      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
    871      */
    872     public void setHasFixedSize(boolean hasFixedSize) {
    873         mHasFixedSize = hasFixedSize;
    874     }
    875 
    876     /**
    877      * @return true if the app has specified that changes in adapter content cannot change
    878      * the size of the RecyclerView itself.
    879      */
    880     public boolean hasFixedSize() {
    881         return mHasFixedSize;
    882     }
    883 
    884     @Override
    885     public void setClipToPadding(boolean clipToPadding) {
    886         if (clipToPadding != mClipToPadding) {
    887             invalidateGlows();
    888         }
    889         mClipToPadding = clipToPadding;
    890         super.setClipToPadding(clipToPadding);
    891         if (mFirstLayoutComplete) {
    892             requestLayout();
    893         }
    894     }
    895 
    896     /**
    897      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
    898      * not clip) any EdgeEffect to the padded region, if padding is present.
    899      * <p>
    900      * By default, children are clipped to the padding of their parent
    901      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
    902      *
    903      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
    904      *         clip) any EdgeEffect to the padded region, false otherwise.
    905      *
    906      * @attr name android:clipToPadding
    907      */
    908     @Override
    909     public boolean getClipToPadding() {
    910         return mClipToPadding;
    911     }
    912 
    913     /**
    914      * Configure the scrolling touch slop for a specific use case.
    915      *
    916      * Set up the RecyclerView's scrolling motion threshold based on common usages.
    917      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
    918      *
    919      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
    920      *                     the intended usage of this RecyclerView
    921      */
    922     public void setScrollingTouchSlop(int slopConstant) {
    923         final ViewConfiguration vc = ViewConfiguration.get(getContext());
    924         switch (slopConstant) {
    925             default:
    926                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
    927                         + slopConstant + "; using default value");
    928                 // fall-through
    929             case TOUCH_SLOP_DEFAULT:
    930                 mTouchSlop = vc.getScaledTouchSlop();
    931                 break;
    932 
    933             case TOUCH_SLOP_PAGING:
    934                 mTouchSlop = vc.getScaledPagingTouchSlop();
    935                 break;
    936         }
    937     }
    938 
    939     /**
    940      * Swaps the current adapter with the provided one. It is similar to
    941      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
    942      * {@link ViewHolder} and does not clear the RecycledViewPool.
    943      * <p>
    944      * Note that it still calls onAdapterChanged callbacks.
    945      *
    946      * @param adapter The new adapter to set, or null to set no adapter.
    947      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
    948      *                                      Views. If adapters have stable ids and/or you want to
    949      *                                      animate the disappearing views, you may prefer to set
    950      *                                      this to false.
    951      * @see #setAdapter(Adapter)
    952      */
    953     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
    954         // bail out if layout is frozen
    955         setLayoutFrozen(false);
    956         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
    957         setDataSetChangedAfterLayout();
    958         requestLayout();
    959     }
    960     /**
    961      * Set a new adapter to provide child views on demand.
    962      * <p>
    963      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
    964      * only one adapter, it will be cleared.
    965      *
    966      * @param adapter The new adapter to set, or null to set no adapter.
    967      * @see #swapAdapter(Adapter, boolean)
    968      */
    969     public void setAdapter(Adapter adapter) {
    970         // bail out if layout is frozen
    971         setLayoutFrozen(false);
    972         setAdapterInternal(adapter, false, true);
    973         requestLayout();
    974     }
    975 
    976     /**
    977      * Removes and recycles all views - both those currently attached, and those in the Recycler.
    978      */
    979     void removeAndRecycleViews() {
    980         // end all running animations
    981         if (mItemAnimator != null) {
    982             mItemAnimator.endAnimations();
    983         }
    984         // Since animations are ended, mLayout.children should be equal to
    985         // recyclerView.children. This may not be true if item animator's end does not work as
    986         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
    987         // count.
    988         if (mLayout != null) {
    989             mLayout.removeAndRecycleAllViews(mRecycler);
    990             mLayout.removeAndRecycleScrapInt(mRecycler);
    991         }
    992         // we should clear it here before adapters are swapped to ensure correct callbacks.
    993         mRecycler.clear();
    994     }
    995 
    996     /**
    997      * Replaces the current adapter with the new one and triggers listeners.
    998      * @param adapter The new adapter
    999      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
   1000      *                               item types with the current adapter (helps us avoid cache
   1001      *                               invalidation).
   1002      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
   1003      *                               compatibleWithPrevious is false, this parameter is ignored.
   1004      */
   1005     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
   1006             boolean removeAndRecycleViews) {
   1007         if (mAdapter != null) {
   1008             mAdapter.unregisterAdapterDataObserver(mObserver);
   1009             mAdapter.onDetachedFromRecyclerView(this);
   1010         }
   1011         if (!compatibleWithPrevious || removeAndRecycleViews) {
   1012             removeAndRecycleViews();
   1013         }
   1014         mAdapterHelper.reset();
   1015         final Adapter oldAdapter = mAdapter;
   1016         mAdapter = adapter;
   1017         if (adapter != null) {
   1018             adapter.registerAdapterDataObserver(mObserver);
   1019             adapter.onAttachedToRecyclerView(this);
   1020         }
   1021         if (mLayout != null) {
   1022             mLayout.onAdapterChanged(oldAdapter, mAdapter);
   1023         }
   1024         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
   1025         mState.mStructureChanged = true;
   1026         markKnownViewsInvalid();
   1027     }
   1028 
   1029     /**
   1030      * Retrieves the previously set adapter or null if no adapter is set.
   1031      *
   1032      * @return The previously set adapter
   1033      * @see #setAdapter(Adapter)
   1034      */
   1035     public Adapter getAdapter() {
   1036         return mAdapter;
   1037     }
   1038 
   1039     /**
   1040      * Register a listener that will be notified whenever a child view is recycled.
   1041      *
   1042      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
   1043      * that a child view is no longer needed. If an application associates expensive
   1044      * or heavyweight data with item views, this may be a good place to release
   1045      * or free those resources.</p>
   1046      *
   1047      * @param listener Listener to register, or null to clear
   1048      */
   1049     public void setRecyclerListener(RecyclerListener listener) {
   1050         mRecyclerListener = listener;
   1051     }
   1052 
   1053     /**
   1054      * <p>Return the offset of the RecyclerView's text baseline from the its top
   1055      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
   1056      * this method returns -1.</p>
   1057      *
   1058      * @return the offset of the baseline within the RecyclerView's bounds or -1
   1059      *         if baseline alignment is not supported
   1060      */
   1061     @Override
   1062     public int getBaseline() {
   1063         if (mLayout != null) {
   1064             return mLayout.getBaseline();
   1065         } else {
   1066             return super.getBaseline();
   1067         }
   1068     }
   1069 
   1070     /**
   1071      * Register a listener that will be notified whenever a child view is attached to or detached
   1072      * from RecyclerView.
   1073      *
   1074      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
   1075      * that a child view is no longer needed. If an application associates expensive
   1076      * or heavyweight data with item views, this may be a good place to release
   1077      * or free those resources.</p>
   1078      *
   1079      * @param listener Listener to register
   1080      */
   1081     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
   1082         if (mOnChildAttachStateListeners == null) {
   1083             mOnChildAttachStateListeners = new ArrayList<>();
   1084         }
   1085         mOnChildAttachStateListeners.add(listener);
   1086     }
   1087 
   1088     /**
   1089      * Removes the provided listener from child attached state listeners list.
   1090      *
   1091      * @param listener Listener to unregister
   1092      */
   1093     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
   1094         if (mOnChildAttachStateListeners == null) {
   1095             return;
   1096         }
   1097         mOnChildAttachStateListeners.remove(listener);
   1098     }
   1099 
   1100     /**
   1101      * Removes all listeners that were added via
   1102      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
   1103      */
   1104     public void clearOnChildAttachStateChangeListeners() {
   1105         if (mOnChildAttachStateListeners != null) {
   1106             mOnChildAttachStateListeners.clear();
   1107         }
   1108     }
   1109 
   1110     /**
   1111      * Set the {@link LayoutManager} that this RecyclerView will use.
   1112      *
   1113      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
   1114      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
   1115      * layout arrangements for child views. These arrangements are controlled by the
   1116      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
   1117      *
   1118      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
   1119      *
   1120      * @param layout LayoutManager to use
   1121      */
   1122     public void setLayoutManager(LayoutManager layout) {
   1123         if (layout == mLayout) {
   1124             return;
   1125         }
   1126         stopScroll();
   1127         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
   1128         // chance that LayoutManagers will re-use views.
   1129         if (mLayout != null) {
   1130             // end all running animations
   1131             if (mItemAnimator != null) {
   1132                 mItemAnimator.endAnimations();
   1133             }
   1134             mLayout.removeAndRecycleAllViews(mRecycler);
   1135             mLayout.removeAndRecycleScrapInt(mRecycler);
   1136             mRecycler.clear();
   1137 
   1138             if (mIsAttached) {
   1139                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
   1140             }
   1141             mLayout.setRecyclerView(null);
   1142             mLayout = null;
   1143         } else {
   1144             mRecycler.clear();
   1145         }
   1146         // this is just a defensive measure for faulty item animators.
   1147         mChildHelper.removeAllViewsUnfiltered();
   1148         mLayout = layout;
   1149         if (layout != null) {
   1150             if (layout.mRecyclerView != null) {
   1151                 throw new IllegalArgumentException("LayoutManager " + layout
   1152                         + " is already attached to a RecyclerView: " + layout.mRecyclerView);
   1153             }
   1154             mLayout.setRecyclerView(this);
   1155             if (mIsAttached) {
   1156                 mLayout.dispatchAttachedToWindow(this);
   1157             }
   1158         }
   1159         mRecycler.updateViewCacheSize();
   1160         requestLayout();
   1161     }
   1162 
   1163     /**
   1164      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
   1165      * <p>
   1166      * If the {@link OnFlingListener} is set then it will receive
   1167      * calls to {@link #fling(int,int)} and will be able to intercept them.
   1168      *
   1169      * @param onFlingListener The {@link OnFlingListener} instance.
   1170      */
   1171     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
   1172         mOnFlingListener = onFlingListener;
   1173     }
   1174 
   1175     /**
   1176      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
   1177      *
   1178      * @return The {@link OnFlingListener} instance currently set (can be null).
   1179      */
   1180     @Nullable
   1181     public OnFlingListener getOnFlingListener() {
   1182         return mOnFlingListener;
   1183     }
   1184 
   1185     @Override
   1186     protected Parcelable onSaveInstanceState() {
   1187         SavedState state = new SavedState(super.onSaveInstanceState());
   1188         if (mPendingSavedState != null) {
   1189             state.copyFrom(mPendingSavedState);
   1190         } else if (mLayout != null) {
   1191             state.mLayoutState = mLayout.onSaveInstanceState();
   1192         } else {
   1193             state.mLayoutState = null;
   1194         }
   1195 
   1196         return state;
   1197     }
   1198 
   1199     @Override
   1200     protected void onRestoreInstanceState(Parcelable state) {
   1201         if (!(state instanceof SavedState)) {
   1202             super.onRestoreInstanceState(state);
   1203             return;
   1204         }
   1205 
   1206         mPendingSavedState = (SavedState) state;
   1207         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
   1208         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
   1209             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
   1210         }
   1211     }
   1212 
   1213     /**
   1214      * Override to prevent freezing of any views created by the adapter.
   1215      */
   1216     @Override
   1217     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
   1218         dispatchFreezeSelfOnly(container);
   1219     }
   1220 
   1221     /**
   1222      * Override to prevent thawing of any views created by the adapter.
   1223      */
   1224     @Override
   1225     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
   1226         dispatchThawSelfOnly(container);
   1227     }
   1228 
   1229     /**
   1230      * Adds a view to the animatingViews list.
   1231      * mAnimatingViews holds the child views that are currently being kept around
   1232      * purely for the purpose of being animated out of view. They are drawn as a regular
   1233      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
   1234      * as they are managed separately from the regular child views.
   1235      * @param viewHolder The ViewHolder to be removed
   1236      */
   1237     private void addAnimatingView(ViewHolder viewHolder) {
   1238         final View view = viewHolder.itemView;
   1239         final boolean alreadyParented = view.getParent() == this;
   1240         mRecycler.unscrapView(getChildViewHolder(view));
   1241         if (viewHolder.isTmpDetached()) {
   1242             // re-attach
   1243             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
   1244         } else if (!alreadyParented) {
   1245             mChildHelper.addView(view, true);
   1246         } else {
   1247             mChildHelper.hide(view);
   1248         }
   1249     }
   1250 
   1251     /**
   1252      * Removes a view from the animatingViews list.
   1253      * @param view The view to be removed
   1254      * @see #addAnimatingView(RecyclerView.ViewHolder)
   1255      * @return true if an animating view is removed
   1256      */
   1257     boolean removeAnimatingView(View view) {
   1258         eatRequestLayout();
   1259         final boolean removed = mChildHelper.removeViewIfHidden(view);
   1260         if (removed) {
   1261             final ViewHolder viewHolder = getChildViewHolderInt(view);
   1262             mRecycler.unscrapView(viewHolder);
   1263             mRecycler.recycleViewHolderInternal(viewHolder);
   1264             if (DEBUG) {
   1265                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
   1266             }
   1267         }
   1268         // only clear request eaten flag if we removed the view.
   1269         resumeRequestLayout(!removed);
   1270         return removed;
   1271     }
   1272 
   1273     /**
   1274      * Return the {@link LayoutManager} currently responsible for
   1275      * layout policy for this RecyclerView.
   1276      *
   1277      * @return The currently bound LayoutManager
   1278      */
   1279     public LayoutManager getLayoutManager() {
   1280         return mLayout;
   1281     }
   1282 
   1283     /**
   1284      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
   1285      * if no pool is set for this view a new one will be created. See
   1286      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
   1287      *
   1288      * @return The pool used to store recycled item views for reuse.
   1289      * @see #setRecycledViewPool(RecycledViewPool)
   1290      */
   1291     public RecycledViewPool getRecycledViewPool() {
   1292         return mRecycler.getRecycledViewPool();
   1293     }
   1294 
   1295     /**
   1296      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
   1297      * This can be useful if you have multiple RecyclerViews with adapters that use the same
   1298      * view types, for example if you have several data sets with the same kinds of item views
   1299      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
   1300      *
   1301      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
   1302      */
   1303     public void setRecycledViewPool(RecycledViewPool pool) {
   1304         mRecycler.setRecycledViewPool(pool);
   1305     }
   1306 
   1307     /**
   1308      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
   1309      *
   1310      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
   1311      *
   1312      * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
   1313      */
   1314     public void setViewCacheExtension(ViewCacheExtension extension) {
   1315         mRecycler.setViewCacheExtension(extension);
   1316     }
   1317 
   1318     /**
   1319      * Set the number of offscreen views to retain before adding them to the potentially shared
   1320      * {@link #getRecycledViewPool() recycled view pool}.
   1321      *
   1322      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
   1323      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
   1324      * to rebind them.</p>
   1325      *
   1326      * @param size Number of views to cache offscreen before returning them to the general
   1327      *             recycled view pool
   1328      */
   1329     public void setItemViewCacheSize(int size) {
   1330         mRecycler.setViewCacheSize(size);
   1331     }
   1332 
   1333     /**
   1334      * Return the current scrolling state of the RecyclerView.
   1335      *
   1336      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
   1337      * {@link #SCROLL_STATE_SETTLING}
   1338      */
   1339     public int getScrollState() {
   1340         return mScrollState;
   1341     }
   1342 
   1343     void setScrollState(int state) {
   1344         if (state == mScrollState) {
   1345             return;
   1346         }
   1347         if (DEBUG) {
   1348             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
   1349                     new Exception());
   1350         }
   1351         mScrollState = state;
   1352         if (state != SCROLL_STATE_SETTLING) {
   1353             stopScrollersInternal();
   1354         }
   1355         dispatchOnScrollStateChanged(state);
   1356     }
   1357 
   1358     /**
   1359      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1360      * affect both measurement and drawing of individual item views.
   1361      *
   1362      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1363      * be run/queried/drawn first for their effects on item views. Padding added to views
   1364      * will be nested; a padding added by an earlier decoration will mean further
   1365      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1366      * given area.</p>
   1367      *
   1368      * @param decor Decoration to add
   1369      * @param index Position in the decoration chain to insert this decoration at. If this value
   1370      *              is negative the decoration will be added at the end.
   1371      */
   1372     public void addItemDecoration(ItemDecoration decor, int index) {
   1373         if (mLayout != null) {
   1374             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
   1375                     + " layout");
   1376         }
   1377         if (mItemDecorations.isEmpty()) {
   1378             setWillNotDraw(false);
   1379         }
   1380         if (index < 0) {
   1381             mItemDecorations.add(decor);
   1382         } else {
   1383             mItemDecorations.add(index, decor);
   1384         }
   1385         markItemDecorInsetsDirty();
   1386         requestLayout();
   1387     }
   1388 
   1389     /**
   1390      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1391      * affect both measurement and drawing of individual item views.
   1392      *
   1393      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1394      * be run/queried/drawn first for their effects on item views. Padding added to views
   1395      * will be nested; a padding added by an earlier decoration will mean further
   1396      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1397      * given area.</p>
   1398      *
   1399      * @param decor Decoration to add
   1400      */
   1401     public void addItemDecoration(ItemDecoration decor) {
   1402         addItemDecoration(decor, -1);
   1403     }
   1404 
   1405     /**
   1406      * Remove an {@link ItemDecoration} from this RecyclerView.
   1407      *
   1408      * <p>The given decoration will no longer impact the measurement and drawing of
   1409      * item views.</p>
   1410      *
   1411      * @param decor Decoration to remove
   1412      * @see #addItemDecoration(ItemDecoration)
   1413      */
   1414     public void removeItemDecoration(ItemDecoration decor) {
   1415         if (mLayout != null) {
   1416             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
   1417                     + " layout");
   1418         }
   1419         mItemDecorations.remove(decor);
   1420         if (mItemDecorations.isEmpty()) {
   1421             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
   1422         }
   1423         markItemDecorInsetsDirty();
   1424         requestLayout();
   1425     }
   1426 
   1427     /**
   1428      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
   1429      * <p>
   1430      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
   1431      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
   1432      * true if childDrawingOrderCallback is not null, false otherwise.
   1433      * <p>
   1434      * Note that child drawing order may be overridden by View's elevation.
   1435      *
   1436      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
   1437      *                                  system.
   1438      */
   1439     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
   1440         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
   1441             return;
   1442         }
   1443         mChildDrawingOrderCallback = childDrawingOrderCallback;
   1444         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
   1445     }
   1446 
   1447     /**
   1448      * Set a listener that will be notified of any changes in scroll state or position.
   1449      *
   1450      * @param listener Listener to set or null to clear
   1451      *
   1452      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
   1453      *             {@link #removeOnScrollListener(OnScrollListener)}
   1454      */
   1455     @Deprecated
   1456     public void setOnScrollListener(OnScrollListener listener) {
   1457         mScrollListener = listener;
   1458     }
   1459 
   1460     /**
   1461      * Add a listener that will be notified of any changes in scroll state or position.
   1462      *
   1463      * <p>Components that add a listener should take care to remove it when finished.
   1464      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
   1465      * to remove all attached listeners.</p>
   1466      *
   1467      * @param listener listener to set or null to clear
   1468      */
   1469     public void addOnScrollListener(OnScrollListener listener) {
   1470         if (mScrollListeners == null) {
   1471             mScrollListeners = new ArrayList<>();
   1472         }
   1473         mScrollListeners.add(listener);
   1474     }
   1475 
   1476     /**
   1477      * Remove a listener that was notified of any changes in scroll state or position.
   1478      *
   1479      * @param listener listener to set or null to clear
   1480      */
   1481     public void removeOnScrollListener(OnScrollListener listener) {
   1482         if (mScrollListeners != null) {
   1483             mScrollListeners.remove(listener);
   1484         }
   1485     }
   1486 
   1487     /**
   1488      * Remove all secondary listener that were notified of any changes in scroll state or position.
   1489      */
   1490     public void clearOnScrollListeners() {
   1491         if (mScrollListeners != null) {
   1492             mScrollListeners.clear();
   1493         }
   1494     }
   1495 
   1496     /**
   1497      * Convenience method to scroll to a certain position.
   1498      *
   1499      * RecyclerView does not implement scrolling logic, rather forwards the call to
   1500      * {@link com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
   1501      * @param position Scroll to this adapter position
   1502      * @see com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)
   1503      */
   1504     public void scrollToPosition(int position) {
   1505         if (mLayoutFrozen) {
   1506             return;
   1507         }
   1508         stopScroll();
   1509         if (mLayout == null) {
   1510             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
   1511                     + "Call setLayoutManager with a non-null argument.");
   1512             return;
   1513         }
   1514         mLayout.scrollToPosition(position);
   1515         awakenScrollBars();
   1516     }
   1517 
   1518     void jumpToPositionForSmoothScroller(int position) {
   1519         if (mLayout == null) {
   1520             return;
   1521         }
   1522         mLayout.scrollToPosition(position);
   1523         awakenScrollBars();
   1524     }
   1525 
   1526     /**
   1527      * Starts a smooth scroll to an adapter position.
   1528      * <p>
   1529      * To support smooth scrolling, you must override
   1530      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
   1531      * {@link SmoothScroller}.
   1532      * <p>
   1533      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
   1534      * provide a custom smooth scroll logic, override
   1535      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
   1536      * LayoutManager.
   1537      *
   1538      * @param position The adapter position to scroll to
   1539      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
   1540      */
   1541     public void smoothScrollToPosition(int position) {
   1542         if (mLayoutFrozen) {
   1543             return;
   1544         }
   1545         if (mLayout == null) {
   1546             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
   1547                     + "Call setLayoutManager with a non-null argument.");
   1548             return;
   1549         }
   1550         mLayout.smoothScrollToPosition(this, mState, position);
   1551     }
   1552 
   1553     @Override
   1554     public void scrollTo(int x, int y) {
   1555         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
   1556                 + "Use scrollToPosition instead");
   1557     }
   1558 
   1559     @Override
   1560     public void scrollBy(int x, int y) {
   1561         if (mLayout == null) {
   1562             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
   1563                     + "Call setLayoutManager with a non-null argument.");
   1564             return;
   1565         }
   1566         if (mLayoutFrozen) {
   1567             return;
   1568         }
   1569         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   1570         final boolean canScrollVertical = mLayout.canScrollVertically();
   1571         if (canScrollHorizontal || canScrollVertical) {
   1572             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
   1573         }
   1574     }
   1575 
   1576     /**
   1577      * Helper method reflect data changes to the state.
   1578      * <p>
   1579      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
   1580      * but data actually changed.
   1581      * <p>
   1582      * This method consumes all deferred changes to avoid that case.
   1583      */
   1584     void consumePendingUpdateOperations() {
   1585         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
   1586             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
   1587             dispatchLayout();
   1588             Trace.endSection();
   1589             return;
   1590         }
   1591         if (!mAdapterHelper.hasPendingUpdates()) {
   1592             return;
   1593         }
   1594 
   1595         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
   1596         // of the visible items is affected and if not, just ignore the change.
   1597         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
   1598                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
   1599                         | AdapterHelper.UpdateOp.MOVE)) {
   1600             Trace.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
   1601             eatRequestLayout();
   1602             onEnterLayoutOrScroll();
   1603             mAdapterHelper.preProcess();
   1604             if (!mLayoutRequestEaten) {
   1605                 if (hasUpdatedView()) {
   1606                     dispatchLayout();
   1607                 } else {
   1608                     // no need to layout, clean state
   1609                     mAdapterHelper.consumePostponedUpdates();
   1610                 }
   1611             }
   1612             resumeRequestLayout(true);
   1613             onExitLayoutOrScroll();
   1614             Trace.endSection();
   1615         } else if (mAdapterHelper.hasPendingUpdates()) {
   1616             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
   1617             dispatchLayout();
   1618             Trace.endSection();
   1619         }
   1620     }
   1621 
   1622     /**
   1623      * @return True if an existing view holder needs to be updated
   1624      */
   1625     private boolean hasUpdatedView() {
   1626         final int childCount = mChildHelper.getChildCount();
   1627         for (int i = 0; i < childCount; i++) {
   1628             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   1629             if (holder == null || holder.shouldIgnore()) {
   1630                 continue;
   1631             }
   1632             if (holder.isUpdated()) {
   1633                 return true;
   1634             }
   1635         }
   1636         return false;
   1637     }
   1638 
   1639     /**
   1640      * Does not perform bounds checking. Used by internal methods that have already validated input.
   1641      * <p>
   1642      * It also reports any unused scroll request to the related EdgeEffect.
   1643      *
   1644      * @param x The amount of horizontal scroll request
   1645      * @param y The amount of vertical scroll request
   1646      * @param ev The originating MotionEvent, or null if not from a touch event.
   1647      *
   1648      * @return Whether any scroll was consumed in either direction.
   1649      */
   1650     boolean scrollByInternal(int x, int y, MotionEvent ev) {
   1651         int unconsumedX = 0, unconsumedY = 0;
   1652         int consumedX = 0, consumedY = 0;
   1653 
   1654         consumePendingUpdateOperations();
   1655         if (mAdapter != null) {
   1656             eatRequestLayout();
   1657             onEnterLayoutOrScroll();
   1658             Trace.beginSection(TRACE_SCROLL_TAG);
   1659             if (x != 0) {
   1660                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
   1661                 unconsumedX = x - consumedX;
   1662             }
   1663             if (y != 0) {
   1664                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
   1665                 unconsumedY = y - consumedY;
   1666             }
   1667             Trace.endSection();
   1668             repositionShadowingViews();
   1669             onExitLayoutOrScroll();
   1670             resumeRequestLayout(false);
   1671         }
   1672         if (!mItemDecorations.isEmpty()) {
   1673             invalidate();
   1674         }
   1675 
   1676         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
   1677             // Update the last touch co-ords, taking any scroll offset into account
   1678             mLastTouchX -= mScrollOffset[0];
   1679             mLastTouchY -= mScrollOffset[1];
   1680             if (ev != null) {
   1681                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   1682             }
   1683             mNestedOffsets[0] += mScrollOffset[0];
   1684             mNestedOffsets[1] += mScrollOffset[1];
   1685         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   1686             if (ev != null) {
   1687                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
   1688             }
   1689             considerReleasingGlowsOnScroll(x, y);
   1690         }
   1691         if (consumedX != 0 || consumedY != 0) {
   1692             dispatchOnScrolled(consumedX, consumedY);
   1693         }
   1694         if (!awakenScrollBars()) {
   1695             invalidate();
   1696         }
   1697         return consumedX != 0 || consumedY != 0;
   1698     }
   1699 
   1700     /**
   1701      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
   1702      * range. This value is used to compute the length of the thumb within the scrollbar's track.
   1703      * </p>
   1704      *
   1705      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1706      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
   1707      *
   1708      * <p>Default implementation returns 0.</p>
   1709      *
   1710      * <p>If you want to support scroll bars, override
   1711      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
   1712      * LayoutManager. </p>
   1713      *
   1714      * @return The horizontal offset of the scrollbar's thumb
   1715      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
   1716      * (RecyclerView.State)
   1717      */
   1718     @Override
   1719     public int computeHorizontalScrollOffset() {
   1720         if (mLayout == null) {
   1721             return 0;
   1722         }
   1723         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
   1724     }
   1725 
   1726     /**
   1727      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
   1728      * horizontal range. This value is used to compute the length of the thumb within the
   1729      * scrollbar's track.</p>
   1730      *
   1731      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1732      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
   1733      *
   1734      * <p>Default implementation returns 0.</p>
   1735      *
   1736      * <p>If you want to support scroll bars, override
   1737      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
   1738      * LayoutManager.</p>
   1739      *
   1740      * @return The horizontal extent of the scrollbar's thumb
   1741      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
   1742      */
   1743     @Override
   1744     public int computeHorizontalScrollExtent() {
   1745         if (mLayout == null) {
   1746             return 0;
   1747         }
   1748         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
   1749     }
   1750 
   1751     /**
   1752      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
   1753      *
   1754      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1755      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
   1756      *
   1757      * <p>Default implementation returns 0.</p>
   1758      *
   1759      * <p>If you want to support scroll bars, override
   1760      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
   1761      * LayoutManager.</p>
   1762      *
   1763      * @return The total horizontal range represented by the vertical scrollbar
   1764      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
   1765      */
   1766     @Override
   1767     public int computeHorizontalScrollRange() {
   1768         if (mLayout == null) {
   1769             return 0;
   1770         }
   1771         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
   1772     }
   1773 
   1774     /**
   1775      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
   1776      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
   1777      *
   1778      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1779      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
   1780      *
   1781      * <p>Default implementation returns 0.</p>
   1782      *
   1783      * <p>If you want to support scroll bars, override
   1784      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
   1785      * LayoutManager.</p>
   1786      *
   1787      * @return The vertical offset of the scrollbar's thumb
   1788      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
   1789      * (RecyclerView.State)
   1790      */
   1791     @Override
   1792     public int computeVerticalScrollOffset() {
   1793         if (mLayout == null) {
   1794             return 0;
   1795         }
   1796         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
   1797     }
   1798 
   1799     /**
   1800      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
   1801      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
   1802      *
   1803      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1804      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
   1805      *
   1806      * <p>Default implementation returns 0.</p>
   1807      *
   1808      * <p>If you want to support scroll bars, override
   1809      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
   1810      * LayoutManager.</p>
   1811      *
   1812      * @return The vertical extent of the scrollbar's thumb
   1813      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
   1814      */
   1815     @Override
   1816     public int computeVerticalScrollExtent() {
   1817         if (mLayout == null) {
   1818             return 0;
   1819         }
   1820         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
   1821     }
   1822 
   1823     /**
   1824      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
   1825      *
   1826      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1827      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
   1828      *
   1829      * <p>Default implementation returns 0.</p>
   1830      *
   1831      * <p>If you want to support scroll bars, override
   1832      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
   1833      * LayoutManager.</p>
   1834      *
   1835      * @return The total vertical range represented by the vertical scrollbar
   1836      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
   1837      */
   1838     @Override
   1839     public int computeVerticalScrollRange() {
   1840         if (mLayout == null) {
   1841             return 0;
   1842         }
   1843         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
   1844     }
   1845 
   1846 
   1847     void eatRequestLayout() {
   1848         mEatRequestLayout++;
   1849         if (mEatRequestLayout == 1 && !mLayoutFrozen) {
   1850             mLayoutRequestEaten = false;
   1851         }
   1852     }
   1853 
   1854     void resumeRequestLayout(boolean performLayoutChildren) {
   1855         if (mEatRequestLayout < 1) {
   1856             //noinspection PointlessBooleanExpression
   1857             if (DEBUG) {
   1858                 throw new IllegalStateException("invalid eat request layout count");
   1859             }
   1860             mEatRequestLayout = 1;
   1861         }
   1862         if (!performLayoutChildren) {
   1863             // Reset the layout request eaten counter.
   1864             // This is necessary since eatRequest calls can be nested in which case the other
   1865             // call will override the inner one.
   1866             // for instance:
   1867             // eat layout for process adapter updates
   1868             //   eat layout for dispatchLayout
   1869             //     a bunch of req layout calls arrive
   1870 
   1871             mLayoutRequestEaten = false;
   1872         }
   1873         if (mEatRequestLayout == 1) {
   1874             // when layout is frozen we should delay dispatchLayout()
   1875             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
   1876                     && mLayout != null && mAdapter != null) {
   1877                 dispatchLayout();
   1878             }
   1879             if (!mLayoutFrozen) {
   1880                 mLayoutRequestEaten = false;
   1881             }
   1882         }
   1883         mEatRequestLayout--;
   1884     }
   1885 
   1886     /**
   1887      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
   1888      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
   1889      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
   1890      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
   1891      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
   1892      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
   1893      * called.
   1894      *
   1895      * <p>
   1896      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
   1897      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
   1898      * RecyclerView, State, int)}.
   1899      * <p>
   1900      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
   1901      * stop frozen.
   1902      * <p>
   1903      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
   1904      * responsibility to call ItemAnimator.end().
   1905      *
   1906      * @param frozen   true to freeze layout and scroll, false to re-enable.
   1907      */
   1908     public void setLayoutFrozen(boolean frozen) {
   1909         if (frozen != mLayoutFrozen) {
   1910             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
   1911             if (!frozen) {
   1912                 mLayoutFrozen = false;
   1913                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
   1914                     requestLayout();
   1915                 }
   1916                 mLayoutRequestEaten = false;
   1917             } else {
   1918                 final long now = SystemClock.uptimeMillis();
   1919                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
   1920                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
   1921                 onTouchEvent(cancelEvent);
   1922                 mLayoutFrozen = true;
   1923                 mIgnoreMotionEventTillDown = true;
   1924                 stopScroll();
   1925             }
   1926         }
   1927     }
   1928 
   1929     /**
   1930      * Returns true if layout and scroll are frozen.
   1931      *
   1932      * @return true if layout and scroll are frozen
   1933      * @see #setLayoutFrozen(boolean)
   1934      */
   1935     public boolean isLayoutFrozen() {
   1936         return mLayoutFrozen;
   1937     }
   1938 
   1939     /**
   1940      * Animate a scroll by the given amount of pixels along either axis.
   1941      *
   1942      * @param dx Pixels to scroll horizontally
   1943      * @param dy Pixels to scroll vertically
   1944      */
   1945     public void smoothScrollBy(int dx, int dy) {
   1946         smoothScrollBy(dx, dy, null);
   1947     }
   1948 
   1949     /**
   1950      * Animate a scroll by the given amount of pixels along either axis.
   1951      *
   1952      * @param dx Pixels to scroll horizontally
   1953      * @param dy Pixels to scroll vertically
   1954      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
   1955      *                     {@code null}, RecyclerView is going to use the default interpolator.
   1956      */
   1957     public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
   1958         if (mLayout == null) {
   1959             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
   1960                     + "Call setLayoutManager with a non-null argument.");
   1961             return;
   1962         }
   1963         if (mLayoutFrozen) {
   1964             return;
   1965         }
   1966         if (!mLayout.canScrollHorizontally()) {
   1967             dx = 0;
   1968         }
   1969         if (!mLayout.canScrollVertically()) {
   1970             dy = 0;
   1971         }
   1972         if (dx != 0 || dy != 0) {
   1973             mViewFlinger.smoothScrollBy(dx, dy, interpolator);
   1974         }
   1975     }
   1976 
   1977     /**
   1978      * Begin a standard fling with an initial velocity along each axis in pixels per second.
   1979      * If the velocity given is below the system-defined minimum this method will return false
   1980      * and no fling will occur.
   1981      *
   1982      * @param velocityX Initial horizontal velocity in pixels per second
   1983      * @param velocityY Initial vertical velocity in pixels per second
   1984      * @return true if the fling was started, false if the velocity was too low to fling or
   1985      * LayoutManager does not support scrolling in the axis fling is issued.
   1986      *
   1987      * @see LayoutManager#canScrollVertically()
   1988      * @see LayoutManager#canScrollHorizontally()
   1989      */
   1990     public boolean fling(int velocityX, int velocityY) {
   1991         if (mLayout == null) {
   1992             Log.e(TAG, "Cannot fling without a LayoutManager set. "
   1993                     + "Call setLayoutManager with a non-null argument.");
   1994             return false;
   1995         }
   1996         if (mLayoutFrozen) {
   1997             return false;
   1998         }
   1999 
   2000         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   2001         final boolean canScrollVertical = mLayout.canScrollVertically();
   2002 
   2003         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
   2004             velocityX = 0;
   2005         }
   2006         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
   2007             velocityY = 0;
   2008         }
   2009         if (velocityX == 0 && velocityY == 0) {
   2010             // If we don't have any velocity, return false
   2011             return false;
   2012         }
   2013 
   2014         if (!dispatchNestedPreFling(velocityX, velocityY)) {
   2015             final boolean canScroll = canScrollHorizontal || canScrollVertical;
   2016             dispatchNestedFling(velocityX, velocityY, canScroll);
   2017 
   2018             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
   2019                 return true;
   2020             }
   2021 
   2022             if (canScroll) {
   2023                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
   2024                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
   2025                 mViewFlinger.fling(velocityX, velocityY);
   2026                 return true;
   2027             }
   2028         }
   2029         return false;
   2030     }
   2031 
   2032     /**
   2033      * Stop any current scroll in progress, such as one started by
   2034      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
   2035      */
   2036     public void stopScroll() {
   2037         setScrollState(SCROLL_STATE_IDLE);
   2038         stopScrollersInternal();
   2039     }
   2040 
   2041     /**
   2042      * Similar to {@link #stopScroll()} but does not set the state.
   2043      */
   2044     private void stopScrollersInternal() {
   2045         mViewFlinger.stop();
   2046         if (mLayout != null) {
   2047             mLayout.stopSmoothScroller();
   2048         }
   2049     }
   2050 
   2051     /**
   2052      * Returns the minimum velocity to start a fling.
   2053      *
   2054      * @return The minimum velocity to start a fling
   2055      */
   2056     public int getMinFlingVelocity() {
   2057         return mMinFlingVelocity;
   2058     }
   2059 
   2060 
   2061     /**
   2062      * Returns the maximum fling velocity used by this RecyclerView.
   2063      *
   2064      * @return The maximum fling velocity used by this RecyclerView.
   2065      */
   2066     public int getMaxFlingVelocity() {
   2067         return mMaxFlingVelocity;
   2068     }
   2069 
   2070     /**
   2071      * Apply a pull to relevant overscroll glow effects
   2072      */
   2073     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
   2074         boolean invalidate = false;
   2075         if (overscrollX < 0) {
   2076             ensureLeftGlow();
   2077             mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight());
   2078             invalidate = true;
   2079         } else if (overscrollX > 0) {
   2080             ensureRightGlow();
   2081             mRightGlow.onPull(overscrollX / getWidth(), y / getHeight());
   2082             invalidate = true;
   2083         }
   2084 
   2085         if (overscrollY < 0) {
   2086             ensureTopGlow();
   2087             mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth());
   2088             invalidate = true;
   2089         } else if (overscrollY > 0) {
   2090             ensureBottomGlow();
   2091             mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth());
   2092             invalidate = true;
   2093         }
   2094 
   2095         if (invalidate || overscrollX != 0 || overscrollY != 0) {
   2096             postInvalidateOnAnimation();
   2097         }
   2098     }
   2099 
   2100     private void releaseGlows() {
   2101         boolean needsInvalidate = false;
   2102         if (mLeftGlow != null) {
   2103             mLeftGlow.onRelease();
   2104             needsInvalidate = true;
   2105         }
   2106         if (mTopGlow != null) {
   2107             mTopGlow.onRelease();
   2108             needsInvalidate = true;
   2109         }
   2110         if (mRightGlow != null) {
   2111             mRightGlow.onRelease();
   2112             needsInvalidate = true;
   2113         }
   2114         if (mBottomGlow != null) {
   2115             mBottomGlow.onRelease();
   2116             needsInvalidate = true;
   2117         }
   2118         if (needsInvalidate) {
   2119             postInvalidateOnAnimation();
   2120         }
   2121     }
   2122 
   2123     void considerReleasingGlowsOnScroll(int dx, int dy) {
   2124         boolean needsInvalidate = false;
   2125         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
   2126             mLeftGlow.onRelease();
   2127             needsInvalidate = true;
   2128         }
   2129         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
   2130             mRightGlow.onRelease();
   2131             needsInvalidate = true;
   2132         }
   2133         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
   2134             mTopGlow.onRelease();
   2135             needsInvalidate = true;
   2136         }
   2137         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
   2138             mBottomGlow.onRelease();
   2139             needsInvalidate = true;
   2140         }
   2141         if (needsInvalidate) {
   2142             postInvalidateOnAnimation();
   2143         }
   2144     }
   2145 
   2146     void absorbGlows(int velocityX, int velocityY) {
   2147         if (velocityX < 0) {
   2148             ensureLeftGlow();
   2149             mLeftGlow.onAbsorb(-velocityX);
   2150         } else if (velocityX > 0) {
   2151             ensureRightGlow();
   2152             mRightGlow.onAbsorb(velocityX);
   2153         }
   2154 
   2155         if (velocityY < 0) {
   2156             ensureTopGlow();
   2157             mTopGlow.onAbsorb(-velocityY);
   2158         } else if (velocityY > 0) {
   2159             ensureBottomGlow();
   2160             mBottomGlow.onAbsorb(velocityY);
   2161         }
   2162 
   2163         if (velocityX != 0 || velocityY != 0) {
   2164             postInvalidateOnAnimation();
   2165         }
   2166     }
   2167 
   2168     void ensureLeftGlow() {
   2169         if (mLeftGlow != null) {
   2170             return;
   2171         }
   2172         mLeftGlow = new EdgeEffect(getContext());
   2173         if (mClipToPadding) {
   2174             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   2175                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   2176         } else {
   2177             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   2178         }
   2179     }
   2180 
   2181     void ensureRightGlow() {
   2182         if (mRightGlow != null) {
   2183             return;
   2184         }
   2185         mRightGlow = new EdgeEffect(getContext());
   2186         if (mClipToPadding) {
   2187             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   2188                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   2189         } else {
   2190             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   2191         }
   2192     }
   2193 
   2194     void ensureTopGlow() {
   2195         if (mTopGlow != null) {
   2196             return;
   2197         }
   2198         mTopGlow = new EdgeEffect(getContext());
   2199         if (mClipToPadding) {
   2200             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   2201                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   2202         } else {
   2203             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   2204         }
   2205 
   2206     }
   2207 
   2208     void ensureBottomGlow() {
   2209         if (mBottomGlow != null) {
   2210             return;
   2211         }
   2212         mBottomGlow = new EdgeEffect(getContext());
   2213         if (mClipToPadding) {
   2214             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   2215                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   2216         } else {
   2217             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   2218         }
   2219     }
   2220 
   2221     void invalidateGlows() {
   2222         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
   2223     }
   2224 
   2225     /**
   2226      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
   2227      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
   2228      * that differs from other ViewGroups.
   2229      * <p>
   2230      * It first does a focus search within the RecyclerView. If this search finds a View that is in
   2231      * the focus direction with respect to the currently focused View, RecyclerView returns that
   2232      * child as the next focus target. When it cannot find such child, it calls
   2233      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
   2234      * in the focus search direction. If LayoutManager adds a View that matches the
   2235      * focus search criteria, it will be returned as the focus search result. Otherwise,
   2236      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
   2237      * <p>
   2238      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
   2239      * is not in the focus direction is still valid focus target which may not be the desired
   2240      * behavior if the Adapter has more children in the focus direction. To handle this case,
   2241      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
   2242      * focus search in that direction. If there are no Views to gain focus, it will call
   2243      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
   2244      * focus search with the original (relative) direction. This allows RecyclerView to provide
   2245      * better candidates to the focus search while still allowing the view system to take focus from
   2246      * the RecyclerView and give it to a more suitable child if such child exists.
   2247      *
   2248      * @param focused The view that currently has focus
   2249      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   2250      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
   2251      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
   2252      *
   2253      * @return A new View that can be the next focus after the focused View
   2254      */
   2255     @Override
   2256     public View focusSearch(View focused, int direction) {
   2257         View result = mLayout.onInterceptFocusSearch(focused, direction);
   2258         if (result != null) {
   2259             return result;
   2260         }
   2261         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
   2262                 && !isComputingLayout() && !mLayoutFrozen;
   2263 
   2264         final FocusFinder ff = FocusFinder.getInstance();
   2265         if (canRunFocusFailure
   2266                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
   2267             // convert direction to absolute direction and see if we have a view there and if not
   2268             // tell LayoutManager to add if it can.
   2269             boolean needsFocusFailureLayout = false;
   2270             if (mLayout.canScrollVertically()) {
   2271                 final int absDir =
   2272                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
   2273                 final View found = ff.findNextFocus(this, focused, absDir);
   2274                 needsFocusFailureLayout = found == null;
   2275                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
   2276                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
   2277                     direction = absDir;
   2278                 }
   2279             }
   2280             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
   2281                 boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
   2282                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
   2283                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
   2284                 final View found = ff.findNextFocus(this, focused, absDir);
   2285                 needsFocusFailureLayout = found == null;
   2286                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
   2287                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
   2288                     direction = absDir;
   2289                 }
   2290             }
   2291             if (needsFocusFailureLayout) {
   2292                 consumePendingUpdateOperations();
   2293                 final View focusedItemView = findContainingItemView(focused);
   2294                 if (focusedItemView == null) {
   2295                     // panic, focused view is not a child anymore, cannot call super.
   2296                     return null;
   2297                 }
   2298                 eatRequestLayout();
   2299                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
   2300                 resumeRequestLayout(false);
   2301             }
   2302             result = ff.findNextFocus(this, focused, direction);
   2303         } else {
   2304             result = ff.findNextFocus(this, focused, direction);
   2305             if (result == null && canRunFocusFailure) {
   2306                 consumePendingUpdateOperations();
   2307                 final View focusedItemView = findContainingItemView(focused);
   2308                 if (focusedItemView == null) {
   2309                     // panic, focused view is not a child anymore, cannot call super.
   2310                     return null;
   2311                 }
   2312                 eatRequestLayout();
   2313                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
   2314                 resumeRequestLayout(false);
   2315             }
   2316         }
   2317         return isPreferredNextFocus(focused, result, direction)
   2318                 ? result : super.focusSearch(focused, direction);
   2319     }
   2320 
   2321     /**
   2322      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
   2323      * assign it as the next focus View instead of letting view hierarchy decide.
   2324      * A good candidate means a View that is aligned in the focus direction wrt the focused View
   2325      * and is not the RecyclerView itself.
   2326      * When this method returns false, RecyclerView will let the parent make the decision so the
   2327      * same View may still get the focus as a result of that search.
   2328      */
   2329     private boolean isPreferredNextFocus(View focused, View next, int direction) {
   2330         if (next == null || next == this) {
   2331             return false;
   2332         }
   2333         if (focused == null) {
   2334             return true;
   2335         }
   2336 
   2337         if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
   2338             final boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
   2339             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
   2340                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
   2341             if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
   2342                 return true;
   2343             }
   2344             if (direction == View.FOCUS_FORWARD) {
   2345                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
   2346             } else {
   2347                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
   2348             }
   2349         } else {
   2350             return isPreferredNextFocusAbsolute(focused, next, direction);
   2351         }
   2352 
   2353     }
   2354 
   2355     /**
   2356      * Logic taken from FocusSearch#isCandidate
   2357      */
   2358     private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
   2359         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
   2360         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
   2361         offsetDescendantRectToMyCoords(focused, mTempRect);
   2362         offsetDescendantRectToMyCoords(next, mTempRect2);
   2363         switch (direction) {
   2364             case View.FOCUS_LEFT:
   2365                 return (mTempRect.right > mTempRect2.right
   2366                         || mTempRect.left >= mTempRect2.right)
   2367                         && mTempRect.left > mTempRect2.left;
   2368             case View.FOCUS_RIGHT:
   2369                 return (mTempRect.left < mTempRect2.left
   2370                         || mTempRect.right <= mTempRect2.left)
   2371                         && mTempRect.right < mTempRect2.right;
   2372             case View.FOCUS_UP:
   2373                 return (mTempRect.bottom > mTempRect2.bottom
   2374                         || mTempRect.top >= mTempRect2.bottom)
   2375                         && mTempRect.top > mTempRect2.top;
   2376             case View.FOCUS_DOWN:
   2377                 return (mTempRect.top < mTempRect2.top
   2378                         || mTempRect.bottom <= mTempRect2.top)
   2379                         && mTempRect.bottom < mTempRect2.bottom;
   2380         }
   2381         throw new IllegalArgumentException("direction must be absolute. received:" + direction);
   2382     }
   2383 
   2384     @Override
   2385     public void requestChildFocus(View child, View focused) {
   2386         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
   2387             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
   2388 
   2389             // get item decor offsets w/o refreshing. If they are invalid, there will be another
   2390             // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
   2391             // View in viewport.
   2392             final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
   2393             if (focusedLayoutParams instanceof LayoutParams) {
   2394                 // if focused child has item decors, use them. Otherwise, ignore.
   2395                 final LayoutParams lp = (LayoutParams) focusedLayoutParams;
   2396                 if (!lp.mInsetsDirty) {
   2397                     final Rect insets = lp.mDecorInsets;
   2398                     mTempRect.left -= insets.left;
   2399                     mTempRect.right += insets.right;
   2400                     mTempRect.top -= insets.top;
   2401                     mTempRect.bottom += insets.bottom;
   2402                 }
   2403             }
   2404 
   2405             offsetDescendantRectToMyCoords(focused, mTempRect);
   2406             offsetRectIntoDescendantCoords(child, mTempRect);
   2407             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
   2408         }
   2409         super.requestChildFocus(child, focused);
   2410     }
   2411 
   2412     @Override
   2413     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
   2414         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
   2415     }
   2416 
   2417     @Override
   2418     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
   2419         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
   2420             super.addFocusables(views, direction, focusableMode);
   2421         }
   2422     }
   2423 
   2424     @Override
   2425     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
   2426         if (isComputingLayout()) {
   2427             // if we are in the middle of a layout calculation, don't let any child take focus.
   2428             // RV will handle it after layout calculation is finished.
   2429             return false;
   2430         }
   2431         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
   2432     }
   2433 
   2434     @Override
   2435     protected void onAttachedToWindow() {
   2436         super.onAttachedToWindow();
   2437         mLayoutOrScrollCounter = 0;
   2438         mIsAttached = true;
   2439         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
   2440         if (mLayout != null) {
   2441             mLayout.dispatchAttachedToWindow(this);
   2442         }
   2443         mPostedAnimatorRunner = false;
   2444 
   2445         if (ALLOW_THREAD_GAP_WORK) {
   2446             // Register with gap worker
   2447             mGapWorker = GapWorker.sGapWorker.get();
   2448             if (mGapWorker == null) {
   2449                 mGapWorker = new GapWorker();
   2450 
   2451                 // break 60 fps assumption if data from display appears valid
   2452                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
   2453                 Display display = getDisplay();
   2454                 float refreshRate = 60.0f;
   2455                 if (!isInEditMode() && display != null) {
   2456                     float displayRefreshRate = display.getRefreshRate();
   2457                     if (displayRefreshRate >= 30.0f) {
   2458                         refreshRate = displayRefreshRate;
   2459                     }
   2460                 }
   2461                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
   2462                 GapWorker.sGapWorker.set(mGapWorker);
   2463             }
   2464             mGapWorker.add(this);
   2465         }
   2466     }
   2467 
   2468     @Override
   2469     protected void onDetachedFromWindow() {
   2470         super.onDetachedFromWindow();
   2471         if (mItemAnimator != null) {
   2472             mItemAnimator.endAnimations();
   2473         }
   2474         stopScroll();
   2475         mIsAttached = false;
   2476         if (mLayout != null) {
   2477             mLayout.dispatchDetachedFromWindow(this, mRecycler);
   2478         }
   2479         mPendingAccessibilityImportanceChange.clear();
   2480         removeCallbacks(mItemAnimatorRunner);
   2481         mViewInfoStore.onDetach();
   2482 
   2483         if (ALLOW_THREAD_GAP_WORK) {
   2484             // Unregister with gap worker
   2485             mGapWorker.remove(this);
   2486             mGapWorker = null;
   2487         }
   2488     }
   2489 
   2490     /**
   2491      * Returns true if RecyclerView is attached to window.
   2492      */
   2493     // @override
   2494     public boolean isAttachedToWindow() {
   2495         return mIsAttached;
   2496     }
   2497 
   2498     /**
   2499      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2500      * {@link IllegalStateException} if it <b>is not</b>.
   2501      *
   2502      * @param message The message for the exception. Can be null.
   2503      * @see #assertNotInLayoutOrScroll(String)
   2504      */
   2505     void assertInLayoutOrScroll(String message) {
   2506         if (!isComputingLayout()) {
   2507             if (message == null) {
   2508                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
   2509                         + "computing a layout or scrolling");
   2510             }
   2511             throw new IllegalStateException(message);
   2512 
   2513         }
   2514     }
   2515 
   2516     /**
   2517      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2518      * {@link IllegalStateException} if it <b>is</b>.
   2519      *
   2520      * @param message The message for the exception. Can be null.
   2521      * @see #assertInLayoutOrScroll(String)
   2522      */
   2523     void assertNotInLayoutOrScroll(String message) {
   2524         if (isComputingLayout()) {
   2525             if (message == null) {
   2526                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
   2527                         + "computing a layout or scrolling");
   2528             }
   2529             throw new IllegalStateException(message);
   2530         }
   2531         if (mDispatchScrollCounter > 0) {
   2532             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
   2533                     + " during a measure & layout pass where you cannot change the RecyclerView"
   2534                     + " data. Any method call that might change the structure of the RecyclerView"
   2535                     + " or the adapter contents should be postponed to the next frame.",
   2536                     new IllegalStateException(""));
   2537         }
   2538     }
   2539 
   2540     /**
   2541      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
   2542      * to child views or this view's standard scrolling behavior.
   2543      *
   2544      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
   2545      * returns true from
   2546      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
   2547      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
   2548      * for each incoming MotionEvent until the end of the gesture.</p>
   2549      *
   2550      * @param listener Listener to add
   2551      * @see SimpleOnItemTouchListener
   2552      */
   2553     public void addOnItemTouchListener(OnItemTouchListener listener) {
   2554         mOnItemTouchListeners.add(listener);
   2555     }
   2556 
   2557     /**
   2558      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
   2559      *
   2560      * @param listener Listener to remove
   2561      */
   2562     public void removeOnItemTouchListener(OnItemTouchListener listener) {
   2563         mOnItemTouchListeners.remove(listener);
   2564         if (mActiveOnItemTouchListener == listener) {
   2565             mActiveOnItemTouchListener = null;
   2566         }
   2567     }
   2568 
   2569     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
   2570         final int action = e.getAction();
   2571         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
   2572             mActiveOnItemTouchListener = null;
   2573         }
   2574 
   2575         final int listenerCount = mOnItemTouchListeners.size();
   2576         for (int i = 0; i < listenerCount; i++) {
   2577             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2578             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
   2579                 mActiveOnItemTouchListener = listener;
   2580                 return true;
   2581             }
   2582         }
   2583         return false;
   2584     }
   2585 
   2586     private boolean dispatchOnItemTouch(MotionEvent e) {
   2587         final int action = e.getAction();
   2588         if (mActiveOnItemTouchListener != null) {
   2589             if (action == MotionEvent.ACTION_DOWN) {
   2590                 // Stale state from a previous gesture, we're starting a new one. Clear it.
   2591                 mActiveOnItemTouchListener = null;
   2592             } else {
   2593                 mActiveOnItemTouchListener.onTouchEvent(this, e);
   2594                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
   2595                     // Clean up for the next gesture.
   2596                     mActiveOnItemTouchListener = null;
   2597                 }
   2598                 return true;
   2599             }
   2600         }
   2601 
   2602         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
   2603         // as called from onInterceptTouchEvent; skip it.
   2604         if (action != MotionEvent.ACTION_DOWN) {
   2605             final int listenerCount = mOnItemTouchListeners.size();
   2606             for (int i = 0; i < listenerCount; i++) {
   2607                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2608                 if (listener.onInterceptTouchEvent(this, e)) {
   2609                     mActiveOnItemTouchListener = listener;
   2610                     return true;
   2611                 }
   2612             }
   2613         }
   2614         return false;
   2615     }
   2616 
   2617     @Override
   2618     public boolean onInterceptTouchEvent(MotionEvent e) {
   2619         if (mLayoutFrozen) {
   2620             // When layout is frozen,  RV does not intercept the motion event.
   2621             // A child view e.g. a button may still get the click.
   2622             return false;
   2623         }
   2624         if (dispatchOnItemTouchIntercept(e)) {
   2625             cancelTouch();
   2626             return true;
   2627         }
   2628 
   2629         if (mLayout == null) {
   2630             return false;
   2631         }
   2632 
   2633         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   2634         final boolean canScrollVertically = mLayout.canScrollVertically();
   2635 
   2636         if (mVelocityTracker == null) {
   2637             mVelocityTracker = VelocityTracker.obtain();
   2638         }
   2639         mVelocityTracker.addMovement(e);
   2640 
   2641         final int action = e.getActionMasked();
   2642         final int actionIndex = e.getActionIndex();
   2643 
   2644         switch (action) {
   2645             case MotionEvent.ACTION_DOWN:
   2646                 if (mIgnoreMotionEventTillDown) {
   2647                     mIgnoreMotionEventTillDown = false;
   2648                 }
   2649                 mScrollPointerId = e.getPointerId(0);
   2650                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   2651                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   2652 
   2653                 if (mScrollState == SCROLL_STATE_SETTLING) {
   2654                     getParent().requestDisallowInterceptTouchEvent(true);
   2655                     setScrollState(SCROLL_STATE_DRAGGING);
   2656                 }
   2657 
   2658                 // Clear the nested offsets
   2659                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
   2660 
   2661                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
   2662                 if (canScrollHorizontally) {
   2663                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
   2664                 }
   2665                 if (canScrollVertically) {
   2666                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
   2667                 }
   2668                 startNestedScroll(nestedScrollAxis);
   2669                 break;
   2670 
   2671             case MotionEvent.ACTION_POINTER_DOWN:
   2672                 mScrollPointerId = e.getPointerId(actionIndex);
   2673                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
   2674                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
   2675                 break;
   2676 
   2677             case MotionEvent.ACTION_MOVE: {
   2678                 final int index = e.findPointerIndex(mScrollPointerId);
   2679                 if (index < 0) {
   2680                     Log.e(TAG, "Error processing scroll; pointer index for id "
   2681                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   2682                     return false;
   2683                 }
   2684 
   2685                 final int x = (int) (e.getX(index) + 0.5f);
   2686                 final int y = (int) (e.getY(index) + 0.5f);
   2687                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   2688                     final int dx = x - mInitialTouchX;
   2689                     final int dy = y - mInitialTouchY;
   2690                     boolean startScroll = false;
   2691                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   2692                         mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
   2693                         startScroll = true;
   2694                     }
   2695                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   2696                         mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
   2697                         startScroll = true;
   2698                     }
   2699                     if (startScroll) {
   2700                         setScrollState(SCROLL_STATE_DRAGGING);
   2701                     }
   2702                 }
   2703             } break;
   2704 
   2705             case MotionEvent.ACTION_POINTER_UP: {
   2706                 onPointerUp(e);
   2707             } break;
   2708 
   2709             case MotionEvent.ACTION_UP: {
   2710                 mVelocityTracker.clear();
   2711                 stopNestedScroll();
   2712             } break;
   2713 
   2714             case MotionEvent.ACTION_CANCEL: {
   2715                 cancelTouch();
   2716             }
   2717         }
   2718         return mScrollState == SCROLL_STATE_DRAGGING;
   2719     }
   2720 
   2721     @Override
   2722     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   2723         final int listenerCount = mOnItemTouchListeners.size();
   2724         for (int i = 0; i < listenerCount; i++) {
   2725             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2726             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
   2727         }
   2728         super.requestDisallowInterceptTouchEvent(disallowIntercept);
   2729     }
   2730 
   2731     @Override
   2732     public boolean onTouchEvent(MotionEvent e) {
   2733         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
   2734             return false;
   2735         }
   2736         if (dispatchOnItemTouch(e)) {
   2737             cancelTouch();
   2738             return true;
   2739         }
   2740 
   2741         if (mLayout == null) {
   2742             return false;
   2743         }
   2744 
   2745         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   2746         final boolean canScrollVertically = mLayout.canScrollVertically();
   2747 
   2748         if (mVelocityTracker == null) {
   2749             mVelocityTracker = VelocityTracker.obtain();
   2750         }
   2751         boolean eventAddedToVelocityTracker = false;
   2752 
   2753         final MotionEvent vtev = MotionEvent.obtain(e);
   2754         final int action = e.getActionMasked();
   2755         final int actionIndex = e.getActionIndex();
   2756 
   2757         if (action == MotionEvent.ACTION_DOWN) {
   2758             mNestedOffsets[0] = mNestedOffsets[1] = 0;
   2759         }
   2760         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
   2761 
   2762         switch (action) {
   2763             case MotionEvent.ACTION_DOWN: {
   2764                 mScrollPointerId = e.getPointerId(0);
   2765                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   2766                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   2767 
   2768                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
   2769                 if (canScrollHorizontally) {
   2770                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
   2771                 }
   2772                 if (canScrollVertically) {
   2773                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
   2774                 }
   2775                 startNestedScroll(nestedScrollAxis);
   2776             } break;
   2777 
   2778             case MotionEvent.ACTION_POINTER_DOWN: {
   2779                 mScrollPointerId = e.getPointerId(actionIndex);
   2780                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
   2781                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
   2782             } break;
   2783 
   2784             case MotionEvent.ACTION_MOVE: {
   2785                 final int index = e.findPointerIndex(mScrollPointerId);
   2786                 if (index < 0) {
   2787                     Log.e(TAG, "Error processing scroll; pointer index for id "
   2788                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   2789                     return false;
   2790                 }
   2791 
   2792                 final int x = (int) (e.getX(index) + 0.5f);
   2793                 final int y = (int) (e.getY(index) + 0.5f);
   2794                 int dx = mLastTouchX - x;
   2795                 int dy = mLastTouchY - y;
   2796 
   2797                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
   2798                     dx -= mScrollConsumed[0];
   2799                     dy -= mScrollConsumed[1];
   2800                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   2801                     // Updated the nested offsets
   2802                     mNestedOffsets[0] += mScrollOffset[0];
   2803                     mNestedOffsets[1] += mScrollOffset[1];
   2804                 }
   2805 
   2806                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   2807                     boolean startScroll = false;
   2808                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   2809                         if (dx > 0) {
   2810                             dx -= mTouchSlop;
   2811                         } else {
   2812                             dx += mTouchSlop;
   2813                         }
   2814                         startScroll = true;
   2815                     }
   2816                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   2817                         if (dy > 0) {
   2818                             dy -= mTouchSlop;
   2819                         } else {
   2820                             dy += mTouchSlop;
   2821                         }
   2822                         startScroll = true;
   2823                     }
   2824                     if (startScroll) {
   2825                         setScrollState(SCROLL_STATE_DRAGGING);
   2826                     }
   2827                 }
   2828 
   2829                 if (mScrollState == SCROLL_STATE_DRAGGING) {
   2830                     mLastTouchX = x - mScrollOffset[0];
   2831                     mLastTouchY = y - mScrollOffset[1];
   2832 
   2833                     if (scrollByInternal(
   2834                             canScrollHorizontally ? dx : 0,
   2835                             canScrollVertically ? dy : 0,
   2836                             vtev)) {
   2837                         getParent().requestDisallowInterceptTouchEvent(true);
   2838                     }
   2839                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
   2840                         mGapWorker.postFromTraversal(this, dx, dy);
   2841                     }
   2842                 }
   2843             } break;
   2844 
   2845             case MotionEvent.ACTION_POINTER_UP: {
   2846                 onPointerUp(e);
   2847             } break;
   2848 
   2849             case MotionEvent.ACTION_UP: {
   2850                 mVelocityTracker.addMovement(vtev);
   2851                 eventAddedToVelocityTracker = true;
   2852                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
   2853                 final float xvel = canScrollHorizontally
   2854                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
   2855                 final float yvel = canScrollVertically
   2856                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
   2857                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
   2858                     setScrollState(SCROLL_STATE_IDLE);
   2859                 }
   2860                 resetTouch();
   2861             } break;
   2862 
   2863             case MotionEvent.ACTION_CANCEL: {
   2864                 cancelTouch();
   2865             } break;
   2866         }
   2867 
   2868         if (!eventAddedToVelocityTracker) {
   2869             mVelocityTracker.addMovement(vtev);
   2870         }
   2871         vtev.recycle();
   2872 
   2873         return true;
   2874     }
   2875 
   2876     private void resetTouch() {
   2877         if (mVelocityTracker != null) {
   2878             mVelocityTracker.clear();
   2879         }
   2880         stopNestedScroll();
   2881         releaseGlows();
   2882     }
   2883 
   2884     private void cancelTouch() {
   2885         resetTouch();
   2886         setScrollState(SCROLL_STATE_IDLE);
   2887     }
   2888 
   2889     private void onPointerUp(MotionEvent e) {
   2890         final int actionIndex = e.getActionIndex();
   2891         if (e.getPointerId(actionIndex) == mScrollPointerId) {
   2892             // Pick a new pointer to pick up the slack.
   2893             final int newIndex = actionIndex == 0 ? 1 : 0;
   2894             mScrollPointerId = e.getPointerId(newIndex);
   2895             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
   2896             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
   2897         }
   2898     }
   2899 
   2900     // @Override
   2901     public boolean onGenericMotionEvent(MotionEvent event) {
   2902         if (mLayout == null) {
   2903             return false;
   2904         }
   2905         if (mLayoutFrozen) {
   2906             return false;
   2907         }
   2908         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
   2909             if (event.getAction() == MotionEvent.ACTION_SCROLL) {
   2910                 final float vScroll, hScroll;
   2911                 if (mLayout.canScrollVertically()) {
   2912                     // Inverse the sign of the vertical scroll to align the scroll orientation
   2913                     // with AbsListView.
   2914                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
   2915                 } else {
   2916                     vScroll = 0f;
   2917                 }
   2918                 if (mLayout.canScrollHorizontally()) {
   2919                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
   2920                 } else {
   2921                     hScroll = 0f;
   2922                 }
   2923 
   2924                 if (vScroll != 0 || hScroll != 0) {
   2925                     final float scrollFactor = getScrollFactor();
   2926                     scrollByInternal((int) (hScroll * scrollFactor),
   2927                             (int) (vScroll * scrollFactor), event);
   2928                 }
   2929             }
   2930         }
   2931         return false;
   2932     }
   2933 
   2934     /**
   2935      * Ported from View.getVerticalScrollFactor.
   2936      */
   2937     private float getScrollFactor() {
   2938         if (mScrollFactor == Float.MIN_VALUE) {
   2939             TypedValue outValue = new TypedValue();
   2940             if (getContext().getTheme().resolveAttribute(
   2941                     android.R.attr.listPreferredItemHeight, outValue, true)) {
   2942                 mScrollFactor = outValue.getDimension(
   2943                         getContext().getResources().getDisplayMetrics());
   2944             } else {
   2945                 return 0; //listPreferredItemHeight is not defined, no generic scrolling
   2946             }
   2947         }
   2948         return mScrollFactor;
   2949     }
   2950 
   2951     @Override
   2952     protected void onMeasure(int widthSpec, int heightSpec) {
   2953         if (mLayout == null) {
   2954             defaultOnMeasure(widthSpec, heightSpec);
   2955             return;
   2956         }
   2957         if (mLayout.mAutoMeasure) {
   2958             final int widthMode = MeasureSpec.getMode(widthSpec);
   2959             final int heightMode = MeasureSpec.getMode(heightSpec);
   2960             final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
   2961                     && heightMode == MeasureSpec.EXACTLY;
   2962             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   2963             if (skipMeasure || mAdapter == null) {
   2964                 return;
   2965             }
   2966             if (mState.mLayoutStep == State.STEP_START) {
   2967                 dispatchLayoutStep1();
   2968             }
   2969             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
   2970             // consistency
   2971             mLayout.setMeasureSpecs(widthSpec, heightSpec);
   2972             mState.mIsMeasuring = true;
   2973             dispatchLayoutStep2();
   2974 
   2975             // now we can get the width and height from the children.
   2976             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
   2977 
   2978             // if RecyclerView has non-exact width and height and if there is at least one child
   2979             // which also has non-exact width & height, we have to re-measure.
   2980             if (mLayout.shouldMeasureTwice()) {
   2981                 mLayout.setMeasureSpecs(
   2982                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
   2983                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
   2984                 mState.mIsMeasuring = true;
   2985                 dispatchLayoutStep2();
   2986                 // now we can get the width and height from the children.
   2987                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
   2988             }
   2989         } else {
   2990             if (mHasFixedSize) {
   2991                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   2992                 return;
   2993             }
   2994             // custom onMeasure
   2995             if (mAdapterUpdateDuringMeasure) {
   2996                 eatRequestLayout();
   2997                 onEnterLayoutOrScroll();
   2998                 processAdapterUpdatesAndSetAnimationFlags();
   2999                 onExitLayoutOrScroll();
   3000 
   3001                 if (mState.mRunPredictiveAnimations) {
   3002                     mState.mInPreLayout = true;
   3003                 } else {
   3004                     // consume remaining updates to provide a consistent state with the layout pass.
   3005                     mAdapterHelper.consumeUpdatesInOnePass();
   3006                     mState.mInPreLayout = false;
   3007                 }
   3008                 mAdapterUpdateDuringMeasure = false;
   3009                 resumeRequestLayout(false);
   3010             }
   3011 
   3012             if (mAdapter != null) {
   3013                 mState.mItemCount = mAdapter.getItemCount();
   3014             } else {
   3015                 mState.mItemCount = 0;
   3016             }
   3017             eatRequestLayout();
   3018             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   3019             resumeRequestLayout(false);
   3020             mState.mInPreLayout = false; // clear
   3021         }
   3022     }
   3023 
   3024     /**
   3025      * Used when onMeasure is called before layout manager is set
   3026      */
   3027     void defaultOnMeasure(int widthSpec, int heightSpec) {
   3028         // calling LayoutManager here is not pretty but that API is already public and it is better
   3029         // than creating another method since this is internal.
   3030         final int width = LayoutManager.chooseSize(widthSpec,
   3031                 getPaddingLeft() + getPaddingRight(),
   3032                 getMinimumWidth());
   3033         final int height = LayoutManager.chooseSize(heightSpec,
   3034                 getPaddingTop() + getPaddingBottom(),
   3035                 getMinimumHeight());
   3036 
   3037         setMeasuredDimension(width, height);
   3038     }
   3039 
   3040     @Override
   3041     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   3042         super.onSizeChanged(w, h, oldw, oldh);
   3043         if (w != oldw || h != oldh) {
   3044             invalidateGlows();
   3045             // layout's w/h are updated during measure/layout steps.
   3046         }
   3047     }
   3048 
   3049     /**
   3050      * Sets the {@link ItemAnimator} that will handle animations involving changes
   3051      * to the items in this RecyclerView. By default, RecyclerView instantiates and
   3052      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
   3053      * enabled for the RecyclerView depends on the ItemAnimator and whether
   3054      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
   3055      * supports item animations}.
   3056      *
   3057      * @param animator The ItemAnimator being set. If null, no animations will occur
   3058      * when changes occur to the items in this RecyclerView.
   3059      */
   3060     public void setItemAnimator(ItemAnimator animator) {
   3061         if (mItemAnimator != null) {
   3062             mItemAnimator.endAnimations();
   3063             mItemAnimator.setListener(null);
   3064         }
   3065         mItemAnimator = animator;
   3066         if (mItemAnimator != null) {
   3067             mItemAnimator.setListener(mItemAnimatorListener);
   3068         }
   3069     }
   3070 
   3071     void onEnterLayoutOrScroll() {
   3072         mLayoutOrScrollCounter++;
   3073     }
   3074 
   3075     void onExitLayoutOrScroll() {
   3076         mLayoutOrScrollCounter--;
   3077         if (mLayoutOrScrollCounter < 1) {
   3078             if (DEBUG && mLayoutOrScrollCounter < 0) {
   3079                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
   3080                         + "Some calls are not matching");
   3081             }
   3082             mLayoutOrScrollCounter = 0;
   3083             dispatchContentChangedIfNecessary();
   3084             dispatchPendingImportantForAccessibilityChanges();
   3085         }
   3086     }
   3087 
   3088     boolean isAccessibilityEnabled() {
   3089         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
   3090     }
   3091 
   3092     private void dispatchContentChangedIfNecessary() {
   3093         final int flags = mEatenAccessibilityChangeFlags;
   3094         mEatenAccessibilityChangeFlags = 0;
   3095         if (flags != 0 && isAccessibilityEnabled()) {
   3096             final AccessibilityEvent event = AccessibilityEvent.obtain();
   3097             event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
   3098             event.setContentChangeTypes(flags);
   3099             sendAccessibilityEventUnchecked(event);
   3100         }
   3101     }
   3102 
   3103     /**
   3104      * Returns whether RecyclerView is currently computing a layout.
   3105      * <p>
   3106      * If this method returns true, it means that RecyclerView is in a lockdown state and any
   3107      * attempt to update adapter contents will result in an exception because adapter contents
   3108      * cannot be changed while RecyclerView is trying to compute the layout.
   3109      * <p>
   3110      * It is very unlikely that your code will be running during this state as it is
   3111      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
   3112      * in response to system events (touch, accessibility etc).
   3113      * <p>
   3114      * This case may happen if you have some custom logic to change adapter contents in
   3115      * response to a View callback (e.g. focus change callback) which might be triggered during a
   3116      * layout calculation. In these cases, you should just postpone the change using a Handler or a
   3117      * similar mechanism.
   3118      *
   3119      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
   3120      *         otherwise
   3121      */
   3122     public boolean isComputingLayout() {
   3123         return mLayoutOrScrollCounter > 0;
   3124     }
   3125 
   3126     /**
   3127      * Returns true if an accessibility event should not be dispatched now. This happens when an
   3128      * accessibility request arrives while RecyclerView does not have a stable state which is very
   3129      * hard to handle for a LayoutManager. Instead, this method records necessary information about
   3130      * the event and dispatches a window change event after the critical section is finished.
   3131      *
   3132      * @return True if the accessibility event should be postponed.
   3133      */
   3134     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
   3135         if (isComputingLayout()) {
   3136             int type = 0;
   3137             if (event != null) {
   3138                 type = event.getContentChangeTypes();
   3139             }
   3140             if (type == 0) {
   3141                 type = AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
   3142             }
   3143             mEatenAccessibilityChangeFlags |= type;
   3144             return true;
   3145         }
   3146         return false;
   3147     }
   3148 
   3149     @Override
   3150     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
   3151         if (shouldDeferAccessibilityEvent(event)) {
   3152             return;
   3153         }
   3154         super.sendAccessibilityEventUnchecked(event);
   3155     }
   3156 
   3157     /**
   3158      * Gets the current ItemAnimator for this RecyclerView. A null return value
   3159      * indicates that there is no animator and that item changes will happen without
   3160      * any animations. By default, RecyclerView instantiates and
   3161      * uses an instance of {@link DefaultItemAnimator}.
   3162      *
   3163      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
   3164      * when changes occur to the items in this RecyclerView.
   3165      */
   3166     public ItemAnimator getItemAnimator() {
   3167         return mItemAnimator;
   3168     }
   3169 
   3170     /**
   3171      * Post a runnable to the next frame to run pending item animations. Only the first such
   3172      * request will be posted, governed by the mPostedAnimatorRunner flag.
   3173      */
   3174     void postAnimationRunner() {
   3175         if (!mPostedAnimatorRunner && mIsAttached) {
   3176             postOnAnimation(mItemAnimatorRunner);
   3177             mPostedAnimatorRunner = true;
   3178         }
   3179     }
   3180 
   3181     private boolean predictiveItemAnimationsEnabled() {
   3182         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
   3183     }
   3184 
   3185     /**
   3186      * Consumes adapter updates and calculates which type of animations we want to run.
   3187      * Called in onMeasure and dispatchLayout.
   3188      * <p>
   3189      * This method may process only the pre-layout state of updates or all of them.
   3190      */
   3191     private void processAdapterUpdatesAndSetAnimationFlags() {
   3192         if (mDataSetHasChangedAfterLayout) {
   3193             // Processing these items have no value since data set changed unexpectedly.
   3194             // Instead, we just reset it.
   3195             mAdapterHelper.reset();
   3196             mLayout.onItemsChanged(this);
   3197         }
   3198         // simple animations are a subset of advanced animations (which will cause a
   3199         // pre-layout step)
   3200         // If layout supports predictive animations, pre-process to decide if we want to run them
   3201         if (predictiveItemAnimationsEnabled()) {
   3202             mAdapterHelper.preProcess();
   3203         } else {
   3204             mAdapterHelper.consumeUpdatesInOnePass();
   3205         }
   3206         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
   3207         mState.mRunSimpleAnimations = mFirstLayoutComplete
   3208                 && mItemAnimator != null
   3209                 && (mDataSetHasChangedAfterLayout
   3210                         || animationTypeSupported
   3211                         || mLayout.mRequestedSimpleAnimations)
   3212                 && (!mDataSetHasChangedAfterLayout
   3213                         || mAdapter.hasStableIds());
   3214         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
   3215                 && animationTypeSupported
   3216                 && !mDataSetHasChangedAfterLayout
   3217                 && predictiveItemAnimationsEnabled();
   3218     }
   3219 
   3220     /**
   3221      * Wrapper around layoutChildren() that handles animating changes caused by layout.
   3222      * Animations work on the assumption that there are five different kinds of items
   3223      * in play:
   3224      * PERSISTENT: items are visible before and after layout
   3225      * REMOVED: items were visible before layout and were removed by the app
   3226      * ADDED: items did not exist before layout and were added by the app
   3227      * DISAPPEARING: items exist in the data set before/after, but changed from
   3228      * visible to non-visible in the process of layout (they were moved off
   3229      * screen as a side-effect of other changes)
   3230      * APPEARING: items exist in the data set before/after, but changed from
   3231      * non-visible to visible in the process of layout (they were moved on
   3232      * screen as a side-effect of other changes)
   3233      * The overall approach figures out what items exist before/after layout and
   3234      * infers one of the five above states for each of the items. Then the animations
   3235      * are set up accordingly:
   3236      * PERSISTENT views are animated via
   3237      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3238      * DISAPPEARING views are animated via
   3239      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3240      * APPEARING views are animated via
   3241      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3242      * and changed views are animated via
   3243      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
   3244      */
   3245     void dispatchLayout() {
   3246         if (mAdapter == null) {
   3247             Log.e(TAG, "No adapter attached; skipping layout");
   3248             // leave the state in START
   3249             return;
   3250         }
   3251         if (mLayout == null) {
   3252             Log.e(TAG, "No layout manager attached; skipping layout");
   3253             // leave the state in START
   3254             return;
   3255         }
   3256         mState.mIsMeasuring = false;
   3257         if (mState.mLayoutStep == State.STEP_START) {
   3258             dispatchLayoutStep1();
   3259             mLayout.setExactMeasureSpecsFrom(this);
   3260             dispatchLayoutStep2();
   3261         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
   3262                 || mLayout.getHeight() != getHeight()) {
   3263             // First 2 steps are done in onMeasure but looks like we have to run again due to
   3264             // changed size.
   3265             mLayout.setExactMeasureSpecsFrom(this);
   3266             dispatchLayoutStep2();
   3267         } else {
   3268             // always make sure we sync them (to ensure mode is exact)
   3269             mLayout.setExactMeasureSpecsFrom(this);
   3270         }
   3271         dispatchLayoutStep3();
   3272     }
   3273 
   3274     private void saveFocusInfo() {
   3275         View child = null;
   3276         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
   3277             child = getFocusedChild();
   3278         }
   3279 
   3280         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
   3281         if (focusedVh == null) {
   3282             resetFocusInfo();
   3283         } else {
   3284             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
   3285             // mFocusedItemPosition should hold the current adapter position of the previously
   3286             // focused item. If the item is removed, we store the previous adapter position of the
   3287             // removed item.
   3288             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
   3289                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
   3290                             : focusedVh.getAdapterPosition());
   3291             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
   3292         }
   3293     }
   3294 
   3295     private void resetFocusInfo() {
   3296         mState.mFocusedItemId = NO_ID;
   3297         mState.mFocusedItemPosition = NO_POSITION;
   3298         mState.mFocusedSubChildId = View.NO_ID;
   3299     }
   3300 
   3301     /**
   3302      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
   3303      * previously focused item. It first traverses the adapter forward to find a focusable candidate
   3304      * and if no such candidate is found, it reverses the focus search direction for the items
   3305      * before the mFocusedItemPosition'th index;
   3306      * @return The best candidate to request focus on, or null if no such candidate exists. Null
   3307      * indicates all the existing adapter items are unfocusable.
   3308      */
   3309     @Nullable
   3310     private View findNextViewToFocus() {
   3311         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
   3312                 : 0;
   3313         ViewHolder nextFocus;
   3314         final int itemCount = mState.getItemCount();
   3315         for (int i = startFocusSearchIndex; i < itemCount; i++) {
   3316             nextFocus = findViewHolderForAdapterPosition(i);
   3317             if (nextFocus == null) {
   3318                 break;
   3319             }
   3320             if (nextFocus.itemView.hasFocusable()) {
   3321                 return nextFocus.itemView;
   3322             }
   3323         }
   3324         final int limit = Math.min(itemCount, startFocusSearchIndex);
   3325         for (int i = limit - 1; i >= 0; i--) {
   3326             nextFocus = findViewHolderForAdapterPosition(i);
   3327             if (nextFocus == null) {
   3328                 return null;
   3329             }
   3330             if (nextFocus.itemView.hasFocusable()) {
   3331                 return nextFocus.itemView;
   3332             }
   3333         }
   3334         return null;
   3335     }
   3336 
   3337     private void recoverFocusFromState() {
   3338         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
   3339                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
   3340                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
   3341             // No-op if either of these cases happens:
   3342             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
   3343             // before its children and is focused (i.e. it already stole the focus away from its
   3344             // descendants).
   3345             return;
   3346         }
   3347         // only recover focus if RV itself has the focus or the focused view is hidden
   3348         if (!isFocused()) {
   3349             final View focusedChild = getFocusedChild();
   3350             if (IGNORE_DETACHED_FOCUSED_CHILD
   3351                     && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
   3352                 // Special handling of API 15-. A focused child can be invalid because mFocus is not
   3353                 // cleared when the child is detached (mParent = null),
   3354                 // This happens because clearFocus on API 15- does not invalidate mFocus of its
   3355                 // parent when this child is detached.
   3356                 // For API 16+, this is not an issue because requestFocus takes care of clearing the
   3357                 // prior detached focused child. For API 15- the problem happens in 2 cases because
   3358                 // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
   3359                 // for the current focused item which calls clearChild or 2. when the prior focused
   3360                 // child is removed, removeDetachedView called in layout step 3 which calls
   3361                 // clearChild. We should ignore this invalid focused child in all our calculations
   3362                 // for the next view to receive focus, and apply the focus recovery logic instead.
   3363                 if (mChildHelper.getChildCount() == 0) {
   3364                     // No children left. Request focus on the RV itself since one of its children
   3365                     // was holding focus previously.
   3366                     requestFocus();
   3367                     return;
   3368                 }
   3369             } else if (!mChildHelper.isHidden(focusedChild)) {
   3370                 // If the currently focused child is hidden, apply the focus recovery logic.
   3371                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
   3372                 return;
   3373             }
   3374         }
   3375         ViewHolder focusTarget = null;
   3376         // RV first attempts to locate the previously focused item to request focus on using
   3377         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
   3378         // find the next best candidate to request focus on based on mFocusedItemPosition.
   3379         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
   3380             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
   3381         }
   3382         View viewToFocus = null;
   3383         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
   3384                 || !focusTarget.itemView.hasFocusable()) {
   3385             if (mChildHelper.getChildCount() > 0) {
   3386                 // At this point, RV has focus and either of these conditions are true:
   3387                 // 1. There's no previously focused item either because RV received focused before
   3388                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
   3389                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
   3390                 // focusable. In either of these cases, we make sure that RV still passes down the
   3391                 // focus to one of its focusable children using a best-effort algorithm.
   3392                 viewToFocus = findNextViewToFocus();
   3393             }
   3394         } else {
   3395             // looks like the focused item has been replaced with another view that represents the
   3396             // same item in the adapter. Request focus on that.
   3397             viewToFocus = focusTarget.itemView;
   3398         }
   3399 
   3400         if (viewToFocus != null) {
   3401             if (mState.mFocusedSubChildId != NO_ID) {
   3402                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
   3403                 if (child != null && child.isFocusable()) {
   3404                     viewToFocus = child;
   3405                 }
   3406             }
   3407             viewToFocus.requestFocus();
   3408         }
   3409     }
   3410 
   3411     private int getDeepestFocusedViewWithId(View view) {
   3412         int lastKnownId = view.getId();
   3413         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
   3414             view = ((ViewGroup) view).getFocusedChild();
   3415             final int id = view.getId();
   3416             if (id != View.NO_ID) {
   3417                 lastKnownId = view.getId();
   3418             }
   3419         }
   3420         return lastKnownId;
   3421     }
   3422 
   3423     /**
   3424      * The first step of a layout where we;
   3425      * - process adapter updates
   3426      * - decide which animation should run
   3427      * - save information about current views
   3428      * - If necessary, run predictive layout and save its information
   3429      */
   3430     private void dispatchLayoutStep1() {
   3431         mState.assertLayoutStep(State.STEP_START);
   3432         mState.mIsMeasuring = false;
   3433         eatRequestLayout();
   3434         mViewInfoStore.clear();
   3435         onEnterLayoutOrScroll();
   3436         processAdapterUpdatesAndSetAnimationFlags();
   3437         saveFocusInfo();
   3438         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
   3439         mItemsAddedOrRemoved = mItemsChanged = false;
   3440         mState.mInPreLayout = mState.mRunPredictiveAnimations;
   3441         mState.mItemCount = mAdapter.getItemCount();
   3442         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   3443 
   3444         if (mState.mRunSimpleAnimations) {
   3445             // Step 0: Find out where all non-removed items are, pre-layout
   3446             int count = mChildHelper.getChildCount();
   3447             for (int i = 0; i < count; ++i) {
   3448                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3449                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
   3450                     continue;
   3451                 }
   3452                 final ItemHolderInfo animationInfo = mItemAnimator
   3453                         .recordPreLayoutInformation(mState, holder,
   3454                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
   3455                                 holder.getUnmodifiedPayloads());
   3456                 mViewInfoStore.addToPreLayout(holder, animationInfo);
   3457                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
   3458                         && !holder.shouldIgnore() && !holder.isInvalid()) {
   3459                     long key = getChangedHolderKey(holder);
   3460                     // This is NOT the only place where a ViewHolder is added to old change holders
   3461                     // list. There is another case where:
   3462                     //    * A VH is currently hidden but not deleted
   3463                     //    * The hidden item is changed in the adapter
   3464                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
   3465                     // When this case is detected, RV will un-hide that view and add to the old
   3466                     // change holders list.
   3467                     mViewInfoStore.addToOldChangeHolders(key, holder);
   3468                 }
   3469             }
   3470         }
   3471         if (mState.mRunPredictiveAnimations) {
   3472             // Step 1: run prelayout: This will use the old positions of items. The layout manager
   3473             // is expected to layout everything, even removed items (though not to add removed
   3474             // items back to the container). This gives the pre-layout position of APPEARING views
   3475             // which come into existence as part of the real layout.
   3476 
   3477             // Save old positions so that LayoutManager can run its mapping logic.
   3478             saveOldPositions();
   3479             final boolean didStructureChange = mState.mStructureChanged;
   3480             mState.mStructureChanged = false;
   3481             // temporarily disable flag because we are asking for previous layout
   3482             mLayout.onLayoutChildren(mRecycler, mState);
   3483             mState.mStructureChanged = didStructureChange;
   3484 
   3485             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
   3486                 final View child = mChildHelper.getChildAt(i);
   3487                 final ViewHolder viewHolder = getChildViewHolderInt(child);
   3488                 if (viewHolder.shouldIgnore()) {
   3489                     continue;
   3490                 }
   3491                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
   3492                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
   3493                     boolean wasHidden = viewHolder
   3494                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   3495                     if (!wasHidden) {
   3496                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
   3497                     }
   3498                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
   3499                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
   3500                     if (wasHidden) {
   3501                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
   3502                     } else {
   3503                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
   3504                     }
   3505                 }
   3506             }
   3507             // we don't process disappearing list because they may re-appear in post layout pass.
   3508             clearOldPositions();
   3509         } else {
   3510             clearOldPositions();
   3511         }
   3512         onExitLayoutOrScroll();
   3513         resumeRequestLayout(false);
   3514         mState.mLayoutStep = State.STEP_LAYOUT;
   3515     }
   3516 
   3517     /**
   3518      * The second layout step where we do the actual layout of the views for the final state.
   3519      * This step might be run multiple times if necessary (e.g. measure).
   3520      */
   3521     private void dispatchLayoutStep2() {
   3522         eatRequestLayout();
   3523         onEnterLayoutOrScroll();
   3524         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
   3525         mAdapterHelper.consumeUpdatesInOnePass();
   3526         mState.mItemCount = mAdapter.getItemCount();
   3527         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
   3528 
   3529         // Step 2: Run layout
   3530         mState.mInPreLayout = false;
   3531         mLayout.onLayoutChildren(mRecycler, mState);
   3532 
   3533         mState.mStructureChanged = false;
   3534         mPendingSavedState = null;
   3535 
   3536         // onLayoutChildren may have caused client code to disable item animations; re-check
   3537         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
   3538         mState.mLayoutStep = State.STEP_ANIMATIONS;
   3539         onExitLayoutOrScroll();
   3540         resumeRequestLayout(false);
   3541     }
   3542 
   3543     /**
   3544      * The final step of the layout where we save the information about views for animations,
   3545      * trigger animations and do any necessary cleanup.
   3546      */
   3547     private void dispatchLayoutStep3() {
   3548         mState.assertLayoutStep(State.STEP_ANIMATIONS);
   3549         eatRequestLayout();
   3550         onEnterLayoutOrScroll();
   3551         mState.mLayoutStep = State.STEP_START;
   3552         if (mState.mRunSimpleAnimations) {
   3553             // Step 3: Find out where things are now, and process change animations.
   3554             // traverse list in reverse because we may call animateChange in the loop which may
   3555             // remove the target view holder.
   3556             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
   3557                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3558                 if (holder.shouldIgnore()) {
   3559                     continue;
   3560                 }
   3561                 long key = getChangedHolderKey(holder);
   3562                 final ItemHolderInfo animationInfo = mItemAnimator
   3563                         .recordPostLayoutInformation(mState, holder);
   3564                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
   3565                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
   3566                     // run a change animation
   3567 
   3568                     // If an Item is CHANGED but the updated version is disappearing, it creates
   3569                     // a conflicting case.
   3570                     // Since a view that is marked as disappearing is likely to be going out of
   3571                     // bounds, we run a change animation. Both views will be cleaned automatically
   3572                     // once their animations finish.
   3573                     // On the other hand, if it is the same view holder instance, we run a
   3574                     // disappearing animation instead because we are not going to rebind the updated
   3575                     // VH unless it is enforced by the layout manager.
   3576                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
   3577                             oldChangeViewHolder);
   3578                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
   3579                     if (oldDisappearing && oldChangeViewHolder == holder) {
   3580                         // run disappear animation instead of change
   3581                         mViewInfoStore.addToPostLayout(holder, animationInfo);
   3582                     } else {
   3583                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
   3584                                 oldChangeViewHolder);
   3585                         // we add and remove so that any post info is merged.
   3586                         mViewInfoStore.addToPostLayout(holder, animationInfo);
   3587                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
   3588                         if (preInfo == null) {
   3589                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
   3590                         } else {
   3591                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
   3592                                     oldDisappearing, newDisappearing);
   3593                         }
   3594                     }
   3595                 } else {
   3596                     mViewInfoStore.addToPostLayout(holder, animationInfo);
   3597                 }
   3598             }
   3599 
   3600             // Step 4: Process view info lists and trigger animations
   3601             mViewInfoStore.process(mViewInfoProcessCallback);
   3602         }
   3603 
   3604         mLayout.removeAndRecycleScrapInt(mRecycler);
   3605         mState.mPreviousLayoutItemCount = mState.mItemCount;
   3606         mDataSetHasChangedAfterLayout = false;
   3607         mState.mRunSimpleAnimations = false;
   3608 
   3609         mState.mRunPredictiveAnimations = false;
   3610         mLayout.mRequestedSimpleAnimations = false;
   3611         if (mRecycler.mChangedScrap != null) {
   3612             mRecycler.mChangedScrap.clear();
   3613         }
   3614         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
   3615             // Initial prefetch has expanded cache, so reset until next prefetch.
   3616             // This prevents initial prefetches from expanding the cache permanently.
   3617             mLayout.mPrefetchMaxCountObserved = 0;
   3618             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
   3619             mRecycler.updateViewCacheSize();
   3620         }
   3621 
   3622         mLayout.onLayoutCompleted(mState);
   3623         onExitLayoutOrScroll();
   3624         resumeRequestLayout(false);
   3625         mViewInfoStore.clear();
   3626         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
   3627             dispatchOnScrolled(0, 0);
   3628         }
   3629         recoverFocusFromState();
   3630         resetFocusInfo();
   3631     }
   3632 
   3633     /**
   3634      * This handles the case where there is an unexpected VH missing in the pre-layout map.
   3635      * <p>
   3636      * We might be able to detect the error in the application which will help the developer to
   3637      * resolve the issue.
   3638      * <p>
   3639      * If it is not an expected error, we at least print an error to notify the developer and ignore
   3640      * the animation.
   3641      *
   3642      * https://code.google.com/p/android/issues/detail?id=193958
   3643      *
   3644      * @param key The change key
   3645      * @param holder Current ViewHolder
   3646      * @param oldChangeViewHolder Changed ViewHolder
   3647      */
   3648     private void handleMissingPreInfoForChangeError(long key,
   3649             ViewHolder holder, ViewHolder oldChangeViewHolder) {
   3650         // check if two VH have the same key, if so, print that as an error
   3651         final int childCount = mChildHelper.getChildCount();
   3652         for (int i = 0; i < childCount; i++) {
   3653             View view = mChildHelper.getChildAt(i);
   3654             ViewHolder other = getChildViewHolderInt(view);
   3655             if (other == holder) {
   3656                 continue;
   3657             }
   3658             final long otherKey = getChangedHolderKey(other);
   3659             if (otherKey == key) {
   3660                 if (mAdapter != null && mAdapter.hasStableIds()) {
   3661                     throw new IllegalStateException("Two different ViewHolders have the same stable"
   3662                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
   3663                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
   3664                 } else {
   3665                     throw new IllegalStateException("Two different ViewHolders have the same change"
   3666                             + " ID. This might happen due to inconsistent Adapter update events or"
   3667                             + " if the LayoutManager lays out the same View multiple times."
   3668                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
   3669                 }
   3670             }
   3671         }
   3672         // Very unlikely to happen but if it does, notify the developer.
   3673         Log.e(TAG, "Problem while matching changed view holders with the new"
   3674                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
   3675                 + " cannot be found but it is necessary for " + holder);
   3676     }
   3677 
   3678     /**
   3679      * Records the animation information for a view holder that was bounced from hidden list. It
   3680      * also clears the bounce back flag.
   3681      */
   3682     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
   3683             ItemHolderInfo animationInfo) {
   3684         // looks like this view bounced back from hidden list!
   3685         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   3686         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
   3687                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
   3688             long key = getChangedHolderKey(viewHolder);
   3689             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
   3690         }
   3691         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
   3692     }
   3693 
   3694     private void findMinMaxChildLayoutPositions(int[] into) {
   3695         final int count = mChildHelper.getChildCount();
   3696         if (count == 0) {
   3697             into[0] = NO_POSITION;
   3698             into[1] = NO_POSITION;
   3699             return;
   3700         }
   3701         int minPositionPreLayout = Integer.MAX_VALUE;
   3702         int maxPositionPreLayout = Integer.MIN_VALUE;
   3703         for (int i = 0; i < count; ++i) {
   3704             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3705             if (holder.shouldIgnore()) {
   3706                 continue;
   3707             }
   3708             final int pos = holder.getLayoutPosition();
   3709             if (pos < minPositionPreLayout) {
   3710                 minPositionPreLayout = pos;
   3711             }
   3712             if (pos > maxPositionPreLayout) {
   3713                 maxPositionPreLayout = pos;
   3714             }
   3715         }
   3716         into[0] = minPositionPreLayout;
   3717         into[1] = maxPositionPreLayout;
   3718     }
   3719 
   3720     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
   3721         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   3722         return mMinMaxLayoutPositions[0] != minPositionPreLayout
   3723                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
   3724     }
   3725 
   3726     @Override
   3727     protected void removeDetachedView(View child, boolean animate) {
   3728         ViewHolder vh = getChildViewHolderInt(child);
   3729         if (vh != null) {
   3730             if (vh.isTmpDetached()) {
   3731                 vh.clearTmpDetachFlag();
   3732             } else if (!vh.shouldIgnore()) {
   3733                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
   3734                         + " is not flagged as tmp detached." + vh);
   3735             }
   3736         }
   3737         dispatchChildDetached(child);
   3738         super.removeDetachedView(child, animate);
   3739     }
   3740 
   3741     /**
   3742      * Returns a unique key to be used while handling change animations.
   3743      * It might be child's position or stable id depending on the adapter type.
   3744      */
   3745     long getChangedHolderKey(ViewHolder holder) {
   3746         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
   3747     }
   3748 
   3749     void animateAppearance(@NonNull ViewHolder itemHolder,
   3750             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
   3751         itemHolder.setIsRecyclable(false);
   3752         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
   3753             postAnimationRunner();
   3754         }
   3755     }
   3756 
   3757     void animateDisappearance(@NonNull ViewHolder holder,
   3758             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
   3759         addAnimatingView(holder);
   3760         holder.setIsRecyclable(false);
   3761         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
   3762             postAnimationRunner();
   3763         }
   3764     }
   3765 
   3766     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
   3767             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
   3768             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
   3769         oldHolder.setIsRecyclable(false);
   3770         if (oldHolderDisappearing) {
   3771             addAnimatingView(oldHolder);
   3772         }
   3773         if (oldHolder != newHolder) {
   3774             if (newHolderDisappearing) {
   3775                 addAnimatingView(newHolder);
   3776             }
   3777             oldHolder.mShadowedHolder = newHolder;
   3778             // old holder should disappear after animation ends
   3779             addAnimatingView(oldHolder);
   3780             mRecycler.unscrapView(oldHolder);
   3781             newHolder.setIsRecyclable(false);
   3782             newHolder.mShadowingHolder = oldHolder;
   3783         }
   3784         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
   3785             postAnimationRunner();
   3786         }
   3787     }
   3788 
   3789     @Override
   3790     protected void onLayout(boolean changed, int l, int t, int r, int b) {
   3791         Trace.beginSection(TRACE_ON_LAYOUT_TAG);
   3792         dispatchLayout();
   3793         Trace.endSection();
   3794         mFirstLayoutComplete = true;
   3795     }
   3796 
   3797     @Override
   3798     public void requestLayout() {
   3799         if (mEatRequestLayout == 0 && !mLayoutFrozen) {
   3800             super.requestLayout();
   3801         } else {
   3802             mLayoutRequestEaten = true;
   3803         }
   3804     }
   3805 
   3806     void markItemDecorInsetsDirty() {
   3807         final int childCount = mChildHelper.getUnfilteredChildCount();
   3808         for (int i = 0; i < childCount; i++) {
   3809             final View child = mChildHelper.getUnfilteredChildAt(i);
   3810             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   3811         }
   3812         mRecycler.markItemDecorInsetsDirty();
   3813     }
   3814 
   3815     @Override
   3816     public void draw(Canvas c) {
   3817         super.draw(c);
   3818 
   3819         final int count = mItemDecorations.size();
   3820         for (int i = 0; i < count; i++) {
   3821             mItemDecorations.get(i).onDrawOver(c, this, mState);
   3822         }
   3823         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
   3824         // need find children closest to edges. Not sure if it is worth the effort.
   3825         boolean needsInvalidate = false;
   3826         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
   3827             final int restore = c.save();
   3828             final int padding = mClipToPadding ? getPaddingBottom() : 0;
   3829             c.rotate(270);
   3830             c.translate(-getHeight() + padding, 0);
   3831             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
   3832             c.restoreToCount(restore);
   3833         }
   3834         if (mTopGlow != null && !mTopGlow.isFinished()) {
   3835             final int restore = c.save();
   3836             if (mClipToPadding) {
   3837                 c.translate(getPaddingLeft(), getPaddingTop());
   3838             }
   3839             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
   3840             c.restoreToCount(restore);
   3841         }
   3842         if (mRightGlow != null && !mRightGlow.isFinished()) {
   3843             final int restore = c.save();
   3844             final int width = getWidth();
   3845             final int padding = mClipToPadding ? getPaddingTop() : 0;
   3846             c.rotate(90);
   3847             c.translate(-padding, -width);
   3848             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
   3849             c.restoreToCount(restore);
   3850         }
   3851         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
   3852             final int restore = c.save();
   3853             c.rotate(180);
   3854             if (mClipToPadding) {
   3855                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
   3856             } else {
   3857                 c.translate(-getWidth(), -getHeight());
   3858             }
   3859             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
   3860             c.restoreToCount(restore);
   3861         }
   3862 
   3863         // If some views are animating, ItemDecorators are likely to move/change with them.
   3864         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
   3865         // display lists are not invalidated.
   3866         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
   3867                 && mItemAnimator.isRunning()) {
   3868             needsInvalidate = true;
   3869         }
   3870 
   3871         if (needsInvalidate) {
   3872             postInvalidateOnAnimation();
   3873         }
   3874     }
   3875 
   3876     @Override
   3877     public void onDraw(Canvas c) {
   3878         super.onDraw(c);
   3879 
   3880         final int count = mItemDecorations.size();
   3881         for (int i = 0; i < count; i++) {
   3882             mItemDecorations.get(i).onDraw(c, this, mState);
   3883         }
   3884     }
   3885 
   3886     @Override
   3887     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
   3888         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
   3889     }
   3890 
   3891     @Override
   3892     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
   3893         if (mLayout == null) {
   3894             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3895         }
   3896         return mLayout.generateDefaultLayoutParams();
   3897     }
   3898 
   3899     @Override
   3900     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
   3901         if (mLayout == null) {
   3902             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3903         }
   3904         return mLayout.generateLayoutParams(getContext(), attrs);
   3905     }
   3906 
   3907     @Override
   3908     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
   3909         if (mLayout == null) {
   3910             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3911         }
   3912         return mLayout.generateLayoutParams(p);
   3913     }
   3914 
   3915     /**
   3916      * Returns true if RecyclerView is currently running some animations.
   3917      * <p>
   3918      * If you want to be notified when animations are finished, use
   3919      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
   3920      *
   3921      * @return True if there are some item animations currently running or waiting to be started.
   3922      */
   3923     public boolean isAnimating() {
   3924         return mItemAnimator != null && mItemAnimator.isRunning();
   3925     }
   3926 
   3927     void saveOldPositions() {
   3928         final int childCount = mChildHelper.getUnfilteredChildCount();
   3929         for (int i = 0; i < childCount; i++) {
   3930             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3931             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
   3932                 throw new IllegalStateException("view holder cannot have position -1 unless it"
   3933                         + " is removed");
   3934             }
   3935             if (!holder.shouldIgnore()) {
   3936                 holder.saveOldPosition();
   3937             }
   3938         }
   3939     }
   3940 
   3941     void clearOldPositions() {
   3942         final int childCount = mChildHelper.getUnfilteredChildCount();
   3943         for (int i = 0; i < childCount; i++) {
   3944             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3945             if (!holder.shouldIgnore()) {
   3946                 holder.clearOldPosition();
   3947             }
   3948         }
   3949         mRecycler.clearOldPositions();
   3950     }
   3951 
   3952     void offsetPositionRecordsForMove(int from, int to) {
   3953         final int childCount = mChildHelper.getUnfilteredChildCount();
   3954         final int start, end, inBetweenOffset;
   3955         if (from < to) {
   3956             start = from;
   3957             end = to;
   3958             inBetweenOffset = -1;
   3959         } else {
   3960             start = to;
   3961             end = from;
   3962             inBetweenOffset = 1;
   3963         }
   3964 
   3965         for (int i = 0; i < childCount; i++) {
   3966             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3967             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   3968                 continue;
   3969             }
   3970             if (DEBUG) {
   3971                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
   3972                         + holder);
   3973             }
   3974             if (holder.mPosition == from) {
   3975                 holder.offsetPosition(to - from, false);
   3976             } else {
   3977                 holder.offsetPosition(inBetweenOffset, false);
   3978             }
   3979 
   3980             mState.mStructureChanged = true;
   3981         }
   3982         mRecycler.offsetPositionRecordsForMove(from, to);
   3983         requestLayout();
   3984     }
   3985 
   3986     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
   3987         final int childCount = mChildHelper.getUnfilteredChildCount();
   3988         for (int i = 0; i < childCount; i++) {
   3989             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3990             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
   3991                 if (DEBUG) {
   3992                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
   3993                             + holder + " now at position " + (holder.mPosition + itemCount));
   3994                 }
   3995                 holder.offsetPosition(itemCount, false);
   3996                 mState.mStructureChanged = true;
   3997             }
   3998         }
   3999         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
   4000         requestLayout();
   4001     }
   4002 
   4003     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
   4004             boolean applyToPreLayout) {
   4005         final int positionEnd = positionStart + itemCount;
   4006         final int childCount = mChildHelper.getUnfilteredChildCount();
   4007         for (int i = 0; i < childCount; i++) {
   4008             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4009             if (holder != null && !holder.shouldIgnore()) {
   4010                 if (holder.mPosition >= positionEnd) {
   4011                     if (DEBUG) {
   4012                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
   4013                                 + " holder " + holder + " now at position "
   4014                                 + (holder.mPosition - itemCount));
   4015                     }
   4016                     holder.offsetPosition(-itemCount, applyToPreLayout);
   4017                     mState.mStructureChanged = true;
   4018                 } else if (holder.mPosition >= positionStart) {
   4019                     if (DEBUG) {
   4020                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
   4021                                 + " holder " + holder + " now REMOVED");
   4022                     }
   4023                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
   4024                             applyToPreLayout);
   4025                     mState.mStructureChanged = true;
   4026                 }
   4027             }
   4028         }
   4029         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
   4030         requestLayout();
   4031     }
   4032 
   4033     /**
   4034      * Rebind existing views for the given range, or create as needed.
   4035      *
   4036      * @param positionStart Adapter position to start at
   4037      * @param itemCount Number of views that must explicitly be rebound
   4038      */
   4039     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
   4040         final int childCount = mChildHelper.getUnfilteredChildCount();
   4041         final int positionEnd = positionStart + itemCount;
   4042 
   4043         for (int i = 0; i < childCount; i++) {
   4044             final View child = mChildHelper.getUnfilteredChildAt(i);
   4045             final ViewHolder holder = getChildViewHolderInt(child);
   4046             if (holder == null || holder.shouldIgnore()) {
   4047                 continue;
   4048             }
   4049             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
   4050                 // We re-bind these view holders after pre-processing is complete so that
   4051                 // ViewHolders have their final positions assigned.
   4052                 holder.addFlags(ViewHolder.FLAG_UPDATE);
   4053                 holder.addChangePayload(payload);
   4054                 // lp cannot be null since we get ViewHolder from it.
   4055                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   4056             }
   4057         }
   4058         mRecycler.viewRangeUpdate(positionStart, itemCount);
   4059     }
   4060 
   4061     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
   4062         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
   4063                 viewHolder.getUnmodifiedPayloads());
   4064     }
   4065 
   4066 
   4067     /**
   4068      * Call this method to signal that *all* adapter content has changed (generally, because of
   4069      * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
   4070      * be discarded or animated. Note that this work is deferred because RecyclerView requires a
   4071      * layout to resolve non-incremental changes to the data set.
   4072      *
   4073      * Attached items are labeled as position unknown, and may no longer be cached.
   4074      *
   4075      * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
   4076      * so calling this method *must* be associated with marking the cache invalid, so that the
   4077      * only valid items that remain in the cache, once layout occurs, are prefetched items.
   4078      */
   4079     void setDataSetChangedAfterLayout() {
   4080         if (mDataSetHasChangedAfterLayout) {
   4081             return;
   4082         }
   4083         mDataSetHasChangedAfterLayout = true;
   4084         final int childCount = mChildHelper.getUnfilteredChildCount();
   4085         for (int i = 0; i < childCount; i++) {
   4086             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4087             if (holder != null && !holder.shouldIgnore()) {
   4088                 holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   4089             }
   4090         }
   4091         mRecycler.setAdapterPositionsAsUnknown();
   4092 
   4093         // immediately mark all views as invalid, so prefetched views can be
   4094         // differentiated from views bound to previous data set - both in children, and cache
   4095         markKnownViewsInvalid();
   4096     }
   4097 
   4098     /**
   4099      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
   4100      * data change event.
   4101      */
   4102     void markKnownViewsInvalid() {
   4103         final int childCount = mChildHelper.getUnfilteredChildCount();
   4104         for (int i = 0; i < childCount; i++) {
   4105             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4106             if (holder != null && !holder.shouldIgnore()) {
   4107                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   4108             }
   4109         }
   4110         markItemDecorInsetsDirty();
   4111         mRecycler.markKnownViewsInvalid();
   4112     }
   4113 
   4114     /**
   4115      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
   4116      * will trigger a {@link #requestLayout()} call.
   4117      */
   4118     public void invalidateItemDecorations() {
   4119         if (mItemDecorations.size() == 0) {
   4120             return;
   4121         }
   4122         if (mLayout != null) {
   4123             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
   4124                     + " or layout");
   4125         }
   4126         markItemDecorInsetsDirty();
   4127         requestLayout();
   4128     }
   4129 
   4130     /**
   4131      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
   4132      * focus even if the View representing the Item is replaced during a layout calculation.
   4133      * <p>
   4134      * By default, this value is {@code true}.
   4135      *
   4136      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
   4137      * focus.
   4138      *
   4139      * @see #setPreserveFocusAfterLayout(boolean)
   4140      */
   4141     public boolean getPreserveFocusAfterLayout() {
   4142         return mPreserveFocusAfterLayout;
   4143     }
   4144 
   4145     /**
   4146      * Set whether the RecyclerView should try to keep the same Item focused after a layout
   4147      * calculation or not.
   4148      * <p>
   4149      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
   4150      * views may lose focus during a layout calculation as their state changes or they are replaced
   4151      * with another view due to type change or animation. In these cases, RecyclerView can request
   4152      * focus on the new view automatically.
   4153      *
   4154      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
   4155      *                                 layout calculations. Defaults to true.
   4156      *
   4157      * @see #getPreserveFocusAfterLayout()
   4158      */
   4159     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
   4160         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
   4161     }
   4162 
   4163     /**
   4164      * Retrieve the {@link ViewHolder} for the given child view.
   4165      *
   4166      * @param child Child of this RecyclerView to query for its ViewHolder
   4167      * @return The child view's ViewHolder
   4168      */
   4169     public ViewHolder getChildViewHolder(View child) {
   4170         final ViewParent parent = child.getParent();
   4171         if (parent != null && parent != this) {
   4172             throw new IllegalArgumentException("View " + child + " is not a direct child of "
   4173                     + this);
   4174         }
   4175         return getChildViewHolderInt(child);
   4176     }
   4177 
   4178     /**
   4179      * Traverses the ancestors of the given view and returns the item view that contains it and
   4180      * also a direct child of the RecyclerView. This returned view can be used to get the
   4181      * ViewHolder by calling {@link #getChildViewHolder(View)}.
   4182      *
   4183      * @param view The view that is a descendant of the RecyclerView.
   4184      *
   4185      * @return The direct child of the RecyclerView which contains the given view or null if the
   4186      * provided view is not a descendant of this RecyclerView.
   4187      *
   4188      * @see #getChildViewHolder(View)
   4189      * @see #findContainingViewHolder(View)
   4190      */
   4191     @Nullable
   4192     public View findContainingItemView(View view) {
   4193         ViewParent parent = view.getParent();
   4194         while (parent != null && parent != this && parent instanceof View) {
   4195             view = (View) parent;
   4196             parent = view.getParent();
   4197         }
   4198         return parent == this ? view : null;
   4199     }
   4200 
   4201     /**
   4202      * Returns the ViewHolder that contains the given view.
   4203      *
   4204      * @param view The view that is a descendant of the RecyclerView.
   4205      *
   4206      * @return The ViewHolder that contains the given view or null if the provided view is not a
   4207      * descendant of this RecyclerView.
   4208      */
   4209     @Nullable
   4210     public ViewHolder findContainingViewHolder(View view) {
   4211         View itemView = findContainingItemView(view);
   4212         return itemView == null ? null : getChildViewHolder(itemView);
   4213     }
   4214 
   4215 
   4216     static ViewHolder getChildViewHolderInt(View child) {
   4217         if (child == null) {
   4218             return null;
   4219         }
   4220         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
   4221     }
   4222 
   4223     /**
   4224      * @deprecated use {@link #getChildAdapterPosition(View)} or
   4225      * {@link #getChildLayoutPosition(View)}.
   4226      */
   4227     @Deprecated
   4228     public int getChildPosition(View child) {
   4229         return getChildAdapterPosition(child);
   4230     }
   4231 
   4232     /**
   4233      * Return the adapter position that the given child view corresponds to.
   4234      *
   4235      * @param child Child View to query
   4236      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
   4237      */
   4238     public int getChildAdapterPosition(View child) {
   4239         final ViewHolder holder = getChildViewHolderInt(child);
   4240         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
   4241     }
   4242 
   4243     /**
   4244      * Return the adapter position of the given child view as of the latest completed layout pass.
   4245      * <p>
   4246      * This position may not be equal to Item's adapter position if there are pending changes
   4247      * in the adapter which have not been reflected to the layout yet.
   4248      *
   4249      * @param child Child View to query
   4250      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
   4251      * the View is representing a removed item.
   4252      */
   4253     public int getChildLayoutPosition(View child) {
   4254         final ViewHolder holder = getChildViewHolderInt(child);
   4255         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
   4256     }
   4257 
   4258     /**
   4259      * Return the stable item id that the given child view corresponds to.
   4260      *
   4261      * @param child Child View to query
   4262      * @return Item id corresponding to the given view or {@link #NO_ID}
   4263      */
   4264     public long getChildItemId(View child) {
   4265         if (mAdapter == null || !mAdapter.hasStableIds()) {
   4266             return NO_ID;
   4267         }
   4268         final ViewHolder holder = getChildViewHolderInt(child);
   4269         return holder != null ? holder.getItemId() : NO_ID;
   4270     }
   4271 
   4272     /**
   4273      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
   4274      * {@link #findViewHolderForAdapterPosition(int)}
   4275      */
   4276     @Deprecated
   4277     public ViewHolder findViewHolderForPosition(int position) {
   4278         return findViewHolderForPosition(position, false);
   4279     }
   4280 
   4281     /**
   4282      * Return the ViewHolder for the item in the given position of the data set as of the latest
   4283      * layout pass.
   4284      * <p>
   4285      * This method checks only the children of RecyclerView. If the item at the given
   4286      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   4287      * <p>
   4288      * Note that when Adapter contents change, ViewHolder positions are not updated until the
   4289      * next layout calculation. If there are pending adapter updates, the return value of this
   4290      * method may not match your adapter contents. You can use
   4291      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
   4292      * <p>
   4293      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
   4294      * with the same layout position representing the same Item. In this case, the updated
   4295      * ViewHolder will be returned.
   4296      *
   4297      * @param position The position of the item in the data set of the adapter
   4298      * @return The ViewHolder at <code>position</code> or null if there is no such item
   4299      */
   4300     public ViewHolder findViewHolderForLayoutPosition(int position) {
   4301         return findViewHolderForPosition(position, false);
   4302     }
   4303 
   4304     /**
   4305      * Return the ViewHolder for the item in the given position of the data set. Unlike
   4306      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
   4307      * adapter changes that may not be reflected to the layout yet. On the other hand, if
   4308      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
   4309      * calculated yet, this method will return <code>null</code> since the new positions of views
   4310      * are unknown until the layout is calculated.
   4311      * <p>
   4312      * This method checks only the children of RecyclerView. If the item at the given
   4313      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   4314      * <p>
   4315      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
   4316      * representing the same Item. In this case, the updated ViewHolder will be returned.
   4317      *
   4318      * @param position The position of the item in the data set of the adapter
   4319      * @return The ViewHolder at <code>position</code> or null if there is no such item
   4320      */
   4321     public ViewHolder findViewHolderForAdapterPosition(int position) {
   4322         if (mDataSetHasChangedAfterLayout) {
   4323             return null;
   4324         }
   4325         final int childCount = mChildHelper.getUnfilteredChildCount();
   4326         // hidden VHs are not preferred but if that is the only one we find, we rather return it
   4327         ViewHolder hidden = null;
   4328         for (int i = 0; i < childCount; i++) {
   4329             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4330             if (holder != null && !holder.isRemoved()
   4331                     && getAdapterPositionFor(holder) == position) {
   4332                 if (mChildHelper.isHidden(holder.itemView)) {
   4333                     hidden = holder;
   4334                 } else {
   4335                     return holder;
   4336                 }
   4337             }
   4338         }
   4339         return hidden;
   4340     }
   4341 
   4342     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
   4343         final int childCount = mChildHelper.getUnfilteredChildCount();
   4344         ViewHolder hidden = null;
   4345         for (int i = 0; i < childCount; i++) {
   4346             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4347             if (holder != null && !holder.isRemoved()) {
   4348                 if (checkNewPosition) {
   4349                     if (holder.mPosition != position) {
   4350                         continue;
   4351                     }
   4352                 } else if (holder.getLayoutPosition() != position) {
   4353                     continue;
   4354                 }
   4355                 if (mChildHelper.isHidden(holder.itemView)) {
   4356                     hidden = holder;
   4357                 } else {
   4358                     return holder;
   4359                 }
   4360             }
   4361         }
   4362         // This method should not query cached views. It creates a problem during adapter updates
   4363         // when we are dealing with already laid out views. Also, for the public method, it is more
   4364         // reasonable to return null if position is not laid out.
   4365         return hidden;
   4366     }
   4367 
   4368     /**
   4369      * Return the ViewHolder for the item with the given id. The RecyclerView must
   4370      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
   4371      * return a non-null value.
   4372      * <p>
   4373      * This method checks only the children of RecyclerView. If the item with the given
   4374      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
   4375      *
   4376      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
   4377      * same id. In this case, the updated ViewHolder will be returned.
   4378      *
   4379      * @param id The id for the requested item
   4380      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
   4381      */
   4382     public ViewHolder findViewHolderForItemId(long id) {
   4383         if (mAdapter == null || !mAdapter.hasStableIds()) {
   4384             return null;
   4385         }
   4386         final int childCount = mChildHelper.getUnfilteredChildCount();
   4387         ViewHolder hidden = null;
   4388         for (int i = 0; i < childCount; i++) {
   4389             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4390             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
   4391                 if (mChildHelper.isHidden(holder.itemView)) {
   4392                     hidden = holder;
   4393                 } else {
   4394                     return holder;
   4395                 }
   4396             }
   4397         }
   4398         return hidden;
   4399     }
   4400 
   4401     /**
   4402      * Find the topmost view under the given point.
   4403      *
   4404      * @param x Horizontal position in pixels to search
   4405      * @param y Vertical position in pixels to search
   4406      * @return The child view under (x, y) or null if no matching child is found
   4407      */
   4408     public View findChildViewUnder(float x, float y) {
   4409         final int count = mChildHelper.getChildCount();
   4410         for (int i = count - 1; i >= 0; i--) {
   4411             final View child = mChildHelper.getChildAt(i);
   4412             final float translationX = child.getTranslationX();
   4413             final float translationY = child.getTranslationY();
   4414             if (x >= child.getLeft() + translationX
   4415                     && x <= child.getRight() + translationX
   4416                     && y >= child.getTop() + translationY
   4417                     && y <= child.getBottom() + translationY) {
   4418                 return child;
   4419             }
   4420         }
   4421         return null;
   4422     }
   4423 
   4424     @Override
   4425     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
   4426         return super.drawChild(canvas, child, drawingTime);
   4427     }
   4428 
   4429     /**
   4430      * Offset the bounds of all child views by <code>dy</code> pixels.
   4431      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   4432      *
   4433      * @param dy Vertical pixel offset to apply to the bounds of all child views
   4434      */
   4435     public void offsetChildrenVertical(int dy) {
   4436         final int childCount = mChildHelper.getChildCount();
   4437         for (int i = 0; i < childCount; i++) {
   4438             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
   4439         }
   4440     }
   4441 
   4442     /**
   4443      * Called when an item view is attached to this RecyclerView.
   4444      *
   4445      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   4446      * of child views as they become attached. This will be called before a
   4447      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
   4448      * changes.</p>
   4449      *
   4450      * @param child Child view that is now attached to this RecyclerView and its associated window
   4451      */
   4452     public void onChildAttachedToWindow(View child) {
   4453     }
   4454 
   4455     /**
   4456      * Called when an item view is detached from this RecyclerView.
   4457      *
   4458      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   4459      * of child views as they become detached. This will be called as a
   4460      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
   4461      *
   4462      * @param child Child view that is now detached from this RecyclerView and its associated window
   4463      */
   4464     public void onChildDetachedFromWindow(View child) {
   4465     }
   4466 
   4467     /**
   4468      * Offset the bounds of all child views by <code>dx</code> pixels.
   4469      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   4470      *
   4471      * @param dx Horizontal pixel offset to apply to the bounds of all child views
   4472      */
   4473     public void offsetChildrenHorizontal(int dx) {
   4474         final int childCount = mChildHelper.getChildCount();
   4475         for (int i = 0; i < childCount; i++) {
   4476             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
   4477         }
   4478     }
   4479 
   4480     /**
   4481      * Returns the bounds of the view including its decoration and margins.
   4482      *
   4483      * @param view The view element to check
   4484      * @param outBounds A rect that will receive the bounds of the element including its
   4485      *                  decoration and margins.
   4486      */
   4487     public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
   4488         getDecoratedBoundsWithMarginsInt(view, outBounds);
   4489     }
   4490 
   4491     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
   4492         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
   4493         final Rect insets = lp.mDecorInsets;
   4494         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
   4495                 view.getTop() - insets.top - lp.topMargin,
   4496                 view.getRight() + insets.right + lp.rightMargin,
   4497                 view.getBottom() + insets.bottom + lp.bottomMargin);
   4498     }
   4499 
   4500     Rect getItemDecorInsetsForChild(View child) {
   4501         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   4502         if (!lp.mInsetsDirty) {
   4503             return lp.mDecorInsets;
   4504         }
   4505 
   4506         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
   4507             // changed/invalid items should not be updated until they are rebound.
   4508             return lp.mDecorInsets;
   4509         }
   4510         final Rect insets = lp.mDecorInsets;
   4511         insets.set(0, 0, 0, 0);
   4512         final int decorCount = mItemDecorations.size();
   4513         for (int i = 0; i < decorCount; i++) {
   4514             mTempRect.set(0, 0, 0, 0);
   4515             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
   4516             insets.left += mTempRect.left;
   4517             insets.top += mTempRect.top;
   4518             insets.right += mTempRect.right;
   4519             insets.bottom += mTempRect.bottom;
   4520         }
   4521         lp.mInsetsDirty = false;
   4522         return insets;
   4523     }
   4524 
   4525     /**
   4526      * Called when the scroll position of this RecyclerView changes. Subclasses should use
   4527      * this method to respond to scrolling within the adapter's data set instead of an explicit
   4528      * listener.
   4529      *
   4530      * <p>This method will always be invoked before listeners. If a subclass needs to perform
   4531      * any additional upkeep or bookkeeping after scrolling but before listeners run,
   4532      * this is a good place to do so.</p>
   4533      *
   4534      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
   4535      * the distance scrolled in either direction within the adapter's data set instead of absolute
   4536      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
   4537      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
   4538      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
   4539      * do not correspond to the data set scroll position. However, some subclasses may choose
   4540      * to use these fields as special offsets.</p>
   4541      *
   4542      * @param dx horizontal distance scrolled in pixels
   4543      * @param dy vertical distance scrolled in pixels
   4544      */
   4545     public void onScrolled(int dx, int dy) {
   4546         // Do nothing
   4547     }
   4548 
   4549     void dispatchOnScrolled(int hresult, int vresult) {
   4550         mDispatchScrollCounter++;
   4551         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
   4552         // but some general-purpose code may choose to respond to changes this way.
   4553         final int scrollX = getScrollX();
   4554         final int scrollY = getScrollY();
   4555         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
   4556 
   4557         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
   4558         onScrolled(hresult, vresult);
   4559 
   4560         // Invoke listeners last. Subclassed view methods always handle the event first.
   4561         // All internal state is consistent by the time listeners are invoked.
   4562         if (mScrollListener != null) {
   4563             mScrollListener.onScrolled(this, hresult, vresult);
   4564         }
   4565         if (mScrollListeners != null) {
   4566             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   4567                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
   4568             }
   4569         }
   4570         mDispatchScrollCounter--;
   4571     }
   4572 
   4573     /**
   4574      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
   4575      * method to respond to state changes instead of an explicit listener.
   4576      *
   4577      * <p>This method will always be invoked before listeners, but after the LayoutManager
   4578      * responds to the scroll state change.</p>
   4579      *
   4580      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
   4581      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
   4582      */
   4583     public void onScrollStateChanged(int state) {
   4584         // Do nothing
   4585     }
   4586 
   4587     void dispatchOnScrollStateChanged(int state) {
   4588         // Let the LayoutManager go first; this allows it to bring any properties into
   4589         // a consistent state before the RecyclerView subclass responds.
   4590         if (mLayout != null) {
   4591             mLayout.onScrollStateChanged(state);
   4592         }
   4593 
   4594         // Let the RecyclerView subclass handle this event next; any LayoutManager property
   4595         // changes will be reflected by this time.
   4596         onScrollStateChanged(state);
   4597 
   4598         // Listeners go last. All other internal state is consistent by this point.
   4599         if (mScrollListener != null) {
   4600             mScrollListener.onScrollStateChanged(this, state);
   4601         }
   4602         if (mScrollListeners != null) {
   4603             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   4604                 mScrollListeners.get(i).onScrollStateChanged(this, state);
   4605             }
   4606         }
   4607     }
   4608 
   4609     /**
   4610      * Returns whether there are pending adapter updates which are not yet applied to the layout.
   4611      * <p>
   4612      * If this method returns <code>true</code>, it means that what user is currently seeing may not
   4613      * reflect them adapter contents (depending on what has changed).
   4614      * You may use this information to defer or cancel some operations.
   4615      * <p>
   4616      * This method returns true if RecyclerView has not yet calculated the first layout after it is
   4617      * attached to the Window or the Adapter has been replaced.
   4618      *
   4619      * @return True if there are some adapter updates which are not yet reflected to layout or false
   4620      * if layout is up to date.
   4621      */
   4622     public boolean hasPendingAdapterUpdates() {
   4623         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
   4624                 || mAdapterHelper.hasPendingUpdates();
   4625     }
   4626 
   4627     class ViewFlinger implements Runnable {
   4628         private int mLastFlingX;
   4629         private int mLastFlingY;
   4630         private OverScroller mScroller;
   4631         Interpolator mInterpolator = sQuinticInterpolator;
   4632 
   4633 
   4634         // When set to true, postOnAnimation callbacks are delayed until the run method completes
   4635         private boolean mEatRunOnAnimationRequest = false;
   4636 
   4637         // Tracks if postAnimationCallback should be re-attached when it is done
   4638         private boolean mReSchedulePostAnimationCallback = false;
   4639 
   4640         ViewFlinger() {
   4641             mScroller = new OverScroller(getContext(), sQuinticInterpolator);
   4642         }
   4643 
   4644         @Override
   4645         public void run() {
   4646             if (mLayout == null) {
   4647                 stop();
   4648                 return; // no layout, cannot scroll.
   4649             }
   4650             disableRunOnAnimationRequests();
   4651             consumePendingUpdateOperations();
   4652             // keep a local reference so that if it is changed during onAnimation method, it won't
   4653             // cause unexpected behaviors
   4654             final OverScroller scroller = mScroller;
   4655             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
   4656             if (scroller.computeScrollOffset()) {
   4657                 final int x = scroller.getCurrX();
   4658                 final int y = scroller.getCurrY();
   4659                 final int dx = x - mLastFlingX;
   4660                 final int dy = y - mLastFlingY;
   4661                 int hresult = 0;
   4662                 int vresult = 0;
   4663                 mLastFlingX = x;
   4664                 mLastFlingY = y;
   4665                 int overscrollX = 0, overscrollY = 0;
   4666                 if (mAdapter != null) {
   4667                     eatRequestLayout();
   4668                     onEnterLayoutOrScroll();
   4669                     Trace.beginSection(TRACE_SCROLL_TAG);
   4670                     if (dx != 0) {
   4671                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
   4672                         overscrollX = dx - hresult;
   4673                     }
   4674                     if (dy != 0) {
   4675                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
   4676                         overscrollY = dy - vresult;
   4677                     }
   4678                     Trace.endSection();
   4679                     repositionShadowingViews();
   4680 
   4681                     onExitLayoutOrScroll();
   4682                     resumeRequestLayout(false);
   4683 
   4684                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
   4685                             && smoothScroller.isRunning()) {
   4686                         final int adapterSize = mState.getItemCount();
   4687                         if (adapterSize == 0) {
   4688                             smoothScroller.stop();
   4689                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
   4690                             smoothScroller.setTargetPosition(adapterSize - 1);
   4691                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   4692                         } else {
   4693                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   4694                         }
   4695                     }
   4696                 }
   4697                 if (!mItemDecorations.isEmpty()) {
   4698                     invalidate();
   4699                 }
   4700                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   4701                     considerReleasingGlowsOnScroll(dx, dy);
   4702                 }
   4703                 if (overscrollX != 0 || overscrollY != 0) {
   4704                     final int vel = (int) scroller.getCurrVelocity();
   4705 
   4706                     int velX = 0;
   4707                     if (overscrollX != x) {
   4708                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
   4709                     }
   4710 
   4711                     int velY = 0;
   4712                     if (overscrollY != y) {
   4713                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
   4714                     }
   4715 
   4716                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   4717                         absorbGlows(velX, velY);
   4718                     }
   4719                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
   4720                             && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
   4721                         scroller.abortAnimation();
   4722                     }
   4723                 }
   4724                 if (hresult != 0 || vresult != 0) {
   4725                     dispatchOnScrolled(hresult, vresult);
   4726                 }
   4727 
   4728                 if (!awakenScrollBars()) {
   4729                     invalidate();
   4730                 }
   4731 
   4732                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
   4733                         && vresult == dy;
   4734                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
   4735                         && hresult == dx;
   4736                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
   4737                         || fullyConsumedVertical;
   4738 
   4739                 if (scroller.isFinished() || !fullyConsumedAny) {
   4740                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
   4741                     if (ALLOW_THREAD_GAP_WORK) {
   4742                         mPrefetchRegistry.clearPrefetchPositions();
   4743                     }
   4744                 } else {
   4745                     postOnAnimation();
   4746                     if (mGapWorker != null) {
   4747                         mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
   4748                     }
   4749                 }
   4750             }
   4751             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
   4752             if (smoothScroller != null) {
   4753                 if (smoothScroller.isPendingInitialRun()) {
   4754                     smoothScroller.onAnimation(0, 0);
   4755                 }
   4756                 if (!mReSchedulePostAnimationCallback) {
   4757                     smoothScroller.stop(); //stop if it does not trigger any scroll
   4758                 }
   4759             }
   4760             enableRunOnAnimationRequests();
   4761         }
   4762 
   4763         private void disableRunOnAnimationRequests() {
   4764             mReSchedulePostAnimationCallback = false;
   4765             mEatRunOnAnimationRequest = true;
   4766         }
   4767 
   4768         private void enableRunOnAnimationRequests() {
   4769             mEatRunOnAnimationRequest = false;
   4770             if (mReSchedulePostAnimationCallback) {
   4771                 postOnAnimation();
   4772             }
   4773         }
   4774 
   4775         void postOnAnimation() {
   4776             if (mEatRunOnAnimationRequest) {
   4777                 mReSchedulePostAnimationCallback = true;
   4778             } else {
   4779                 removeCallbacks(this);
   4780                 RecyclerView.this.postOnAnimation(this);
   4781             }
   4782         }
   4783 
   4784         public void fling(int velocityX, int velocityY) {
   4785             setScrollState(SCROLL_STATE_SETTLING);
   4786             mLastFlingX = mLastFlingY = 0;
   4787             mScroller.fling(0, 0, velocityX, velocityY,
   4788                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
   4789             postOnAnimation();
   4790         }
   4791 
   4792         public void smoothScrollBy(int dx, int dy) {
   4793             smoothScrollBy(dx, dy, 0, 0);
   4794         }
   4795 
   4796         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
   4797             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
   4798         }
   4799 
   4800         private float distanceInfluenceForSnapDuration(float f) {
   4801             f -= 0.5f; // center the values about 0.
   4802             f *= 0.3f * Math.PI / 2.0f;
   4803             return (float) Math.sin(f);
   4804         }
   4805 
   4806         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
   4807             final int absDx = Math.abs(dx);
   4808             final int absDy = Math.abs(dy);
   4809             final boolean horizontal = absDx > absDy;
   4810             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
   4811             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
   4812             final int containerSize = horizontal ? getWidth() : getHeight();
   4813             final int halfContainerSize = containerSize / 2;
   4814             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
   4815             final float distance = halfContainerSize + halfContainerSize
   4816                     * distanceInfluenceForSnapDuration(distanceRatio);
   4817 
   4818             final int duration;
   4819             if (velocity > 0) {
   4820                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
   4821             } else {
   4822                 float absDelta = (float) (horizontal ? absDx : absDy);
   4823                 duration = (int) (((absDelta / containerSize) + 1) * 300);
   4824             }
   4825             return Math.min(duration, MAX_SCROLL_DURATION);
   4826         }
   4827 
   4828         public void smoothScrollBy(int dx, int dy, int duration) {
   4829             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
   4830         }
   4831 
   4832         public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
   4833             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
   4834                     interpolator == null ? sQuinticInterpolator : interpolator);
   4835         }
   4836 
   4837         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
   4838             if (mInterpolator != interpolator) {
   4839                 mInterpolator = interpolator;
   4840                 mScroller = new OverScroller(getContext(), interpolator);
   4841             }
   4842             setScrollState(SCROLL_STATE_SETTLING);
   4843             mLastFlingX = mLastFlingY = 0;
   4844             mScroller.startScroll(0, 0, dx, dy, duration);
   4845             postOnAnimation();
   4846         }
   4847 
   4848         public void stop() {
   4849             removeCallbacks(this);
   4850             mScroller.abortAnimation();
   4851         }
   4852 
   4853     }
   4854 
   4855     void repositionShadowingViews() {
   4856         // Fix up shadow views used by change animations
   4857         int count = mChildHelper.getChildCount();
   4858         for (int i = 0; i < count; i++) {
   4859             View view = mChildHelper.getChildAt(i);
   4860             ViewHolder holder = getChildViewHolder(view);
   4861             if (holder != null && holder.mShadowingHolder != null) {
   4862                 View shadowingView = holder.mShadowingHolder.itemView;
   4863                 int left = view.getLeft();
   4864                 int top = view.getTop();
   4865                 if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
   4866                     shadowingView.layout(left, top,
   4867                             left + shadowingView.getWidth(),
   4868                             top + shadowingView.getHeight());
   4869                 }
   4870             }
   4871         }
   4872     }
   4873 
   4874     private class RecyclerViewDataObserver extends AdapterDataObserver {
   4875         RecyclerViewDataObserver() {
   4876         }
   4877 
   4878         @Override
   4879         public void onChanged() {
   4880             assertNotInLayoutOrScroll(null);
   4881             mState.mStructureChanged = true;
   4882 
   4883             setDataSetChangedAfterLayout();
   4884             if (!mAdapterHelper.hasPendingUpdates()) {
   4885                 requestLayout();
   4886             }
   4887         }
   4888 
   4889         @Override
   4890         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   4891             assertNotInLayoutOrScroll(null);
   4892             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
   4893                 triggerUpdateProcessor();
   4894             }
   4895         }
   4896 
   4897         @Override
   4898         public void onItemRangeInserted(int positionStart, int itemCount) {
   4899             assertNotInLayoutOrScroll(null);
   4900             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
   4901                 triggerUpdateProcessor();
   4902             }
   4903         }
   4904 
   4905         @Override
   4906         public void onItemRangeRemoved(int positionStart, int itemCount) {
   4907             assertNotInLayoutOrScroll(null);
   4908             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
   4909                 triggerUpdateProcessor();
   4910             }
   4911         }
   4912 
   4913         @Override
   4914         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   4915             assertNotInLayoutOrScroll(null);
   4916             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
   4917                 triggerUpdateProcessor();
   4918             }
   4919         }
   4920 
   4921         void triggerUpdateProcessor() {
   4922             if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
   4923                 RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
   4924             } else {
   4925                 mAdapterUpdateDuringMeasure = true;
   4926                 requestLayout();
   4927             }
   4928         }
   4929     }
   4930 
   4931     /**
   4932      * RecycledViewPool lets you share Views between multiple RecyclerViews.
   4933      * <p>
   4934      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
   4935      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
   4936      * <p>
   4937      * RecyclerView automatically creates a pool for itself if you don't provide one.
   4938      *
   4939      */
   4940     public static class RecycledViewPool {
   4941         private static final int DEFAULT_MAX_SCRAP = 5;
   4942 
   4943         /**
   4944          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
   4945          *
   4946          * Note that this tracks running averages of create/bind time across all RecyclerViews
   4947          * (and, indirectly, Adapters) that use this pool.
   4948          *
   4949          * 1) This enables us to track average create and bind times across multiple adapters. Even
   4950          * though create (and especially bind) may behave differently for different Adapter
   4951          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
   4952          *
   4953          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
   4954          * false for all other views of its type for the same deadline. This prevents items
   4955          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
   4956          */
   4957         static class ScrapData {
   4958             ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
   4959             int mMaxScrap = DEFAULT_MAX_SCRAP;
   4960             long mCreateRunningAverageNs = 0;
   4961             long mBindRunningAverageNs = 0;
   4962         }
   4963         SparseArray<ScrapData> mScrap = new SparseArray<>();
   4964 
   4965         private int mAttachCount = 0;
   4966 
   4967         public void clear() {
   4968             for (int i = 0; i < mScrap.size(); i++) {
   4969                 ScrapData data = mScrap.valueAt(i);
   4970                 data.mScrapHeap.clear();
   4971             }
   4972         }
   4973 
   4974         public void setMaxRecycledViews(int viewType, int max) {
   4975             ScrapData scrapData = getScrapDataForType(viewType);
   4976             scrapData.mMaxScrap = max;
   4977             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
   4978             if (scrapHeap != null) {
   4979                 while (scrapHeap.size() > max) {
   4980                     scrapHeap.remove(scrapHeap.size() - 1);
   4981                 }
   4982             }
   4983         }
   4984 
   4985         /**
   4986          * Returns the current number of Views held by the RecycledViewPool of the given view type.
   4987          */
   4988         public int getRecycledViewCount(int viewType) {
   4989             return getScrapDataForType(viewType).mScrapHeap.size();
   4990         }
   4991 
   4992         public ViewHolder getRecycledView(int viewType) {
   4993             final ScrapData scrapData = mScrap.get(viewType);
   4994             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
   4995                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
   4996                 return scrapHeap.remove(scrapHeap.size() - 1);
   4997             }
   4998             return null;
   4999         }
   5000 
   5001         int size() {
   5002             int count = 0;
   5003             for (int i = 0; i < mScrap.size(); i++) {
   5004                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
   5005                 if (viewHolders != null) {
   5006                     count += viewHolders.size();
   5007                 }
   5008             }
   5009             return count;
   5010         }
   5011 
   5012         public void putRecycledView(ViewHolder scrap) {
   5013             final int viewType = scrap.getItemViewType();
   5014             final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
   5015             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
   5016                 return;
   5017             }
   5018             if (DEBUG && scrapHeap.contains(scrap)) {
   5019                 throw new IllegalArgumentException("this scrap item already exists");
   5020             }
   5021             scrap.resetInternal();
   5022             scrapHeap.add(scrap);
   5023         }
   5024 
   5025         long runningAverage(long oldAverage, long newValue) {
   5026             if (oldAverage == 0) {
   5027                 return newValue;
   5028             }
   5029             return (oldAverage / 4 * 3) + (newValue / 4);
   5030         }
   5031 
   5032         void factorInCreateTime(int viewType, long createTimeNs) {
   5033             ScrapData scrapData = getScrapDataForType(viewType);
   5034             scrapData.mCreateRunningAverageNs = runningAverage(
   5035                     scrapData.mCreateRunningAverageNs, createTimeNs);
   5036         }
   5037 
   5038         void factorInBindTime(int viewType, long bindTimeNs) {
   5039             ScrapData scrapData = getScrapDataForType(viewType);
   5040             scrapData.mBindRunningAverageNs = runningAverage(
   5041                     scrapData.mBindRunningAverageNs, bindTimeNs);
   5042         }
   5043 
   5044         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
   5045             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
   5046             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
   5047         }
   5048 
   5049         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
   5050             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
   5051             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
   5052         }
   5053 
   5054         void attach(Adapter adapter) {
   5055             mAttachCount++;
   5056         }
   5057 
   5058         void detach() {
   5059             mAttachCount--;
   5060         }
   5061 
   5062 
   5063         /**
   5064          * Detaches the old adapter and attaches the new one.
   5065          * <p>
   5066          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
   5067          * adapter uses a different ViewHolder than the oldAdapter.
   5068          *
   5069          * @param oldAdapter The previous adapter instance. Will be detached.
   5070          * @param newAdapter The new adapter instance. Will be attached.
   5071          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
   5072          *                               ViewHolder and view types.
   5073          */
   5074         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   5075                 boolean compatibleWithPrevious) {
   5076             if (oldAdapter != null) {
   5077                 detach();
   5078             }
   5079             if (!compatibleWithPrevious && mAttachCount == 0) {
   5080                 clear();
   5081             }
   5082             if (newAdapter != null) {
   5083                 attach(newAdapter);
   5084             }
   5085         }
   5086 
   5087         private ScrapData getScrapDataForType(int viewType) {
   5088             ScrapData scrapData = mScrap.get(viewType);
   5089             if (scrapData == null) {
   5090                 scrapData = new ScrapData();
   5091                 mScrap.put(viewType, scrapData);
   5092             }
   5093             return scrapData;
   5094         }
   5095     }
   5096 
   5097     /**
   5098      * Utility method for finding an internal RecyclerView, if present
   5099      */
   5100     @Nullable
   5101     static RecyclerView findNestedRecyclerView(@NonNull View view) {
   5102         if (!(view instanceof ViewGroup)) {
   5103             return null;
   5104         }
   5105         if (view instanceof RecyclerView) {
   5106             return (RecyclerView) view;
   5107         }
   5108         final ViewGroup parent = (ViewGroup) view;
   5109         final int count = parent.getChildCount();
   5110         for (int i = 0; i < count; i++) {
   5111             final View child = parent.getChildAt(i);
   5112             final RecyclerView descendant = findNestedRecyclerView(child);
   5113             if (descendant != null) {
   5114                 return descendant;
   5115             }
   5116         }
   5117         return null;
   5118     }
   5119 
   5120     /**
   5121      * Utility method for clearing holder's internal RecyclerView, if present
   5122      */
   5123     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
   5124         if (holder.mNestedRecyclerView != null) {
   5125             View item = holder.mNestedRecyclerView.get();
   5126             while (item != null) {
   5127                 if (item == holder.itemView) {
   5128                     return; // match found, don't need to clear
   5129                 }
   5130 
   5131                 ViewParent parent = item.getParent();
   5132                 if (parent instanceof View) {
   5133                     item = (View) parent;
   5134                 } else {
   5135                     item = null;
   5136                 }
   5137             }
   5138             holder.mNestedRecyclerView = null; // not nested
   5139         }
   5140     }
   5141 
   5142     /**
   5143      * Time base for deadline-aware work scheduling. Overridable for testing.
   5144      *
   5145      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
   5146      * isn't relevant.
   5147      */
   5148     long getNanoTime() {
   5149         if (ALLOW_THREAD_GAP_WORK) {
   5150             return System.nanoTime();
   5151         } else {
   5152             return 0;
   5153         }
   5154     }
   5155 
   5156     /**
   5157      * A Recycler is responsible for managing scrapped or detached item views for reuse.
   5158      *
   5159      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
   5160      * that has been marked for removal or reuse.</p>
   5161      *
   5162      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
   5163      * an adapter's data set representing the data at a given position or item ID.
   5164      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
   5165      * If not, the view can be quickly reused by the LayoutManager with no further work.
   5166      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
   5167      * may be repositioned by a LayoutManager without remeasurement.</p>
   5168      */
   5169     public final class Recycler {
   5170         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
   5171         ArrayList<ViewHolder> mChangedScrap = null;
   5172 
   5173         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
   5174 
   5175         private final List<ViewHolder>
   5176                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
   5177 
   5178         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
   5179         int mViewCacheMax = DEFAULT_CACHE_SIZE;
   5180 
   5181         RecycledViewPool mRecyclerPool;
   5182 
   5183         private ViewCacheExtension mViewCacheExtension;
   5184 
   5185         static final int DEFAULT_CACHE_SIZE = 2;
   5186 
   5187         /**
   5188          * Clear scrap views out of this recycler. Detached views contained within a
   5189          * recycled view pool will remain.
   5190          */
   5191         public void clear() {
   5192             mAttachedScrap.clear();
   5193             recycleAndClearCachedViews();
   5194         }
   5195 
   5196         /**
   5197          * Set the maximum number of detached, valid views we should retain for later use.
   5198          *
   5199          * @param viewCount Number of views to keep before sending views to the shared pool
   5200          */
   5201         public void setViewCacheSize(int viewCount) {
   5202             mRequestedCacheMax = viewCount;
   5203             updateViewCacheSize();
   5204         }
   5205 
   5206         void updateViewCacheSize() {
   5207             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
   5208             mViewCacheMax = mRequestedCacheMax + extraCache;
   5209 
   5210             // first, try the views that can be recycled
   5211             for (int i = mCachedViews.size() - 1;
   5212                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
   5213                 recycleCachedViewAt(i);
   5214             }
   5215         }
   5216 
   5217         /**
   5218          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
   5219          *
   5220          * @return List of ViewHolders in the scrap list.
   5221          */
   5222         public List<ViewHolder> getScrapList() {
   5223             return mUnmodifiableAttachedScrap;
   5224         }
   5225 
   5226         /**
   5227          * Helper method for getViewForPosition.
   5228          * <p>
   5229          * Checks whether a given view holder can be used for the provided position.
   5230          *
   5231          * @param holder ViewHolder
   5232          * @return true if ViewHolder matches the provided position, false otherwise
   5233          */
   5234         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
   5235             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
   5236             // if it is not removed, verify the type and id.
   5237             if (holder.isRemoved()) {
   5238                 if (DEBUG && !mState.isPreLayout()) {
   5239                     throw new IllegalStateException("should not receive a removed view unless it"
   5240                             + " is pre layout");
   5241                 }
   5242                 return mState.isPreLayout();
   5243             }
   5244             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
   5245                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
   5246                         + "adapter position" + holder);
   5247             }
   5248             if (!mState.isPreLayout()) {
   5249                 // don't check type if it is pre-layout.
   5250                 final int type = mAdapter.getItemViewType(holder.mPosition);
   5251                 if (type != holder.getItemViewType()) {
   5252                     return false;
   5253                 }
   5254             }
   5255             if (mAdapter.hasStableIds()) {
   5256                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
   5257             }
   5258             return true;
   5259         }
   5260 
   5261         /**
   5262          * Attempts to bind view, and account for relevant timing information. If
   5263          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
   5264          *
   5265          * @param holder Holder to be bound.
   5266          * @param offsetPosition Position of item to be bound.
   5267          * @param position Pre-layout position of item to be bound.
   5268          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
   5269          *                   complete. If FOREVER_NS is passed, this method will not fail to
   5270          *                   bind the holder.
   5271          * @return
   5272          */
   5273         private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
   5274                 int position, long deadlineNs) {
   5275             holder.mOwnerRecyclerView = RecyclerView.this;
   5276             final int viewType = holder.getItemViewType();
   5277             long startBindNs = getNanoTime();
   5278             if (deadlineNs != FOREVER_NS
   5279                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
   5280                 // abort - we have a deadline we can't meet
   5281                 return false;
   5282             }
   5283             mAdapter.bindViewHolder(holder, offsetPosition);
   5284             long endBindNs = getNanoTime();
   5285             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
   5286             attachAccessibilityDelegate(holder.itemView);
   5287             if (mState.isPreLayout()) {
   5288                 holder.mPreLayoutPosition = position;
   5289             }
   5290             return true;
   5291         }
   5292 
   5293         /**
   5294          * Binds the given View to the position. The View can be a View previously retrieved via
   5295          * {@link #getViewForPosition(int)} or created by
   5296          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
   5297          * <p>
   5298          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
   5299          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
   5300          * wants to handle its own recycling logic.
   5301          * <p>
   5302          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
   5303          * you don't need to call this method unless you want to bind this View to another position.
   5304          *
   5305          * @param view The view to update.
   5306          * @param position The position of the item to bind to this View.
   5307          */
   5308         public void bindViewToPosition(View view, int position) {
   5309             ViewHolder holder = getChildViewHolderInt(view);
   5310             if (holder == null) {
   5311                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
   5312                         + " pass arbitrary views to this method, they should be created by the "
   5313                         + "Adapter");
   5314             }
   5315             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5316             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   5317                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   5318                         + "position " + position + "(offset:" + offsetPosition + ")."
   5319                         + "state:" + mState.getItemCount());
   5320             }
   5321             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
   5322 
   5323             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   5324             final LayoutParams rvLayoutParams;
   5325             if (lp == null) {
   5326                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   5327                 holder.itemView.setLayoutParams(rvLayoutParams);
   5328             } else if (!checkLayoutParams(lp)) {
   5329                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   5330                 holder.itemView.setLayoutParams(rvLayoutParams);
   5331             } else {
   5332                 rvLayoutParams = (LayoutParams) lp;
   5333             }
   5334 
   5335             rvLayoutParams.mInsetsDirty = true;
   5336             rvLayoutParams.mViewHolder = holder;
   5337             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
   5338         }
   5339 
   5340         /**
   5341          * RecyclerView provides artificial position range (item count) in pre-layout state and
   5342          * automatically maps these positions to {@link Adapter} positions when
   5343          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
   5344          * <p>
   5345          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
   5346          * LayoutManager may need to call some custom component with item positions in which
   5347          * case you need the actual adapter position instead of the pre layout position. You
   5348          * can use this method to convert a pre-layout position to adapter (post layout) position.
   5349          * <p>
   5350          * Note that if the provided position belongs to a deleted ViewHolder, this method will
   5351          * return -1.
   5352          * <p>
   5353          * Calling this method in post-layout state returns the same value back.
   5354          *
   5355          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
   5356          *                 less than {@link State#getItemCount()}.
   5357          */
   5358         public int convertPreLayoutPositionToPostLayout(int position) {
   5359             if (position < 0 || position >= mState.getItemCount()) {
   5360                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
   5361                         + "item count is " + mState.getItemCount());
   5362             }
   5363             if (!mState.isPreLayout()) {
   5364                 return position;
   5365             }
   5366             return mAdapterHelper.findPositionOffset(position);
   5367         }
   5368 
   5369         /**
   5370          * Obtain a view initialized for the given position.
   5371          *
   5372          * This method should be used by {@link LayoutManager} implementations to obtain
   5373          * views to represent data from an {@link Adapter}.
   5374          * <p>
   5375          * The Recycler may reuse a scrap or detached view from a shared pool if one is
   5376          * available for the correct view type. If the adapter has not indicated that the
   5377          * data at the given position has changed, the Recycler will attempt to hand back
   5378          * a scrap view that was previously initialized for that data without rebinding.
   5379          *
   5380          * @param position Position to obtain a view for
   5381          * @return A view representing the data at <code>position</code> from <code>adapter</code>
   5382          */
   5383         public View getViewForPosition(int position) {
   5384             return getViewForPosition(position, false);
   5385         }
   5386 
   5387         View getViewForPosition(int position, boolean dryRun) {
   5388             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
   5389         }
   5390 
   5391         /**
   5392          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
   5393          * cache, the RecycledViewPool, or creating it directly.
   5394          * <p>
   5395          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
   5396          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
   5397          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
   5398          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
   5399          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
   5400          *
   5401          * @param position Position of ViewHolder to be returned.
   5402          * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
   5403          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
   5404          *                   complete. If FOREVER_NS is passed, this method will not fail to
   5405          *                   create/bind the holder if needed.
   5406          *
   5407          * @return ViewHolder for requested position
   5408          */
   5409         @Nullable
   5410         ViewHolder tryGetViewHolderForPositionByDeadline(int position,
   5411                 boolean dryRun, long deadlineNs) {
   5412             if (position < 0 || position >= mState.getItemCount()) {
   5413                 throw new IndexOutOfBoundsException("Invalid item position " + position
   5414                         + "(" + position + "). Item count:" + mState.getItemCount());
   5415             }
   5416             boolean fromScrapOrHiddenOrCache = false;
   5417             ViewHolder holder = null;
   5418             // 0) If there is a changed scrap, try to find from there
   5419             if (mState.isPreLayout()) {
   5420                 holder = getChangedScrapViewForPosition(position);
   5421                 fromScrapOrHiddenOrCache = holder != null;
   5422             }
   5423             // 1) Find by position from scrap/hidden list/cache
   5424             if (holder == null) {
   5425                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
   5426                 if (holder != null) {
   5427                     if (!validateViewHolderForOffsetPosition(holder)) {
   5428                         // recycle holder (and unscrap if relevant) since it can't be used
   5429                         if (!dryRun) {
   5430                             // we would like to recycle this but need to make sure it is not used by
   5431                             // animation logic etc.
   5432                             holder.addFlags(ViewHolder.FLAG_INVALID);
   5433                             if (holder.isScrap()) {
   5434                                 removeDetachedView(holder.itemView, false);
   5435                                 holder.unScrap();
   5436                             } else if (holder.wasReturnedFromScrap()) {
   5437                                 holder.clearReturnedFromScrapFlag();
   5438                             }
   5439                             recycleViewHolderInternal(holder);
   5440                         }
   5441                         holder = null;
   5442                     } else {
   5443                         fromScrapOrHiddenOrCache = true;
   5444                     }
   5445                 }
   5446             }
   5447             if (holder == null) {
   5448                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5449                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   5450                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   5451                             + "position " + position + "(offset:" + offsetPosition + ")."
   5452                             + "state:" + mState.getItemCount());
   5453                 }
   5454 
   5455                 final int type = mAdapter.getItemViewType(offsetPosition);
   5456                 // 2) Find from scrap/cache via stable ids, if exists
   5457                 if (mAdapter.hasStableIds()) {
   5458                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
   5459                             type, dryRun);
   5460                     if (holder != null) {
   5461                         // update position
   5462                         holder.mPosition = offsetPosition;
   5463                         fromScrapOrHiddenOrCache = true;
   5464                     }
   5465                 }
   5466                 if (holder == null && mViewCacheExtension != null) {
   5467                     // We are NOT sending the offsetPosition because LayoutManager does not
   5468                     // know it.
   5469                     final View view = mViewCacheExtension
   5470                             .getViewForPositionAndType(this, position, type);
   5471                     if (view != null) {
   5472                         holder = getChildViewHolder(view);
   5473                         if (holder == null) {
   5474                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   5475                                     + " a view which does not have a ViewHolder");
   5476                         } else if (holder.shouldIgnore()) {
   5477                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   5478                                     + " a view that is ignored. You must call stopIgnoring before"
   5479                                     + " returning this view.");
   5480                         }
   5481                     }
   5482                 }
   5483                 if (holder == null) { // fallback to pool
   5484                     if (DEBUG) {
   5485                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
   5486                                 + position + ") fetching from shared pool");
   5487                     }
   5488                     holder = getRecycledViewPool().getRecycledView(type);
   5489                     if (holder != null) {
   5490                         holder.resetInternal();
   5491                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
   5492                             invalidateDisplayListInt(holder);
   5493                         }
   5494                     }
   5495                 }
   5496                 if (holder == null) {
   5497                     long start = getNanoTime();
   5498                     if (deadlineNs != FOREVER_NS
   5499                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
   5500                         // abort - we have a deadline we can't meet
   5501                         return null;
   5502                     }
   5503                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
   5504                     if (ALLOW_THREAD_GAP_WORK) {
   5505                         // only bother finding nested RV if prefetching
   5506                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
   5507                         if (innerView != null) {
   5508                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
   5509                         }
   5510                     }
   5511 
   5512                     long end = getNanoTime();
   5513                     mRecyclerPool.factorInCreateTime(type, end - start);
   5514                     if (DEBUG) {
   5515                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
   5516                     }
   5517                 }
   5518             }
   5519 
   5520             // This is very ugly but the only place we can grab this information
   5521             // before the View is rebound and returned to the LayoutManager for post layout ops.
   5522             // We don't need this in pre-layout since the VH is not updated by the LM.
   5523             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
   5524                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
   5525                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   5526                 if (mState.mRunSimpleAnimations) {
   5527                     int changeFlags = ItemAnimator
   5528                             .buildAdapterChangeFlagsForAnimations(holder);
   5529                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
   5530                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
   5531                             holder, changeFlags, holder.getUnmodifiedPayloads());
   5532                     recordAnimationInfoIfBouncedHiddenView(holder, info);
   5533                 }
   5534             }
   5535 
   5536             boolean bound = false;
   5537             if (mState.isPreLayout() && holder.isBound()) {
   5538                 // do not update unless we absolutely have to.
   5539                 holder.mPreLayoutPosition = position;
   5540             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
   5541                 if (DEBUG && holder.isRemoved()) {
   5542                     throw new IllegalStateException("Removed holder should be bound and it should"
   5543                             + " come here only in pre-layout. Holder: " + holder);
   5544                 }
   5545                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5546                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
   5547             }
   5548 
   5549             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   5550             final LayoutParams rvLayoutParams;
   5551             if (lp == null) {
   5552                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   5553                 holder.itemView.setLayoutParams(rvLayoutParams);
   5554             } else if (!checkLayoutParams(lp)) {
   5555                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   5556                 holder.itemView.setLayoutParams(rvLayoutParams);
   5557             } else {
   5558                 rvLayoutParams = (LayoutParams) lp;
   5559             }
   5560             rvLayoutParams.mViewHolder = holder;
   5561             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
   5562             return holder;
   5563         }
   5564 
   5565         private void attachAccessibilityDelegate(View itemView) {
   5566             if (isAccessibilityEnabled()) {
   5567                 if (itemView.getImportantForAccessibility()
   5568                         == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
   5569                     itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
   5570                 }
   5571 
   5572                 if (itemView.getAccessibilityDelegate() == null) {
   5573                     itemView.setAccessibilityDelegate(mAccessibilityDelegate.getItemDelegate());
   5574                 }
   5575             }
   5576         }
   5577 
   5578         private void invalidateDisplayListInt(ViewHolder holder) {
   5579             if (holder.itemView instanceof ViewGroup) {
   5580                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
   5581             }
   5582         }
   5583 
   5584         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
   5585             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
   5586                 final View view = viewGroup.getChildAt(i);
   5587                 if (view instanceof ViewGroup) {
   5588                     invalidateDisplayListInt((ViewGroup) view, true);
   5589                 }
   5590             }
   5591             if (!invalidateThis) {
   5592                 return;
   5593             }
   5594             // we need to force it to become invisible
   5595             if (viewGroup.getVisibility() == View.INVISIBLE) {
   5596                 viewGroup.setVisibility(View.VISIBLE);
   5597                 viewGroup.setVisibility(View.INVISIBLE);
   5598             } else {
   5599                 final int visibility = viewGroup.getVisibility();
   5600                 viewGroup.setVisibility(View.INVISIBLE);
   5601                 viewGroup.setVisibility(visibility);
   5602             }
   5603         }
   5604 
   5605         /**
   5606          * Recycle a detached view. The specified view will be added to a pool of views
   5607          * for later rebinding and reuse.
   5608          *
   5609          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
   5610          * View is scrapped, it will be removed from scrap list.</p>
   5611          *
   5612          * @param view Removed view for recycling
   5613          * @see LayoutManager#removeAndRecycleView(View, Recycler)
   5614          */
   5615         public void recycleView(View view) {
   5616             // This public recycle method tries to make view recycle-able since layout manager
   5617             // intended to recycle this view (e.g. even if it is in scrap or change cache)
   5618             ViewHolder holder = getChildViewHolderInt(view);
   5619             if (holder.isTmpDetached()) {
   5620                 removeDetachedView(view, false);
   5621             }
   5622             if (holder.isScrap()) {
   5623                 holder.unScrap();
   5624             } else if (holder.wasReturnedFromScrap()) {
   5625                 holder.clearReturnedFromScrapFlag();
   5626             }
   5627             recycleViewHolderInternal(holder);
   5628         }
   5629 
   5630         /**
   5631          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
   5632          * catch potential bugs.
   5633          * @param view
   5634          */
   5635         void recycleViewInternal(View view) {
   5636             recycleViewHolderInternal(getChildViewHolderInt(view));
   5637         }
   5638 
   5639         void recycleAndClearCachedViews() {
   5640             final int count = mCachedViews.size();
   5641             for (int i = count - 1; i >= 0; i--) {
   5642                 recycleCachedViewAt(i);
   5643             }
   5644             mCachedViews.clear();
   5645             if (ALLOW_THREAD_GAP_WORK) {
   5646                 mPrefetchRegistry.clearPrefetchPositions();
   5647             }
   5648         }
   5649 
   5650         /**
   5651          * Recycles a cached view and removes the view from the list. Views are added to cache
   5652          * if and only if they are recyclable, so this method does not check it again.
   5653          * <p>
   5654          * A small exception to this rule is when the view does not have an animator reference
   5655          * but transient state is true (due to animations created outside ItemAnimator). In that
   5656          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
   5657          * still recyclable since Adapter wants to do so.
   5658          *
   5659          * @param cachedViewIndex The index of the view in cached views list
   5660          */
   5661         void recycleCachedViewAt(int cachedViewIndex) {
   5662             if (DEBUG) {
   5663                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
   5664             }
   5665             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
   5666             if (DEBUG) {
   5667                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
   5668             }
   5669             addViewHolderToRecycledViewPool(viewHolder, true);
   5670             mCachedViews.remove(cachedViewIndex);
   5671         }
   5672 
   5673         /**
   5674          * internal implementation checks if view is scrapped or attached and throws an exception
   5675          * if so.
   5676          * Public version un-scraps before calling recycle.
   5677          */
   5678         void recycleViewHolderInternal(ViewHolder holder) {
   5679             if (holder.isScrap() || holder.itemView.getParent() != null) {
   5680                 throw new IllegalArgumentException(
   5681                         "Scrapped or attached views may not be recycled. isScrap:"
   5682                                 + holder.isScrap() + " isAttached:"
   5683                                 + (holder.itemView.getParent() != null));
   5684             }
   5685 
   5686             if (holder.isTmpDetached()) {
   5687                 throw new IllegalArgumentException("Tmp detached view should be removed "
   5688                         + "from RecyclerView before it can be recycled: " + holder);
   5689             }
   5690 
   5691             if (holder.shouldIgnore()) {
   5692                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
   5693                         + " should first call stopIgnoringView(view) before calling recycle.");
   5694             }
   5695             //noinspection unchecked
   5696             final boolean transientStatePreventsRecycling = holder
   5697                     .doesTransientStatePreventRecycling();
   5698             final boolean forceRecycle = mAdapter != null
   5699                     && transientStatePreventsRecycling
   5700                     && mAdapter.onFailedToRecycleView(holder);
   5701             boolean cached = false;
   5702             boolean recycled = false;
   5703             if (DEBUG && mCachedViews.contains(holder)) {
   5704                 throw new IllegalArgumentException("cached view received recycle internal? "
   5705                         + holder);
   5706             }
   5707             if (forceRecycle || holder.isRecyclable()) {
   5708                 if (mViewCacheMax > 0
   5709                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
   5710                                 | ViewHolder.FLAG_REMOVED
   5711                                 | ViewHolder.FLAG_UPDATE
   5712                                 | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
   5713                     // Retire oldest cached view
   5714                     int cachedViewSize = mCachedViews.size();
   5715                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
   5716                         recycleCachedViewAt(0);
   5717                         cachedViewSize--;
   5718                     }
   5719 
   5720                     int targetCacheIndex = cachedViewSize;
   5721                     if (ALLOW_THREAD_GAP_WORK
   5722                             && cachedViewSize > 0
   5723                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
   5724                         // when adding the view, skip past most recently prefetched views
   5725                         int cacheIndex = cachedViewSize - 1;
   5726                         while (cacheIndex >= 0) {
   5727                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
   5728                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
   5729                                 break;
   5730                             }
   5731                             cacheIndex--;
   5732                         }
   5733                         targetCacheIndex = cacheIndex + 1;
   5734                     }
   5735                     mCachedViews.add(targetCacheIndex, holder);
   5736                     cached = true;
   5737                 }
   5738                 if (!cached) {
   5739                     addViewHolderToRecycledViewPool(holder, true);
   5740                     recycled = true;
   5741                 }
   5742             } else {
   5743                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
   5744                 // runs. In this case, the item is eventually recycled by
   5745                 // ItemAnimatorRestoreListener#onAnimationFinished.
   5746 
   5747                 // TODO: consider cancelling an animation when an item is removed scrollBy,
   5748                 // to return it to the pool faster
   5749                 if (DEBUG) {
   5750                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
   5751                             + "re-visit here. We are still removing it from animation lists");
   5752                 }
   5753             }
   5754             // even if the holder is not removed, we still call this method so that it is removed
   5755             // from view holder lists.
   5756             mViewInfoStore.removeViewHolder(holder);
   5757             if (!cached && !recycled && transientStatePreventsRecycling) {
   5758                 holder.mOwnerRecyclerView = null;
   5759             }
   5760         }
   5761 
   5762         /**
   5763          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
   5764          *
   5765          * Pass false to dispatchRecycled for views that have not been bound.
   5766          *
   5767          * @param holder Holder to be added to the pool.
   5768          * @param dispatchRecycled True to dispatch View recycled callbacks.
   5769          */
   5770         void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
   5771             clearNestedRecyclerViewIfNotNested(holder);
   5772             holder.itemView.setAccessibilityDelegate(null);
   5773             if (dispatchRecycled) {
   5774                 dispatchViewRecycled(holder);
   5775             }
   5776             holder.mOwnerRecyclerView = null;
   5777             getRecycledViewPool().putRecycledView(holder);
   5778         }
   5779 
   5780         /**
   5781          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
   5782          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
   5783          * internal bookkeeping.
   5784          */
   5785         void quickRecycleScrapView(View view) {
   5786             final ViewHolder holder = getChildViewHolderInt(view);
   5787             holder.mScrapContainer = null;
   5788             holder.mInChangeScrap = false;
   5789             holder.clearReturnedFromScrapFlag();
   5790             recycleViewHolderInternal(holder);
   5791         }
   5792 
   5793         /**
   5794          * Mark an attached view as scrap.
   5795          *
   5796          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
   5797          * for rebinding and reuse. Requests for a view for a given position may return a
   5798          * reused or rebound scrap view instance.</p>
   5799          *
   5800          * @param view View to scrap
   5801          */
   5802         void scrapView(View view) {
   5803             final ViewHolder holder = getChildViewHolderInt(view);
   5804             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
   5805                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
   5806                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
   5807                     throw new IllegalArgumentException("Called scrap view with an invalid view."
   5808                             + " Invalid views cannot be reused from scrap, they should rebound from"
   5809                             + " recycler pool.");
   5810                 }
   5811                 holder.setScrapContainer(this, false);
   5812                 mAttachedScrap.add(holder);
   5813             } else {
   5814                 if (mChangedScrap == null) {
   5815                     mChangedScrap = new ArrayList<ViewHolder>();
   5816                 }
   5817                 holder.setScrapContainer(this, true);
   5818                 mChangedScrap.add(holder);
   5819             }
   5820         }
   5821 
   5822         /**
   5823          * Remove a previously scrapped view from the pool of eligible scrap.
   5824          *
   5825          * <p>This view will no longer be eligible for reuse until re-scrapped or
   5826          * until it is explicitly removed and recycled.</p>
   5827          */
   5828         void unscrapView(ViewHolder holder) {
   5829             if (holder.mInChangeScrap) {
   5830                 mChangedScrap.remove(holder);
   5831             } else {
   5832                 mAttachedScrap.remove(holder);
   5833             }
   5834             holder.mScrapContainer = null;
   5835             holder.mInChangeScrap = false;
   5836             holder.clearReturnedFromScrapFlag();
   5837         }
   5838 
   5839         int getScrapCount() {
   5840             return mAttachedScrap.size();
   5841         }
   5842 
   5843         View getScrapViewAt(int index) {
   5844             return mAttachedScrap.get(index).itemView;
   5845         }
   5846 
   5847         void clearScrap() {
   5848             mAttachedScrap.clear();
   5849             if (mChangedScrap != null) {
   5850                 mChangedScrap.clear();
   5851             }
   5852         }
   5853 
   5854         ViewHolder getChangedScrapViewForPosition(int position) {
   5855             // If pre-layout, check the changed scrap for an exact match.
   5856             final int changedScrapSize;
   5857             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
   5858                 return null;
   5859             }
   5860             // find by position
   5861             for (int i = 0; i < changedScrapSize; i++) {
   5862                 final ViewHolder holder = mChangedScrap.get(i);
   5863                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
   5864                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5865                     return holder;
   5866                 }
   5867             }
   5868             // find by id
   5869             if (mAdapter.hasStableIds()) {
   5870                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5871                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
   5872                     final long id = mAdapter.getItemId(offsetPosition);
   5873                     for (int i = 0; i < changedScrapSize; i++) {
   5874                         final ViewHolder holder = mChangedScrap.get(i);
   5875                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
   5876                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5877                             return holder;
   5878                         }
   5879                     }
   5880                 }
   5881             }
   5882             return null;
   5883         }
   5884 
   5885         /**
   5886          * Returns a view for the position either from attach scrap, hidden children, or cache.
   5887          *
   5888          * @param position Item position
   5889          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
   5890          * @return a ViewHolder that can be re-used for this position.
   5891          */
   5892         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
   5893             final int scrapCount = mAttachedScrap.size();
   5894 
   5895             // Try first for an exact, non-invalid match from scrap.
   5896             for (int i = 0; i < scrapCount; i++) {
   5897                 final ViewHolder holder = mAttachedScrap.get(i);
   5898                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
   5899                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
   5900                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5901                     return holder;
   5902                 }
   5903             }
   5904 
   5905             if (!dryRun) {
   5906                 View view = mChildHelper.findHiddenNonRemovedView(position);
   5907                 if (view != null) {
   5908                     // This View is good to be used. We just need to unhide, detach and move to the
   5909                     // scrap list.
   5910                     final ViewHolder vh = getChildViewHolderInt(view);
   5911                     mChildHelper.unhide(view);
   5912                     int layoutIndex = mChildHelper.indexOfChild(view);
   5913                     if (layoutIndex == RecyclerView.NO_POSITION) {
   5914                         throw new IllegalStateException("layout index should not be -1 after "
   5915                                 + "unhiding a view:" + vh);
   5916                     }
   5917                     mChildHelper.detachViewFromParent(layoutIndex);
   5918                     scrapView(view);
   5919                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
   5920                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   5921                     return vh;
   5922                 }
   5923             }
   5924 
   5925             // Search in our first-level recycled view cache.
   5926             final int cacheSize = mCachedViews.size();
   5927             for (int i = 0; i < cacheSize; i++) {
   5928                 final ViewHolder holder = mCachedViews.get(i);
   5929                 // invalid view holders may be in cache if adapter has stable ids as they can be
   5930                 // retrieved via getScrapOrCachedViewForId
   5931                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
   5932                     if (!dryRun) {
   5933                         mCachedViews.remove(i);
   5934                     }
   5935                     if (DEBUG) {
   5936                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
   5937                                 + ") found match in cache: " + holder);
   5938                     }
   5939                     return holder;
   5940                 }
   5941             }
   5942             return null;
   5943         }
   5944 
   5945         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
   5946             // Look in our attached views first
   5947             final int count = mAttachedScrap.size();
   5948             for (int i = count - 1; i >= 0; i--) {
   5949                 final ViewHolder holder = mAttachedScrap.get(i);
   5950                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
   5951                     if (type == holder.getItemViewType()) {
   5952                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5953                         if (holder.isRemoved()) {
   5954                             // this might be valid in two cases:
   5955                             // > item is removed but we are in pre-layout pass
   5956                             // >> do nothing. return as is. make sure we don't rebind
   5957                             // > item is removed then added to another position and we are in
   5958                             // post layout.
   5959                             // >> remove removed and invalid flags, add update flag to rebind
   5960                             // because item was invisible to us and we don't know what happened in
   5961                             // between.
   5962                             if (!mState.isPreLayout()) {
   5963                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
   5964                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
   5965                             }
   5966                         }
   5967                         return holder;
   5968                     } else if (!dryRun) {
   5969                         // if we are running animations, it is actually better to keep it in scrap
   5970                         // but this would force layout manager to lay it out which would be bad.
   5971                         // Recycle this scrap. Type mismatch.
   5972                         mAttachedScrap.remove(i);
   5973                         removeDetachedView(holder.itemView, false);
   5974                         quickRecycleScrapView(holder.itemView);
   5975                     }
   5976                 }
   5977             }
   5978 
   5979             // Search the first-level cache
   5980             final int cacheSize = mCachedViews.size();
   5981             for (int i = cacheSize - 1; i >= 0; i--) {
   5982                 final ViewHolder holder = mCachedViews.get(i);
   5983                 if (holder.getItemId() == id) {
   5984                     if (type == holder.getItemViewType()) {
   5985                         if (!dryRun) {
   5986                             mCachedViews.remove(i);
   5987                         }
   5988                         return holder;
   5989                     } else if (!dryRun) {
   5990                         recycleCachedViewAt(i);
   5991                         return null;
   5992                     }
   5993                 }
   5994             }
   5995             return null;
   5996         }
   5997 
   5998         void dispatchViewRecycled(ViewHolder holder) {
   5999             if (mRecyclerListener != null) {
   6000                 mRecyclerListener.onViewRecycled(holder);
   6001             }
   6002             if (mAdapter != null) {
   6003                 mAdapter.onViewRecycled(holder);
   6004             }
   6005             if (mState != null) {
   6006                 mViewInfoStore.removeViewHolder(holder);
   6007             }
   6008             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
   6009         }
   6010 
   6011         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   6012                 boolean compatibleWithPrevious) {
   6013             clear();
   6014             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
   6015         }
   6016 
   6017         void offsetPositionRecordsForMove(int from, int to) {
   6018             final int start, end, inBetweenOffset;
   6019             if (from < to) {
   6020                 start = from;
   6021                 end = to;
   6022                 inBetweenOffset = -1;
   6023             } else {
   6024                 start = to;
   6025                 end = from;
   6026                 inBetweenOffset = 1;
   6027             }
   6028             final int cachedCount = mCachedViews.size();
   6029             for (int i = 0; i < cachedCount; i++) {
   6030                 final ViewHolder holder = mCachedViews.get(i);
   6031                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   6032                     continue;
   6033                 }
   6034                 if (holder.mPosition == from) {
   6035                     holder.offsetPosition(to - from, false);
   6036                 } else {
   6037                     holder.offsetPosition(inBetweenOffset, false);
   6038                 }
   6039                 if (DEBUG) {
   6040                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
   6041                             + holder);
   6042                 }
   6043             }
   6044         }
   6045 
   6046         void offsetPositionRecordsForInsert(int insertedAt, int count) {
   6047             final int cachedCount = mCachedViews.size();
   6048             for (int i = 0; i < cachedCount; i++) {
   6049                 final ViewHolder holder = mCachedViews.get(i);
   6050                 if (holder != null && holder.mPosition >= insertedAt) {
   6051                     if (DEBUG) {
   6052                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
   6053                                 + holder + " now at position " + (holder.mPosition + count));
   6054                     }
   6055                     holder.offsetPosition(count, true);
   6056                 }
   6057             }
   6058         }
   6059 
   6060         /**
   6061          * @param removedFrom Remove start index
   6062          * @param count Remove count
   6063          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
   6064          *                         false, they'll be applied before the second layout pass
   6065          */
   6066         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
   6067             final int removedEnd = removedFrom + count;
   6068             final int cachedCount = mCachedViews.size();
   6069             for (int i = cachedCount - 1; i >= 0; i--) {
   6070                 final ViewHolder holder = mCachedViews.get(i);
   6071                 if (holder != null) {
   6072                     if (holder.mPosition >= removedEnd) {
   6073                         if (DEBUG) {
   6074                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
   6075                                     + " holder " + holder + " now at position "
   6076                                     + (holder.mPosition - count));
   6077                         }
   6078                         holder.offsetPosition(-count, applyToPreLayout);
   6079                     } else if (holder.mPosition >= removedFrom) {
   6080                         // Item for this view was removed. Dump it from the cache.
   6081                         holder.addFlags(ViewHolder.FLAG_REMOVED);
   6082                         recycleCachedViewAt(i);
   6083                     }
   6084                 }
   6085             }
   6086         }
   6087 
   6088         void setViewCacheExtension(ViewCacheExtension extension) {
   6089             mViewCacheExtension = extension;
   6090         }
   6091 
   6092         void setRecycledViewPool(RecycledViewPool pool) {
   6093             if (mRecyclerPool != null) {
   6094                 mRecyclerPool.detach();
   6095             }
   6096             mRecyclerPool = pool;
   6097             if (pool != null) {
   6098                 mRecyclerPool.attach(getAdapter());
   6099             }
   6100         }
   6101 
   6102         RecycledViewPool getRecycledViewPool() {
   6103             if (mRecyclerPool == null) {
   6104                 mRecyclerPool = new RecycledViewPool();
   6105             }
   6106             return mRecyclerPool;
   6107         }
   6108 
   6109         void viewRangeUpdate(int positionStart, int itemCount) {
   6110             final int positionEnd = positionStart + itemCount;
   6111             final int cachedCount = mCachedViews.size();
   6112             for (int i = cachedCount - 1; i >= 0; i--) {
   6113                 final ViewHolder holder = mCachedViews.get(i);
   6114                 if (holder == null) {
   6115                     continue;
   6116                 }
   6117 
   6118                 final int pos = holder.getLayoutPosition();
   6119                 if (pos >= positionStart && pos < positionEnd) {
   6120                     holder.addFlags(ViewHolder.FLAG_UPDATE);
   6121                     recycleCachedViewAt(i);
   6122                     // cached views should not be flagged as changed because this will cause them
   6123                     // to animate when they are returned from cache.
   6124                 }
   6125             }
   6126         }
   6127 
   6128         void setAdapterPositionsAsUnknown() {
   6129             final int cachedCount = mCachedViews.size();
   6130             for (int i = 0; i < cachedCount; i++) {
   6131                 final ViewHolder holder = mCachedViews.get(i);
   6132                 if (holder != null) {
   6133                     holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   6134                 }
   6135             }
   6136         }
   6137 
   6138         void markKnownViewsInvalid() {
   6139             if (mAdapter != null && mAdapter.hasStableIds()) {
   6140                 final int cachedCount = mCachedViews.size();
   6141                 for (int i = 0; i < cachedCount; i++) {
   6142                     final ViewHolder holder = mCachedViews.get(i);
   6143                     if (holder != null) {
   6144                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   6145                         holder.addChangePayload(null);
   6146                     }
   6147                 }
   6148             } else {
   6149                 // we cannot re-use cached views in this case. Recycle them all
   6150                 recycleAndClearCachedViews();
   6151             }
   6152         }
   6153 
   6154         void clearOldPositions() {
   6155             final int cachedCount = mCachedViews.size();
   6156             for (int i = 0; i < cachedCount; i++) {
   6157                 final ViewHolder holder = mCachedViews.get(i);
   6158                 holder.clearOldPosition();
   6159             }
   6160             final int scrapCount = mAttachedScrap.size();
   6161             for (int i = 0; i < scrapCount; i++) {
   6162                 mAttachedScrap.get(i).clearOldPosition();
   6163             }
   6164             if (mChangedScrap != null) {
   6165                 final int changedScrapCount = mChangedScrap.size();
   6166                 for (int i = 0; i < changedScrapCount; i++) {
   6167                     mChangedScrap.get(i).clearOldPosition();
   6168                 }
   6169             }
   6170         }
   6171 
   6172         void markItemDecorInsetsDirty() {
   6173             final int cachedCount = mCachedViews.size();
   6174             for (int i = 0; i < cachedCount; i++) {
   6175                 final ViewHolder holder = mCachedViews.get(i);
   6176                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
   6177                 if (layoutParams != null) {
   6178                     layoutParams.mInsetsDirty = true;
   6179                 }
   6180             }
   6181         }
   6182     }
   6183 
   6184     /**
   6185      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
   6186      * be controlled by the developer.
   6187      * <p>
   6188      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
   6189      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
   6190      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
   6191      * {@link RecycledViewPool}.
   6192      * <p>
   6193      * Note that, Recycler never sends Views to this method to be cached. It is developers
   6194      * responsibility to decide whether they want to keep their Views in this custom cache or let
   6195      * the default recycling policy handle it.
   6196      */
   6197     public abstract static class ViewCacheExtension {
   6198 
   6199         /**
   6200          * Returns a View that can be binded to the given Adapter position.
   6201          * <p>
   6202          * This method should <b>not</b> create a new View. Instead, it is expected to return
   6203          * an already created View that can be re-used for the given type and position.
   6204          * If the View is marked as ignored, it should first call
   6205          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
   6206          * <p>
   6207          * RecyclerView will re-bind the returned View to the position if necessary.
   6208          *
   6209          * @param recycler The Recycler that can be used to bind the View
   6210          * @param position The adapter position
   6211          * @param type     The type of the View, defined by adapter
   6212          * @return A View that is bound to the given position or NULL if there is no View to re-use
   6213          * @see LayoutManager#ignoreView(View)
   6214          */
   6215         public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
   6216     }
   6217 
   6218     /**
   6219      * Base class for an Adapter
   6220      *
   6221      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
   6222      * within a {@link RecyclerView}.</p>
   6223      *
   6224      * @param  A class that extends ViewHolder that will be used by the adapter.
   6225      */
   6226     public abstract static class Adapter<VH extends ViewHolder> {
   6227         private final AdapterDataObservable mObservable = new AdapterDataObservable();
   6228         private boolean mHasStableIds = false;
   6229 
   6230         /**
   6231          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
   6232          * an item.
   6233          * <p>
   6234          * This new ViewHolder should be constructed with a new View that can represent the items
   6235          * of the given type. You can either create a new View manually or inflate it from an XML
   6236          * layout file.
   6237          * <p>
   6238          * The new ViewHolder will be used to display items of the adapter using
   6239          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
   6240          * different items in the data set, it is a good idea to cache references to sub views of
   6241          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
   6242          *
   6243          * @param parent The ViewGroup into which the new View will be added after it is bound to
   6244          *               an adapter position.
   6245          * @param viewType The view type of the new View.
   6246          *
   6247          * @return A new ViewHolder that holds a View of the given view type.
   6248          * @see #getItemViewType(int)
   6249          * @see #onBindViewHolder(ViewHolder, int)
   6250          */
   6251         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
   6252 
   6253         /**
   6254          * Called by RecyclerView to display the data at the specified position. This method should
   6255          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
   6256          * position.
   6257          * <p>
   6258          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   6259          * again if the position of the item changes in the data set unless the item itself is
   6260          * invalidated or the new position cannot be determined. For this reason, you should only
   6261          * use the <code>position</code> parameter while acquiring the related data item inside
   6262          * this method and should not keep a copy of it. If you need the position of an item later
   6263          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   6264          * have the updated adapter position.
   6265          *
   6266          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
   6267          * handle efficient partial bind.
   6268          *
   6269          * @param holder The ViewHolder which should be updated to represent the contents of the
   6270          *        item at the given position in the data set.
   6271          * @param position The position of the item within the adapter's data set.
   6272          */
   6273         public abstract void onBindViewHolder(VH holder, int position);
   6274 
   6275         /**
   6276          * Called by RecyclerView to display the data at the specified position. This method
   6277          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
   6278          * the given position.
   6279          * <p>
   6280          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   6281          * again if the position of the item changes in the data set unless the item itself is
   6282          * invalidated or the new position cannot be determined. For this reason, you should only
   6283          * use the <code>position</code> parameter while acquiring the related data item inside
   6284          * this method and should not keep a copy of it. If you need the position of an item later
   6285          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   6286          * have the updated adapter position.
   6287          * <p>
   6288          * Partial bind vs full bind:
   6289          * <p>
   6290          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
   6291          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
   6292          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
   6293          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
   6294          * Adapter should not assume that the payload passed in notify methods will be received by
   6295          * onBindViewHolder().  For example when the view is not attached to the screen, the
   6296          * payload in notifyItemChange() will be simply dropped.
   6297          *
   6298          * @param holder The ViewHolder which should be updated to represent the contents of the
   6299          *               item at the given position in the data set.
   6300          * @param position The position of the item within the adapter's data set.
   6301          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
   6302          *                 update.
   6303          */
   6304         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
   6305             onBindViewHolder(holder, position);
   6306         }
   6307 
   6308         /**
   6309          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
   6310          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
   6311          *
   6312          * @see #onCreateViewHolder(ViewGroup, int)
   6313          */
   6314         public final VH createViewHolder(ViewGroup parent, int viewType) {
   6315             Trace.beginSection(TRACE_CREATE_VIEW_TAG);
   6316             final VH holder = onCreateViewHolder(parent, viewType);
   6317             holder.mItemViewType = viewType;
   6318             Trace.endSection();
   6319             return holder;
   6320         }
   6321 
   6322         /**
   6323          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
   6324          * {@link ViewHolder} contents with the item at the given position and also sets up some
   6325          * private fields to be used by RecyclerView.
   6326          *
   6327          * @see #onBindViewHolder(ViewHolder, int)
   6328          */
   6329         public final void bindViewHolder(VH holder, int position) {
   6330             holder.mPosition = position;
   6331             if (hasStableIds()) {
   6332                 holder.mItemId = getItemId(position);
   6333             }
   6334             holder.setFlags(ViewHolder.FLAG_BOUND,
   6335                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
   6336                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   6337             Trace.beginSection(TRACE_BIND_VIEW_TAG);
   6338             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
   6339             holder.clearPayload();
   6340             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
   6341             if (layoutParams instanceof RecyclerView.LayoutParams) {
   6342                 ((LayoutParams) layoutParams).mInsetsDirty = true;
   6343             }
   6344             Trace.endSection();
   6345         }
   6346 
   6347         /**
   6348          * Return the view type of the item at <code>position</code> for the purposes
   6349          * of view recycling.
   6350          *
   6351          * <p>The default implementation of this method returns 0, making the assumption of
   6352          * a single view type for the adapter. Unlike ListView adapters, types need not
   6353          * be contiguous. Consider using id resources to uniquely identify item view types.
   6354          *
   6355          * @param position position to query
   6356          * @return integer value identifying the type of the view needed to represent the item at
   6357          *                 <code>position</code>. Type codes need not be contiguous.
   6358          */
   6359         public int getItemViewType(int position) {
   6360             return 0;
   6361         }
   6362 
   6363         /**
   6364          * Indicates whether each item in the data set can be represented with a unique identifier
   6365          * of type {@link java.lang.Long}.
   6366          *
   6367          * @param hasStableIds Whether items in data set have unique identifiers or not.
   6368          * @see #hasStableIds()
   6369          * @see #getItemId(int)
   6370          */
   6371         public void setHasStableIds(boolean hasStableIds) {
   6372             if (hasObservers()) {
   6373                 throw new IllegalStateException("Cannot change whether this adapter has "
   6374                         + "stable IDs while the adapter has registered observers.");
   6375             }
   6376             mHasStableIds = hasStableIds;
   6377         }
   6378 
   6379         /**
   6380          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
   6381          * would return false this method should return {@link #NO_ID}. The default implementation
   6382          * of this method returns {@link #NO_ID}.
   6383          *
   6384          * @param position Adapter position to query
   6385          * @return the stable ID of the item at position
   6386          */
   6387         public long getItemId(int position) {
   6388             return NO_ID;
   6389         }
   6390 
   6391         /**
   6392          * Returns the total number of items in the data set held by the adapter.
   6393          *
   6394          * @return The total number of items in this adapter.
   6395          */
   6396         public abstract int getItemCount();
   6397 
   6398         /**
   6399          * Returns true if this adapter publishes a unique <code>long</code> value that can
   6400          * act as a key for the item at a given position in the data set. If that item is relocated
   6401          * in the data set, the ID returned for that item should be the same.
   6402          *
   6403          * @return true if this adapter's items have stable IDs
   6404          */
   6405         public final boolean hasStableIds() {
   6406             return mHasStableIds;
   6407         }
   6408 
   6409         /**
   6410          * Called when a view created by this adapter has been recycled.
   6411          *
   6412          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
   6413          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
   6414          * fallen out of visibility or a set of cached views represented by views still
   6415          * attached to the parent RecyclerView. If an item view has large or expensive data
   6416          * bound to it such as large bitmaps, this may be a good place to release those
   6417          * resources.</p>
   6418          * <p>
   6419          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   6420          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   6421          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   6422          * its adapter position.
   6423          *
   6424          * @param holder The ViewHolder for the view being recycled
   6425          */
   6426         public void onViewRecycled(VH holder) {
   6427         }
   6428 
   6429         /**
   6430          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
   6431          * due to its transient state. Upon receiving this callback, Adapter can clear the
   6432          * animation(s) that effect the View's transient state and return <code>true</code> so that
   6433          * the View can be recycled. Keep in mind that the View in question is already removed from
   6434          * the RecyclerView.
   6435          * <p>
   6436          * In some cases, it is acceptable to recycle a View although it has transient state. Most
   6437          * of the time, this is a case where the transient state will be cleared in
   6438          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
   6439          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
   6440          * value of this method to decide whether the View should be recycled or not.
   6441          * <p>
   6442          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
   6443          * should never receive this callback because RecyclerView keeps those Views as children
   6444          * until their animations are complete. This callback is useful when children of the item
   6445          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
   6446          * <p>
   6447          * You should <em>never</em> fix this issue by calling
   6448          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
   6449          * <code>holder.itemView.setHasTransientState(true);</code>. Each
   6450          * <code>View.setHasTransientState(true)</code> call must be matched by a
   6451          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
   6452          * may become inconsistent. You should always prefer to end or cancel animations that are
   6453          * triggering the transient state instead of handling it manually.
   6454          *
   6455          * @param holder The ViewHolder containing the View that could not be recycled due to its
   6456          *               transient state.
   6457          * @return True if the View should be recycled, false otherwise. Note that if this method
   6458          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
   6459          * the View and recycle it regardless. If this method returns <code>false</code>,
   6460          * RecyclerView will check the View's transient state again before giving a final decision.
   6461          * Default implementation returns false.
   6462          */
   6463         public boolean onFailedToRecycleView(VH holder) {
   6464             return false;
   6465         }
   6466 
   6467         /**
   6468          * Called when a view created by this adapter has been attached to a window.
   6469          *
   6470          * <p>This can be used as a reasonable signal that the view is about to be seen
   6471          * by the user. If the adapter previously freed any resources in
   6472          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
   6473          * those resources should be restored here.</p>
   6474          *
   6475          * @param holder Holder of the view being attached
   6476          */
   6477         public void onViewAttachedToWindow(VH holder) {
   6478         }
   6479 
   6480         /**
   6481          * Called when a view created by this adapter has been detached from its window.
   6482          *
   6483          * <p>Becoming detached from the window is not necessarily a permanent condition;
   6484          * the consumer of an Adapter's views may choose to cache views offscreen while they
   6485          * are not visible, attaching and detaching them as appropriate.</p>
   6486          *
   6487          * @param holder Holder of the view being detached
   6488          */
   6489         public void onViewDetachedFromWindow(VH holder) {
   6490         }
   6491 
   6492         /**
   6493          * Returns true if one or more observers are attached to this adapter.
   6494          *
   6495          * @return true if this adapter has observers
   6496          */
   6497         public final boolean hasObservers() {
   6498             return mObservable.hasObservers();
   6499         }
   6500 
   6501         /**
   6502          * Register a new observer to listen for data changes.
   6503          *
   6504          * <p>The adapter may publish a variety of events describing specific changes.
   6505          * Not all adapters may support all change types and some may fall back to a generic
   6506          * {@link com.android.internal.widget.RecyclerView.AdapterDataObserver#onChanged()
   6507          * "something changed"} event if more specific data is not available.</p>
   6508          *
   6509          * <p>Components registering observers with an adapter are responsible for
   6510          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6511          * unregistering} those observers when finished.</p>
   6512          *
   6513          * @param observer Observer to register
   6514          *
   6515          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6516          */
   6517         public void registerAdapterDataObserver(AdapterDataObserver observer) {
   6518             mObservable.registerObserver(observer);
   6519         }
   6520 
   6521         /**
   6522          * Unregister an observer currently listening for data changes.
   6523          *
   6524          * <p>The unregistered observer will no longer receive events about changes
   6525          * to the adapter.</p>
   6526          *
   6527          * @param observer Observer to unregister
   6528          *
   6529          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6530          */
   6531         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
   6532             mObservable.unregisterObserver(observer);
   6533         }
   6534 
   6535         /**
   6536          * Called by RecyclerView when it starts observing this Adapter.
   6537          * <p>
   6538          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
   6539          *
   6540          * @param recyclerView The RecyclerView instance which started observing this adapter.
   6541          * @see #onDetachedFromRecyclerView(RecyclerView)
   6542          */
   6543         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
   6544         }
   6545 
   6546         /**
   6547          * Called by RecyclerView when it stops observing this Adapter.
   6548          *
   6549          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
   6550          * @see #onAttachedToRecyclerView(RecyclerView)
   6551          */
   6552         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
   6553         }
   6554 
   6555         /**
   6556          * Notify any registered observers that the data set has changed.
   6557          *
   6558          * <p>There are two different classes of data change events, item changes and structural
   6559          * changes. Item changes are when a single item has its data updated but no positional
   6560          * changes have occurred. Structural changes are when items are inserted, removed or moved
   6561          * within the data set.</p>
   6562          *
   6563          * <p>This event does not specify what about the data set has changed, forcing
   6564          * any observers to assume that all existing items and structure may no longer be valid.
   6565          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
   6566          *
   6567          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
   6568          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
   6569          * this method is used. This can help for the purposes of animation and visual
   6570          * object persistence but individual item views will still need to be rebound
   6571          * and relaid out.</p>
   6572          *
   6573          * <p>If you are writing an adapter it will always be more efficient to use the more
   6574          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
   6575          * as a last resort.</p>
   6576          *
   6577          * @see #notifyItemChanged(int)
   6578          * @see #notifyItemInserted(int)
   6579          * @see #notifyItemRemoved(int)
   6580          * @see #notifyItemRangeChanged(int, int)
   6581          * @see #notifyItemRangeInserted(int, int)
   6582          * @see #notifyItemRangeRemoved(int, int)
   6583          */
   6584         public final void notifyDataSetChanged() {
   6585             mObservable.notifyChanged();
   6586         }
   6587 
   6588         /**
   6589          * Notify any registered observers that the item at <code>position</code> has changed.
   6590          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
   6591          *
   6592          * <p>This is an item change event, not a structural change event. It indicates that any
   6593          * reflection of the data at <code>position</code> is out of date and should be updated.
   6594          * The item at <code>position</code> retains the same identity.</p>
   6595          *
   6596          * @param position Position of the item that has changed
   6597          *
   6598          * @see #notifyItemRangeChanged(int, int)
   6599          */
   6600         public final void notifyItemChanged(int position) {
   6601             mObservable.notifyItemRangeChanged(position, 1);
   6602         }
   6603 
   6604         /**
   6605          * Notify any registered observers that the item at <code>position</code> has changed with
   6606          * an optional payload object.
   6607          *
   6608          * <p>This is an item change event, not a structural change event. It indicates that any
   6609          * reflection of the data at <code>position</code> is out of date and should be updated.
   6610          * The item at <code>position</code> retains the same identity.
   6611          * </p>
   6612          *
   6613          * <p>
   6614          * Client can optionally pass a payload for partial change. These payloads will be merged
   6615          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   6616          * item is already represented by a ViewHolder and it will be rebound to the same
   6617          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   6618          * payloads on that item and prevent future payload until
   6619          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   6620          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   6621          * attached, the payload will be simply dropped.
   6622          *
   6623          * @param position Position of the item that has changed
   6624          * @param payload Optional parameter, use null to identify a "full" update
   6625          *
   6626          * @see #notifyItemRangeChanged(int, int)
   6627          */
   6628         public final void notifyItemChanged(int position, Object payload) {
   6629             mObservable.notifyItemRangeChanged(position, 1, payload);
   6630         }
   6631 
   6632         /**
   6633          * Notify any registered observers that the <code>itemCount</code> items starting at
   6634          * position <code>positionStart</code> have changed.
   6635          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
   6636          *
   6637          * <p>This is an item change event, not a structural change event. It indicates that
   6638          * any reflection of the data in the given position range is out of date and should
   6639          * be updated. The items in the given range retain the same identity.</p>
   6640          *
   6641          * @param positionStart Position of the first item that has changed
   6642          * @param itemCount Number of items that have changed
   6643          *
   6644          * @see #notifyItemChanged(int)
   6645          */
   6646         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
   6647             mObservable.notifyItemRangeChanged(positionStart, itemCount);
   6648         }
   6649 
   6650         /**
   6651          * Notify any registered observers that the <code>itemCount</code> items starting at
   6652          * position <code>positionStart</code> have changed. An optional payload can be
   6653          * passed to each changed item.
   6654          *
   6655          * <p>This is an item change event, not a structural change event. It indicates that any
   6656          * reflection of the data in the given position range is out of date and should be updated.
   6657          * The items in the given range retain the same identity.
   6658          * </p>
   6659          *
   6660          * <p>
   6661          * Client can optionally pass a payload for partial change. These payloads will be merged
   6662          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   6663          * item is already represented by a ViewHolder and it will be rebound to the same
   6664          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   6665          * payloads on that item and prevent future payload until
   6666          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   6667          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   6668          * attached, the payload will be simply dropped.
   6669          *
   6670          * @param positionStart Position of the first item that has changed
   6671          * @param itemCount Number of items that have changed
   6672          * @param payload  Optional parameter, use null to identify a "full" update
   6673          *
   6674          * @see #notifyItemChanged(int)
   6675          */
   6676         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
   6677             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
   6678         }
   6679 
   6680         /**
   6681          * Notify any registered observers that the item reflected at <code>position</code>
   6682          * has been newly inserted. The item previously at <code>position</code> is now at
   6683          * position <code>position + 1</code>.
   6684          *
   6685          * <p>This is a structural change event. Representations of other existing items in the
   6686          * data set are still considered up to date and will not be rebound, though their
   6687          * positions may be altered.</p>
   6688          *
   6689          * @param position Position of the newly inserted item in the data set
   6690          *
   6691          * @see #notifyItemRangeInserted(int, int)
   6692          */
   6693         public final void notifyItemInserted(int position) {
   6694             mObservable.notifyItemRangeInserted(position, 1);
   6695         }
   6696 
   6697         /**
   6698          * Notify any registered observers that the item reflected at <code>fromPosition</code>
   6699          * has been moved to <code>toPosition</code>.
   6700          *
   6701          * <p>This is a structural change event. Representations of other existing items in the
   6702          * data set are still considered up to date and will not be rebound, though their
   6703          * positions may be altered.</p>
   6704          *
   6705          * @param fromPosition Previous position of the item.
   6706          * @param toPosition New position of the item.
   6707          */
   6708         public final void notifyItemMoved(int fromPosition, int toPosition) {
   6709             mObservable.notifyItemMoved(fromPosition, toPosition);
   6710         }
   6711 
   6712         /**
   6713          * Notify any registered observers that the currently reflected <code>itemCount</code>
   6714          * items starting at <code>positionStart</code> have been newly inserted. The items
   6715          * previously located at <code>positionStart</code> and beyond can now be found starting
   6716          * at position <code>positionStart + itemCount</code>.
   6717          *
   6718          * <p>This is a structural change event. Representations of other existing items in the
   6719          * data set are still considered up to date and will not be rebound, though their positions
   6720          * may be altered.</p>
   6721          *
   6722          * @param positionStart Position of the first item that was inserted
   6723          * @param itemCount Number of items inserted
   6724          *
   6725          * @see #notifyItemInserted(int)
   6726          */
   6727         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
   6728             mObservable.notifyItemRangeInserted(positionStart, itemCount);
   6729         }
   6730 
   6731         /**
   6732          * Notify any registered observers that the item previously located at <code>position</code>
   6733          * has been removed from the data set. The items previously located at and after
   6734          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
   6735          *
   6736          * <p>This is a structural change event. Representations of other existing items in the
   6737          * data set are still considered up to date and will not be rebound, though their positions
   6738          * may be altered.</p>
   6739          *
   6740          * @param position Position of the item that has now been removed
   6741          *
   6742          * @see #notifyItemRangeRemoved(int, int)
   6743          */
   6744         public final void notifyItemRemoved(int position) {
   6745             mObservable.notifyItemRangeRemoved(position, 1);
   6746         }
   6747 
   6748         /**
   6749          * Notify any registered observers that the <code>itemCount</code> items previously
   6750          * located at <code>positionStart</code> have been removed from the data set. The items
   6751          * previously located at and after <code>positionStart + itemCount</code> may now be found
   6752          * at <code>oldPosition - itemCount</code>.
   6753          *
   6754          * <p>This is a structural change event. Representations of other existing items in the data
   6755          * set are still considered up to date and will not be rebound, though their positions
   6756          * may be altered.</p>
   6757          *
   6758          * @param positionStart Previous position of the first item that was removed
   6759          * @param itemCount Number of items removed from the data set
   6760          */
   6761         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
   6762             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
   6763         }
   6764     }
   6765 
   6766     void dispatchChildDetached(View child) {
   6767         final ViewHolder viewHolder = getChildViewHolderInt(child);
   6768         onChildDetachedFromWindow(child);
   6769         if (mAdapter != null && viewHolder != null) {
   6770             mAdapter.onViewDetachedFromWindow(viewHolder);
   6771         }
   6772         if (mOnChildAttachStateListeners != null) {
   6773             final int cnt = mOnChildAttachStateListeners.size();
   6774             for (int i = cnt - 1; i >= 0; i--) {
   6775                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
   6776             }
   6777         }
   6778     }
   6779 
   6780     void dispatchChildAttached(View child) {
   6781         final ViewHolder viewHolder = getChildViewHolderInt(child);
   6782         onChildAttachedToWindow(child);
   6783         if (mAdapter != null && viewHolder != null) {
   6784             mAdapter.onViewAttachedToWindow(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).onChildViewAttachedToWindow(child);
   6790             }
   6791         }
   6792     }
   6793 
   6794     /**
   6795      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
   6796      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
   6797      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
   6798      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
   6799      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
   6800      * layout managers are provided for general use.
   6801      * <p/>
   6802      * If the LayoutManager specifies a default constructor or one with the signature
   6803      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
   6804      * instantiate and set the LayoutManager when being inflated. Most used properties can
   6805      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
   6806      * a LayoutManager specifies both constructors, the non-default constructor will take
   6807      * precedence.
   6808      *
   6809      */
   6810     public abstract static class LayoutManager {
   6811         ChildHelper mChildHelper;
   6812         RecyclerView mRecyclerView;
   6813 
   6814         @Nullable
   6815         SmoothScroller mSmoothScroller;
   6816 
   6817         boolean mRequestedSimpleAnimations = false;
   6818 
   6819         boolean mIsAttachedToWindow = false;
   6820 
   6821         boolean mAutoMeasure = false;
   6822 
   6823         /**
   6824          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
   6825          * if the space that will be given to it is already larger than what it has measured before.
   6826          */
   6827         private boolean mMeasurementCacheEnabled = true;
   6828 
   6829         private boolean mItemPrefetchEnabled = true;
   6830 
   6831         /**
   6832          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
   6833          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
   6834          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
   6835          *
   6836          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
   6837          * will be reset upon layout to prevent initial prefetches (often large, since they're
   6838          * proportional to expected child count) from expanding cache permanently.
   6839          */
   6840         int mPrefetchMaxCountObserved;
   6841 
   6842         /**
   6843          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
   6844          */
   6845         boolean mPrefetchMaxObservedInInitialPrefetch;
   6846 
   6847         /**
   6848          * These measure specs might be the measure specs that were passed into RecyclerView's
   6849          * onMeasure method OR fake measure specs created by the RecyclerView.
   6850          * For example, when a layout is run, RecyclerView always sets these specs to be
   6851          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
   6852          * <p>
   6853          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
   6854          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
   6855          * corrupt values. Older platforms have no responsibility to provide a size if they set
   6856          * mode to unspecified.
   6857          */
   6858         private int mWidthMode, mHeightMode;
   6859         private int mWidth, mHeight;
   6860 
   6861 
   6862         /**
   6863          * Interface for LayoutManagers to request items to be prefetched, based on position, with
   6864          * specified distance from viewport, which indicates priority.
   6865          *
   6866          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
   6867          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
   6868          */
   6869         public interface LayoutPrefetchRegistry {
   6870             /**
   6871              * Requests an an item to be prefetched, based on position, with a specified distance,
   6872              * indicating priority.
   6873              *
   6874              * @param layoutPosition Position of the item to prefetch.
   6875              * @param pixelDistance Distance from the current viewport to the bounds of the item,
   6876              *                      must be non-negative.
   6877              */
   6878             void addPosition(int layoutPosition, int pixelDistance);
   6879         }
   6880 
   6881         void setRecyclerView(RecyclerView recyclerView) {
   6882             if (recyclerView == null) {
   6883                 mRecyclerView = null;
   6884                 mChildHelper = null;
   6885                 mWidth = 0;
   6886                 mHeight = 0;
   6887             } else {
   6888                 mRecyclerView = recyclerView;
   6889                 mChildHelper = recyclerView.mChildHelper;
   6890                 mWidth = recyclerView.getWidth();
   6891                 mHeight = recyclerView.getHeight();
   6892             }
   6893             mWidthMode = MeasureSpec.EXACTLY;
   6894             mHeightMode = MeasureSpec.EXACTLY;
   6895         }
   6896 
   6897         void setMeasureSpecs(int wSpec, int hSpec) {
   6898             mWidth = MeasureSpec.getSize(wSpec);
   6899             mWidthMode = MeasureSpec.getMode(wSpec);
   6900             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
   6901                 mWidth = 0;
   6902             }
   6903 
   6904             mHeight = MeasureSpec.getSize(hSpec);
   6905             mHeightMode = MeasureSpec.getMode(hSpec);
   6906             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
   6907                 mHeight = 0;
   6908             }
   6909         }
   6910 
   6911         /**
   6912          * Called after a layout is calculated during a measure pass when using auto-measure.
   6913          * <p>
   6914          * It simply traverses all children to calculate a bounding box then calls
   6915          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
   6916          * if they need to handle the bounding box differently.
   6917          * <p>
   6918          * For example, GridLayoutManager override that method to ensure that even if a column is
   6919          * empty, the GridLayoutManager still measures wide enough to include it.
   6920          *
   6921          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
   6922          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
   6923          */
   6924         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
   6925             final int count = getChildCount();
   6926             if (count == 0) {
   6927                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
   6928                 return;
   6929             }
   6930             int minX = Integer.MAX_VALUE;
   6931             int minY = Integer.MAX_VALUE;
   6932             int maxX = Integer.MIN_VALUE;
   6933             int maxY = Integer.MIN_VALUE;
   6934 
   6935             for (int i = 0; i < count; i++) {
   6936                 View child = getChildAt(i);
   6937                 final Rect bounds = mRecyclerView.mTempRect;
   6938                 getDecoratedBoundsWithMargins(child, bounds);
   6939                 if (bounds.left < minX) {
   6940                     minX = bounds.left;
   6941                 }
   6942                 if (bounds.right > maxX) {
   6943                     maxX = bounds.right;
   6944                 }
   6945                 if (bounds.top < minY) {
   6946                     minY = bounds.top;
   6947                 }
   6948                 if (bounds.bottom > maxY) {
   6949                     maxY = bounds.bottom;
   6950                 }
   6951             }
   6952             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
   6953             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
   6954         }
   6955 
   6956         /**
   6957          * Sets the measured dimensions from the given bounding box of the children and the
   6958          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
   6959          * called after the RecyclerView calls
   6960          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
   6961          * <p>
   6962          * This method should call {@link #setMeasuredDimension(int, int)}.
   6963          * <p>
   6964          * The default implementation adds the RecyclerView's padding to the given bounding box
   6965          * then caps the value to be within the given measurement specs.
   6966          * <p>
   6967          * This method is only called if the LayoutManager opted into the auto measurement API.
   6968          *
   6969          * @param childrenBounds The bounding box of all children
   6970          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
   6971          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
   6972          *
   6973          * @see #setAutoMeasureEnabled(boolean)
   6974          */
   6975         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
   6976             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
   6977             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
   6978             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
   6979             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
   6980             setMeasuredDimension(width, height);
   6981         }
   6982 
   6983         /**
   6984          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
   6985          */
   6986         public void requestLayout() {
   6987             if (mRecyclerView != null) {
   6988                 mRecyclerView.requestLayout();
   6989             }
   6990         }
   6991 
   6992         /**
   6993          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   6994          * {@link IllegalStateException} if it <b>is not</b>.
   6995          *
   6996          * @param message The message for the exception. Can be null.
   6997          * @see #assertNotInLayoutOrScroll(String)
   6998          */
   6999         public void assertInLayoutOrScroll(String message) {
   7000             if (mRecyclerView != null) {
   7001                 mRecyclerView.assertInLayoutOrScroll(message);
   7002             }
   7003         }
   7004 
   7005         /**
   7006          * Chooses a size from the given specs and parameters that is closest to the desired size
   7007          * and also complies with the spec.
   7008          *
   7009          * @param spec The measureSpec
   7010          * @param desired The preferred measurement
   7011          * @param min The minimum value
   7012          *
   7013          * @return A size that fits to the given specs
   7014          */
   7015         public static int chooseSize(int spec, int desired, int min) {
   7016             final int mode = View.MeasureSpec.getMode(spec);
   7017             final int size = View.MeasureSpec.getSize(spec);
   7018             switch (mode) {
   7019                 case View.MeasureSpec.EXACTLY:
   7020                     return size;
   7021                 case View.MeasureSpec.AT_MOST:
   7022                     return Math.min(size, Math.max(desired, min));
   7023                 case View.MeasureSpec.UNSPECIFIED:
   7024                 default:
   7025                     return Math.max(desired, min);
   7026             }
   7027         }
   7028 
   7029         /**
   7030          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   7031          * {@link IllegalStateException} if it <b>is</b>.
   7032          *
   7033          * @param message The message for the exception. Can be null.
   7034          * @see #assertInLayoutOrScroll(String)
   7035          */
   7036         public void assertNotInLayoutOrScroll(String message) {
   7037             if (mRecyclerView != null) {
   7038                 mRecyclerView.assertNotInLayoutOrScroll(message);
   7039             }
   7040         }
   7041 
   7042         /**
   7043          * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
   7044          * wants to handle the layout measurements itself.
   7045          * <p>
   7046          * This method is usually called by the LayoutManager with value {@code true} if it wants
   7047          * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
   7048          * the measurement logic, you can call this method with {@code false} and override
   7049          * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
   7050          * <p>
   7051          * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
   7052          * handle various specs provided by the RecyclerView's parent.
   7053          * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
   7054          * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
   7055          * on children's positions. It does this while supporting all existing animation
   7056          * capabilities of the RecyclerView.
   7057          * <p>
   7058          * AutoMeasure works as follows:
   7059          * <ol>
   7060          * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
   7061          * the framework LayoutManagers use {@code auto-measure}.</li>
   7062          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
   7063          * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
   7064          * doing any layout calculation.</li>
   7065          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
   7066          * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
   7067          * decide whether to run a predictive layout or not. If it decides to do so, it will first
   7068          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
   7069          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
   7070          * return the width and height of the RecyclerView as of the last layout calculation.
   7071          * <p>
   7072          * After handling the predictive case, RecyclerView will call
   7073          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
   7074          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
   7075          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
   7076          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
   7077          * <li>After the layout calculation, RecyclerView sets the measured width & height by
   7078          * calculating the bounding box for the children (+ RecyclerView's padding). The
   7079          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
   7080          * different values. For instance, GridLayoutManager overrides this value to handle the case
   7081          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
   7082          * width to fit 3 items, not 2.</li>
   7083          * <li>Any following on measure call to the RecyclerView will run
   7084          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
   7085          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
   7086          * take care of which views are actually added / removed / moved / changed for animations so
   7087          * that the LayoutManager should not worry about them and handle each
   7088          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
   7089          * </li>
   7090          * <li>When measure is complete and RecyclerView's
   7091          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
   7092          * whether it already did layout calculations during the measure pass and if so, it re-uses
   7093          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
   7094          * if the last measure spec was different from the final dimensions or adapter contents
   7095          * have changed between the measure call and the layout call.</li>
   7096          * <li>Finally, animations are calculated and run as usual.</li>
   7097          * </ol>
   7098          *
   7099          * @param enabled <code>True</code> if the Layout should be measured by the
   7100          *                             RecyclerView, <code>false</code> if the LayoutManager wants
   7101          *                             to measure itself.
   7102          *
   7103          * @see #setMeasuredDimension(Rect, int, int)
   7104          * @see #isAutoMeasureEnabled()
   7105          */
   7106         public void setAutoMeasureEnabled(boolean enabled) {
   7107             mAutoMeasure = enabled;
   7108         }
   7109 
   7110         /**
   7111          * Returns whether the LayoutManager uses the automatic measurement API or not.
   7112          *
   7113          * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
   7114          * <code>false</code> if it measures itself.
   7115          *
   7116          * @see #setAutoMeasureEnabled(boolean)
   7117          */
   7118         public boolean isAutoMeasureEnabled() {
   7119             return mAutoMeasure;
   7120         }
   7121 
   7122         /**
   7123          * Returns whether this LayoutManager supports automatic item animations.
   7124          * A LayoutManager wishing to support item animations should obey certain
   7125          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
   7126          * The default return value is <code>false</code>, so subclasses of LayoutManager
   7127          * will not get predictive item animations by default.
   7128          *
   7129          * <p>Whether item animations are enabled in a RecyclerView is determined both
   7130          * by the return value from this method and the
   7131          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
   7132          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
   7133          * method returns false, then simple item animations will be enabled, in which
   7134          * views that are moving onto or off of the screen are simply faded in/out. If
   7135          * the RecyclerView has a non-null ItemAnimator and this method returns true,
   7136          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
   7137          * setup up the information needed to more intelligently predict where appearing
   7138          * and disappearing views should be animated from/to.</p>
   7139          *
   7140          * @return true if predictive item animations should be enabled, false otherwise
   7141          */
   7142         public boolean supportsPredictiveItemAnimations() {
   7143             return false;
   7144         }
   7145 
   7146         /**
   7147          * Sets whether the LayoutManager should be queried for views outside of
   7148          * its viewport while the UI thread is idle between frames.
   7149          *
   7150          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
   7151          * view system traversals on devices running API 21 or greater. Default value is true.</p>
   7152          *
   7153          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
   7154          * to RenderThread and the starting up its next frame at the next VSync pulse. By
   7155          * prefetching out of window views in this time period, delays from inflation and view
   7156          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
   7157          *
   7158          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
   7159          * size of the View cache to hold prefetched views.</p>
   7160          *
   7161          * @param enabled <code>True</code> if items should be prefetched in between traversals.
   7162          *
   7163          * @see #isItemPrefetchEnabled()
   7164          */
   7165         public final void setItemPrefetchEnabled(boolean enabled) {
   7166             if (enabled != mItemPrefetchEnabled) {
   7167                 mItemPrefetchEnabled = enabled;
   7168                 mPrefetchMaxCountObserved = 0;
   7169                 if (mRecyclerView != null) {
   7170                     mRecyclerView.mRecycler.updateViewCacheSize();
   7171                 }
   7172             }
   7173         }
   7174 
   7175         /**
   7176          * Sets whether the LayoutManager should be queried for views outside of
   7177          * its viewport while the UI thread is idle between frames.
   7178          *
   7179          * @see #setItemPrefetchEnabled(boolean)
   7180          *
   7181          * @return true if item prefetch is enabled, false otherwise
   7182          */
   7183         public final boolean isItemPrefetchEnabled() {
   7184             return mItemPrefetchEnabled;
   7185         }
   7186 
   7187         /**
   7188          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
   7189          *
   7190          * <p>If item prefetch is enabled, this method is called in between traversals to gather
   7191          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
   7192          * traversals.</p>
   7193          *
   7194          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
   7195          * each item to be prepared, and these positions will have their ViewHolders created and
   7196          * bound, if there is sufficient time available, in advance of being needed by a
   7197          * scroll or layout.</p>
   7198          *
   7199          * @param dx X movement component.
   7200          * @param dy Y movement component.
   7201          * @param state State of RecyclerView
   7202          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
   7203          *
   7204          * @see #isItemPrefetchEnabled()
   7205          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
   7206          */
   7207         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
   7208                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
   7209 
   7210         /**
   7211          * Gather all positions from the LayoutManager to be prefetched in preperation for its
   7212          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
   7213          *
   7214          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
   7215          *
   7216          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
   7217          * LayoutManager, this method is called in between draw traversals to gather
   7218          * which positions this LayoutManager will first need, once it appears on the screen.</p>
   7219          *
   7220          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
   7221          * vertically scrolling LayoutManager, this method would be called when the horizontal list
   7222          * is about to come onscreen.</p>
   7223          *
   7224          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
   7225          * each item to be prepared, and these positions will have their ViewHolders created and
   7226          * bound, if there is sufficient time available, in advance of being needed by a
   7227          * scroll or layout.</p>
   7228          *
   7229          * @param adapterItemCount number of items in the associated adapter.
   7230          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
   7231          *
   7232          * @see #isItemPrefetchEnabled()
   7233          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
   7234          */
   7235         public void collectInitialPrefetchPositions(int adapterItemCount,
   7236                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
   7237 
   7238         void dispatchAttachedToWindow(RecyclerView view) {
   7239             mIsAttachedToWindow = true;
   7240             onAttachedToWindow(view);
   7241         }
   7242 
   7243         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
   7244             mIsAttachedToWindow = false;
   7245             onDetachedFromWindow(view, recycler);
   7246         }
   7247 
   7248         /**
   7249          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
   7250          * to a window.
   7251          *
   7252          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
   7253          * is attached to window.
   7254          */
   7255         public boolean isAttachedToWindow() {
   7256             return mIsAttachedToWindow;
   7257         }
   7258 
   7259         /**
   7260          * Causes the Runnable to execute on the next animation time step.
   7261          * The runnable will be run on the user interface thread.
   7262          * <p>
   7263          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   7264          *
   7265          * @param action The Runnable that will be executed.
   7266          *
   7267          * @see #removeCallbacks
   7268          */
   7269         public void postOnAnimation(Runnable action) {
   7270             if (mRecyclerView != null) {
   7271                 mRecyclerView.postOnAnimation(action);
   7272             }
   7273         }
   7274 
   7275         /**
   7276          * Removes the specified Runnable from the message queue.
   7277          * <p>
   7278          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   7279          *
   7280          * @param action The Runnable to remove from the message handling queue
   7281          *
   7282          * @return true if RecyclerView could ask the Handler to remove the Runnable,
   7283          *         false otherwise. When the returned value is true, the Runnable
   7284          *         may or may not have been actually removed from the message queue
   7285          *         (for instance, if the Runnable was not in the queue already.)
   7286          *
   7287          * @see #postOnAnimation
   7288          */
   7289         public boolean removeCallbacks(Runnable action) {
   7290             if (mRecyclerView != null) {
   7291                 return mRecyclerView.removeCallbacks(action);
   7292             }
   7293             return false;
   7294         }
   7295         /**
   7296          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
   7297          * is attached to a window.
   7298          * <p>
   7299          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
   7300          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
   7301          * not requested on the RecyclerView while it was detached.
   7302          * <p>
   7303          * Subclass implementations should always call through to the superclass implementation.
   7304          *
   7305          * @param view The RecyclerView this LayoutManager is bound to
   7306          *
   7307          * @see #onDetachedFromWindow(RecyclerView, Recycler)
   7308          */
   7309         @CallSuper
   7310         public void onAttachedToWindow(RecyclerView view) {
   7311         }
   7312 
   7313         /**
   7314          * @deprecated
   7315          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
   7316          */
   7317         @Deprecated
   7318         public void onDetachedFromWindow(RecyclerView view) {
   7319 
   7320         }
   7321 
   7322         /**
   7323          * Called when this LayoutManager is detached from its parent RecyclerView or when
   7324          * its parent RecyclerView is detached from its window.
   7325          * <p>
   7326          * LayoutManager should clear all of its View references as another LayoutManager might be
   7327          * assigned to the RecyclerView.
   7328          * <p>
   7329          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
   7330          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
   7331          * not requested on the RecyclerView while it was detached.
   7332          * <p>
   7333          * If your LayoutManager has View references that it cleans in on-detach, it should also
   7334          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
   7335          * RecyclerView is re-attached.
   7336          * <p>
   7337          * Subclass implementations should always call through to the superclass implementation.
   7338          *
   7339          * @param view The RecyclerView this LayoutManager is bound to
   7340          * @param recycler The recycler to use if you prefer to recycle your children instead of
   7341          *                 keeping them around.
   7342          *
   7343          * @see #onAttachedToWindow(RecyclerView)
   7344          */
   7345         @CallSuper
   7346         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
   7347             onDetachedFromWindow(view);
   7348         }
   7349 
   7350         /**
   7351          * Check if the RecyclerView is configured to clip child views to its padding.
   7352          *
   7353          * @return true if this RecyclerView clips children to its padding, false otherwise
   7354          */
   7355         public boolean getClipToPadding() {
   7356             return mRecyclerView != null && mRecyclerView.mClipToPadding;
   7357         }
   7358 
   7359         /**
   7360          * Lay out all relevant child views from the given adapter.
   7361          *
   7362          * The LayoutManager is in charge of the behavior of item animations. By default,
   7363          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
   7364          * item animations are enabled. This means that add/remove operations on the
   7365          * adapter will result in animations to add new or appearing items, removed or
   7366          * disappearing items, and moved items. If a LayoutManager returns false from
   7367          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
   7368          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
   7369          * RecyclerView will have enough information to run those animations in a simple
   7370          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
   7371          * simply fade views in and out, whether they are actually added/removed or whether
   7372          * they are moved on or off the screen due to other add/remove operations.
   7373          *
   7374          * <p>A LayoutManager wanting a better item animation experience, where items can be
   7375          * animated onto and off of the screen according to where the items exist when they
   7376          * are not on screen, then the LayoutManager should return true from
   7377          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
   7378          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
   7379          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
   7380          * once as a "pre" layout step to determine where items would have been prior to
   7381          * a real layout, and again to do the "real" layout. In the pre-layout phase,
   7382          * items will remember their pre-layout positions to allow them to be laid out
   7383          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
   7384          * be returned from the scrap to help determine correct placement of other items.
   7385          * These removed items should not be added to the child list, but should be used
   7386          * to help calculate correct positioning of other views, including views that
   7387          * were not previously onscreen (referred to as APPEARING views), but whose
   7388          * pre-layout offscreen position can be determined given the extra
   7389          * information about the pre-layout removed views.</p>
   7390          *
   7391          * <p>The second layout pass is the real layout in which only non-removed views
   7392          * will be used. The only additional requirement during this pass is, if
   7393          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
   7394          * views exist in the child list prior to layout and which are not there after
   7395          * layout (referred to as DISAPPEARING views), and to position/layout those views
   7396          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
   7397          * the animation system to know the location to which to animate these disappearing
   7398          * views.</p>
   7399          *
   7400          * <p>The default LayoutManager implementations for RecyclerView handle all of these
   7401          * requirements for animations already. Clients of RecyclerView can either use one
   7402          * of these layout managers directly or look at their implementations of
   7403          * onLayoutChildren() to see how they account for the APPEARING and
   7404          * DISAPPEARING views.</p>
   7405          *
   7406          * @param recycler         Recycler to use for fetching potentially cached views for a
   7407          *                         position
   7408          * @param state            Transient state of RecyclerView
   7409          */
   7410         public void onLayoutChildren(Recycler recycler, State state) {
   7411             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
   7412         }
   7413 
   7414         /**
   7415          * Called after a full layout calculation is finished. The layout calculation may include
   7416          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
   7417          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
   7418          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
   7419          * <p>
   7420          * This is a good place for the LayoutManager to do some cleanup like pending scroll
   7421          * position, saved state etc.
   7422          *
   7423          * @param state Transient state of RecyclerView
   7424          */
   7425         public void onLayoutCompleted(State state) {
   7426         }
   7427 
   7428         /**
   7429          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
   7430          *
   7431          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
   7432          * to store extra information specific to the layout. Client code should subclass
   7433          * {@link RecyclerView.LayoutParams} for this purpose.</p>
   7434          *
   7435          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   7436          * you must also override
   7437          * {@link #checkLayoutParams(LayoutParams)},
   7438          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   7439          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   7440          *
   7441          * @return A new LayoutParams for a child view
   7442          */
   7443         public abstract LayoutParams generateDefaultLayoutParams();
   7444 
   7445         /**
   7446          * Determines the validity of the supplied LayoutParams object.
   7447          *
   7448          * <p>This should check to make sure that the object is of the correct type
   7449          * and all values are within acceptable ranges. The default implementation
   7450          * returns <code>true</code> for non-null params.</p>
   7451          *
   7452          * @param lp LayoutParams object to check
   7453          * @return true if this LayoutParams object is valid, false otherwise
   7454          */
   7455         public boolean checkLayoutParams(LayoutParams lp) {
   7456             return lp != null;
   7457         }
   7458 
   7459         /**
   7460          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
   7461          * values from the supplied LayoutParams object if possible.
   7462          *
   7463          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   7464          * you must also override
   7465          * {@link #checkLayoutParams(LayoutParams)},
   7466          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   7467          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   7468          *
   7469          * @param lp Source LayoutParams object to copy values from
   7470          * @return a new LayoutParams object
   7471          */
   7472         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
   7473             if (lp instanceof LayoutParams) {
   7474                 return new LayoutParams((LayoutParams) lp);
   7475             } else if (lp instanceof MarginLayoutParams) {
   7476                 return new LayoutParams((MarginLayoutParams) lp);
   7477             } else {
   7478                 return new LayoutParams(lp);
   7479             }
   7480         }
   7481 
   7482         /**
   7483          * Create a LayoutParams object suitable for this LayoutManager from
   7484          * an inflated layout resource.
   7485          *
   7486          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   7487          * you must also override
   7488          * {@link #checkLayoutParams(LayoutParams)},
   7489          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   7490          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   7491          *
   7492          * @param c Context for obtaining styled attributes
   7493          * @param attrs AttributeSet describing the supplied arguments
   7494          * @return a new LayoutParams object
   7495          */
   7496         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
   7497             return new LayoutParams(c, attrs);
   7498         }
   7499 
   7500         /**
   7501          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
   7502          * The default implementation does nothing and returns 0.
   7503          *
   7504          * @param dx            distance to scroll by in pixels. X increases as scroll position
   7505          *                      approaches the right.
   7506          * @param recycler      Recycler to use for fetching potentially cached views for a
   7507          *                      position
   7508          * @param state         Transient state of RecyclerView
   7509          * @return The actual distance scrolled. The return value will be negative if dx was
   7510          * negative and scrolling proceeeded in that direction.
   7511          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
   7512          */
   7513         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
   7514             return 0;
   7515         }
   7516 
   7517         /**
   7518          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
   7519          * The default implementation does nothing and returns 0.
   7520          *
   7521          * @param dy            distance to scroll in pixels. Y increases as scroll position
   7522          *                      approaches the bottom.
   7523          * @param recycler      Recycler to use for fetching potentially cached views for a
   7524          *                      position
   7525          * @param state         Transient state of RecyclerView
   7526          * @return The actual distance scrolled. The return value will be negative if dy was
   7527          * negative and scrolling proceeeded in that direction.
   7528          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
   7529          */
   7530         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
   7531             return 0;
   7532         }
   7533 
   7534         /**
   7535          * Query if horizontal scrolling is currently supported. The default implementation
   7536          * returns false.
   7537          *
   7538          * @return True if this LayoutManager can scroll the current contents horizontally
   7539          */
   7540         public boolean canScrollHorizontally() {
   7541             return false;
   7542         }
   7543 
   7544         /**
   7545          * Query if vertical scrolling is currently supported. The default implementation
   7546          * returns false.
   7547          *
   7548          * @return True if this LayoutManager can scroll the current contents vertically
   7549          */
   7550         public boolean canScrollVertically() {
   7551             return false;
   7552         }
   7553 
   7554         /**
   7555          * Scroll to the specified adapter position.
   7556          *
   7557          * Actual position of the item on the screen depends on the LayoutManager implementation.
   7558          * @param position Scroll to this adapter position.
   7559          */
   7560         public void scrollToPosition(int position) {
   7561             if (DEBUG) {
   7562                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
   7563             }
   7564         }
   7565 
   7566         /**
   7567          * <p>Smooth scroll to the specified adapter position.</p>
   7568          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
   7569          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
   7570          * </p>
   7571          * @param recyclerView The RecyclerView to which this layout manager is attached
   7572          * @param state    Current State of RecyclerView
   7573          * @param position Scroll to this adapter position.
   7574          */
   7575         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
   7576                 int position) {
   7577             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
   7578         }
   7579 
   7580         /**
   7581          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
   7582          * <p>Calling this method will cancel any previous smooth scroll request.</p>
   7583          * @param smoothScroller Instance which defines how smooth scroll should be animated
   7584          */
   7585         public void startSmoothScroll(SmoothScroller smoothScroller) {
   7586             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
   7587                     && mSmoothScroller.isRunning()) {
   7588                 mSmoothScroller.stop();
   7589             }
   7590             mSmoothScroller = smoothScroller;
   7591             mSmoothScroller.start(mRecyclerView, this);
   7592         }
   7593 
   7594         /**
   7595          * @return true if RecycylerView is currently in the state of smooth scrolling.
   7596          */
   7597         public boolean isSmoothScrolling() {
   7598             return mSmoothScroller != null && mSmoothScroller.isRunning();
   7599         }
   7600 
   7601 
   7602         /**
   7603          * Returns the resolved layout direction for this RecyclerView.
   7604          *
   7605          * @return {@link android.view.View#LAYOUT_DIRECTION_RTL} if the layout
   7606          * direction is RTL or returns
   7607          * {@link android.view.View#LAYOUT_DIRECTION_LTR} if the layout direction
   7608          * is not RTL.
   7609          */
   7610         public int getLayoutDirection() {
   7611             return mRecyclerView.getLayoutDirection();
   7612         }
   7613 
   7614         /**
   7615          * Ends all animations on the view created by the {@link ItemAnimator}.
   7616          *
   7617          * @param view The View for which the animations should be ended.
   7618          * @see RecyclerView.ItemAnimator#endAnimations()
   7619          */
   7620         public void endAnimation(View view) {
   7621             if (mRecyclerView.mItemAnimator != null) {
   7622                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
   7623             }
   7624         }
   7625 
   7626         /**
   7627          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   7628          * to the layout that is known to be going away, either because it has been
   7629          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   7630          * visible portion of the container but is being laid out in order to inform RecyclerView
   7631          * in how to animate the item out of view.
   7632          * <p>
   7633          * Views added via this method are going to be invisible to LayoutManager after the
   7634          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   7635          * or won't be included in {@link #getChildCount()} method.
   7636          *
   7637          * @param child View to add and then remove with animation.
   7638          */
   7639         public void addDisappearingView(View child) {
   7640             addDisappearingView(child, -1);
   7641         }
   7642 
   7643         /**
   7644          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   7645          * to the layout that is known to be going away, either because it has been
   7646          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   7647          * visible portion of the container but is being laid out in order to inform RecyclerView
   7648          * in how to animate the item out of view.
   7649          * <p>
   7650          * Views added via this method are going to be invisible to LayoutManager after the
   7651          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   7652          * or won't be included in {@link #getChildCount()} method.
   7653          *
   7654          * @param child View to add and then remove with animation.
   7655          * @param index Index of the view.
   7656          */
   7657         public void addDisappearingView(View child, int index) {
   7658             addViewInt(child, index, true);
   7659         }
   7660 
   7661         /**
   7662          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   7663          * use this method to add views obtained from a {@link Recycler} using
   7664          * {@link Recycler#getViewForPosition(int)}.
   7665          *
   7666          * @param child View to add
   7667          */
   7668         public void addView(View child) {
   7669             addView(child, -1);
   7670         }
   7671 
   7672         /**
   7673          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   7674          * use this method to add views obtained from a {@link Recycler} using
   7675          * {@link Recycler#getViewForPosition(int)}.
   7676          *
   7677          * @param child View to add
   7678          * @param index Index to add child at
   7679          */
   7680         public void addView(View child, int index) {
   7681             addViewInt(child, index, false);
   7682         }
   7683 
   7684         private void addViewInt(View child, int index, boolean disappearing) {
   7685             final ViewHolder holder = getChildViewHolderInt(child);
   7686             if (disappearing || holder.isRemoved()) {
   7687                 // these views will be hidden at the end of the layout pass.
   7688                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
   7689             } else {
   7690                 // This may look like unnecessary but may happen if layout manager supports
   7691                 // predictive layouts and adapter removed then re-added the same item.
   7692                 // In this case, added version will be visible in the post layout (because add is
   7693                 // deferred) but RV will still bind it to the same View.
   7694                 // So if a View re-appears in post layout pass, remove it from disappearing list.
   7695                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
   7696             }
   7697             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   7698             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
   7699                 if (holder.isScrap()) {
   7700                     holder.unScrap();
   7701                 } else {
   7702                     holder.clearReturnedFromScrapFlag();
   7703                 }
   7704                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
   7705                 if (DISPATCH_TEMP_DETACH) {
   7706                     child.dispatchFinishTemporaryDetach();
   7707                 }
   7708             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
   7709                 // ensure in correct position
   7710                 int currentIndex = mChildHelper.indexOfChild(child);
   7711                 if (index == -1) {
   7712                     index = mChildHelper.getChildCount();
   7713                 }
   7714                 if (currentIndex == -1) {
   7715                     throw new IllegalStateException("Added View has RecyclerView as parent but"
   7716                             + " view is not a real child. Unfiltered index:"
   7717                             + mRecyclerView.indexOfChild(child));
   7718                 }
   7719                 if (currentIndex != index) {
   7720                     mRecyclerView.mLayout.moveView(currentIndex, index);
   7721                 }
   7722             } else {
   7723                 mChildHelper.addView(child, index, false);
   7724                 lp.mInsetsDirty = true;
   7725                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
   7726                     mSmoothScroller.onChildAttachedToWindow(child);
   7727                 }
   7728             }
   7729             if (lp.mPendingInvalidate) {
   7730                 if (DEBUG) {
   7731                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
   7732                 }
   7733                 holder.itemView.invalidate();
   7734                 lp.mPendingInvalidate = false;
   7735             }
   7736         }
   7737 
   7738         /**
   7739          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   7740          * use this method to completely remove a child view that is no longer needed.
   7741          * LayoutManagers should strongly consider recycling removed views using
   7742          * {@link Recycler#recycleView(android.view.View)}.
   7743          *
   7744          * @param child View to remove
   7745          */
   7746         public void removeView(View child) {
   7747             mChildHelper.removeView(child);
   7748         }
   7749 
   7750         /**
   7751          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   7752          * use this method to completely remove a child view that is no longer needed.
   7753          * LayoutManagers should strongly consider recycling removed views using
   7754          * {@link Recycler#recycleView(android.view.View)}.
   7755          *
   7756          * @param index Index of the child view to remove
   7757          */
   7758         public void removeViewAt(int index) {
   7759             final View child = getChildAt(index);
   7760             if (child != null) {
   7761                 mChildHelper.removeViewAt(index);
   7762             }
   7763         }
   7764 
   7765         /**
   7766          * Remove all views from the currently attached RecyclerView. This will not recycle
   7767          * any of the affected views; the LayoutManager is responsible for doing so if desired.
   7768          */
   7769         public void removeAllViews() {
   7770             // Only remove non-animating views
   7771             final int childCount = getChildCount();
   7772             for (int i = childCount - 1; i >= 0; i--) {
   7773                 mChildHelper.removeViewAt(i);
   7774             }
   7775         }
   7776 
   7777         /**
   7778          * Returns offset of the RecyclerView's text baseline from the its top boundary.
   7779          *
   7780          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
   7781          * there is no baseline.
   7782          */
   7783         public int getBaseline() {
   7784             return -1;
   7785         }
   7786 
   7787         /**
   7788          * Returns the adapter position of the item represented by the given View. This does not
   7789          * contain any adapter changes that might have happened after the last layout.
   7790          *
   7791          * @param view The view to query
   7792          * @return The adapter position of the item which is rendered by this View.
   7793          */
   7794         public int getPosition(View view) {
   7795             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
   7796         }
   7797 
   7798         /**
   7799          * Returns the View type defined by the adapter.
   7800          *
   7801          * @param view The view to query
   7802          * @return The type of the view assigned by the adapter.
   7803          */
   7804         public int getItemViewType(View view) {
   7805             return getChildViewHolderInt(view).getItemViewType();
   7806         }
   7807 
   7808         /**
   7809          * Traverses the ancestors of the given view and returns the item view that contains it
   7810          * and also a direct child of the LayoutManager.
   7811          * <p>
   7812          * Note that this method may return null if the view is a child of the RecyclerView but
   7813          * not a child of the LayoutManager (e.g. running a disappear animation).
   7814          *
   7815          * @param view The view that is a descendant of the LayoutManager.
   7816          *
   7817          * @return The direct child of the LayoutManager which contains the given view or null if
   7818          * the provided view is not a descendant of this LayoutManager.
   7819          *
   7820          * @see RecyclerView#getChildViewHolder(View)
   7821          * @see RecyclerView#findContainingViewHolder(View)
   7822          */
   7823         @Nullable
   7824         public View findContainingItemView(View view) {
   7825             if (mRecyclerView == null) {
   7826                 return null;
   7827             }
   7828             View found = mRecyclerView.findContainingItemView(view);
   7829             if (found == null) {
   7830                 return null;
   7831             }
   7832             if (mChildHelper.isHidden(found)) {
   7833                 return null;
   7834             }
   7835             return found;
   7836         }
   7837 
   7838         /**
   7839          * Finds the view which represents the given adapter position.
   7840          * <p>
   7841          * This method traverses each child since it has no information about child order.
   7842          * Override this method to improve performance if your LayoutManager keeps data about
   7843          * child views.
   7844          * <p>
   7845          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
   7846          *
   7847          * @param position Position of the item in adapter
   7848          * @return The child view that represents the given position or null if the position is not
   7849          * laid out
   7850          */
   7851         public View findViewByPosition(int position) {
   7852             final int childCount = getChildCount();
   7853             for (int i = 0; i < childCount; i++) {
   7854                 View child = getChildAt(i);
   7855                 ViewHolder vh = getChildViewHolderInt(child);
   7856                 if (vh == null) {
   7857                     continue;
   7858                 }
   7859                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
   7860                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
   7861                     return child;
   7862                 }
   7863             }
   7864             return null;
   7865         }
   7866 
   7867         /**
   7868          * Temporarily detach a child view.
   7869          *
   7870          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   7871          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   7872          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   7873          * so that the detached view may be rebound and reused.</p>
   7874          *
   7875          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   7876          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   7877          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   7878          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   7879          *
   7880          * @param child Child to detach
   7881          */
   7882         public void detachView(View child) {
   7883             final int ind = mChildHelper.indexOfChild(child);
   7884             if (ind >= 0) {
   7885                 detachViewInternal(ind, child);
   7886             }
   7887         }
   7888 
   7889         /**
   7890          * Temporarily detach a child view.
   7891          *
   7892          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   7893          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   7894          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   7895          * so that the detached view may be rebound and reused.</p>
   7896          *
   7897          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   7898          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   7899          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   7900          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   7901          *
   7902          * @param index Index of the child to detach
   7903          */
   7904         public void detachViewAt(int index) {
   7905             detachViewInternal(index, getChildAt(index));
   7906         }
   7907 
   7908         private void detachViewInternal(int index, View view) {
   7909             if (DISPATCH_TEMP_DETACH) {
   7910                 view.dispatchStartTemporaryDetach();
   7911             }
   7912             mChildHelper.detachViewFromParent(index);
   7913         }
   7914 
   7915         /**
   7916          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   7917          * This method should not be used to reattach views that were previously
   7918          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   7919          *
   7920          * @param child Child to reattach
   7921          * @param index Intended child index for child
   7922          * @param lp LayoutParams for child
   7923          */
   7924         public void attachView(View child, int index, LayoutParams lp) {
   7925             ViewHolder vh = getChildViewHolderInt(child);
   7926             if (vh.isRemoved()) {
   7927                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
   7928             } else {
   7929                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
   7930             }
   7931             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
   7932             if (DISPATCH_TEMP_DETACH)  {
   7933                 child.dispatchFinishTemporaryDetach();
   7934             }
   7935         }
   7936 
   7937         /**
   7938          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   7939          * This method should not be used to reattach views that were previously
   7940          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   7941          *
   7942          * @param child Child to reattach
   7943          * @param index Intended child index for child
   7944          */
   7945         public void attachView(View child, int index) {
   7946             attachView(child, index, (LayoutParams) child.getLayoutParams());
   7947         }
   7948 
   7949         /**
   7950          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   7951          * This method should not be used to reattach views that were previously
   7952          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   7953          *
   7954          * @param child Child to reattach
   7955          */
   7956         public void attachView(View child) {
   7957             attachView(child, -1);
   7958         }
   7959 
   7960         /**
   7961          * Finish removing a view that was previously temporarily
   7962          * {@link #detachView(android.view.View) detached}.
   7963          *
   7964          * @param child Detached child to remove
   7965          */
   7966         public void removeDetachedView(View child) {
   7967             mRecyclerView.removeDetachedView(child, false);
   7968         }
   7969 
   7970         /**
   7971          * Moves a View from one position to another.
   7972          *
   7973          * @param fromIndex The View's initial index
   7974          * @param toIndex The View's target index
   7975          */
   7976         public void moveView(int fromIndex, int toIndex) {
   7977             View view = getChildAt(fromIndex);
   7978             if (view == null) {
   7979                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
   7980                         + fromIndex);
   7981             }
   7982             detachViewAt(fromIndex);
   7983             attachView(view, toIndex);
   7984         }
   7985 
   7986         /**
   7987          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
   7988          *
   7989          * <p>Scrapping a view allows it to be rebound and reused to show updated or
   7990          * different data.</p>
   7991          *
   7992          * @param child Child to detach and scrap
   7993          * @param recycler Recycler to deposit the new scrap view into
   7994          */
   7995         public void detachAndScrapView(View child, Recycler recycler) {
   7996             int index = mChildHelper.indexOfChild(child);
   7997             scrapOrRecycleView(recycler, index, child);
   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 index Index of child to detach and scrap
   8007          * @param recycler Recycler to deposit the new scrap view into
   8008          */
   8009         public void detachAndScrapViewAt(int index, Recycler recycler) {
   8010             final View child = getChildAt(index);
   8011             scrapOrRecycleView(recycler, index, child);
   8012         }
   8013 
   8014         /**
   8015          * Remove a child view and recycle it using the given Recycler.
   8016          *
   8017          * @param child Child to remove and recycle
   8018          * @param recycler Recycler to use to recycle child
   8019          */
   8020         public void removeAndRecycleView(View child, Recycler recycler) {
   8021             removeView(child);
   8022             recycler.recycleView(child);
   8023         }
   8024 
   8025         /**
   8026          * Remove a child view and recycle it using the given Recycler.
   8027          *
   8028          * @param index Index of child to remove and recycle
   8029          * @param recycler Recycler to use to recycle child
   8030          */
   8031         public void removeAndRecycleViewAt(int index, Recycler recycler) {
   8032             final View view = getChildAt(index);
   8033             removeViewAt(index);
   8034             recycler.recycleView(view);
   8035         }
   8036 
   8037         /**
   8038          * Return the current number of child views attached to the parent RecyclerView.
   8039          * This does not include child views that were temporarily detached and/or scrapped.
   8040          *
   8041          * @return Number of attached children
   8042          */
   8043         public int getChildCount() {
   8044             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
   8045         }
   8046 
   8047         /**
   8048          * Return the child view at the given index
   8049          * @param index Index of child to return
   8050          * @return Child view at index
   8051          */
   8052         public View getChildAt(int index) {
   8053             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
   8054         }
   8055 
   8056         /**
   8057          * Return the width measurement spec mode of the RecyclerView.
   8058          * <p>
   8059          * This value is set only if the LayoutManager opts into the auto measure api via
   8060          * {@link #setAutoMeasureEnabled(boolean)}.
   8061          * <p>
   8062          * When RecyclerView is running a layout, this value is always set to
   8063          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
   8064          *
   8065          * @return Width measure spec mode.
   8066          *
   8067          * @see View.MeasureSpec#getMode(int)
   8068          * @see View#onMeasure(int, int)
   8069          */
   8070         public int getWidthMode() {
   8071             return mWidthMode;
   8072         }
   8073 
   8074         /**
   8075          * Return the height measurement spec mode of the RecyclerView.
   8076          * <p>
   8077          * This value is set only if the LayoutManager opts into the auto measure api via
   8078          * {@link #setAutoMeasureEnabled(boolean)}.
   8079          * <p>
   8080          * When RecyclerView is running a layout, this value is always set to
   8081          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
   8082          *
   8083          * @return Height measure spec mode.
   8084          *
   8085          * @see View.MeasureSpec#getMode(int)
   8086          * @see View#onMeasure(int, int)
   8087          */
   8088         public int getHeightMode() {
   8089             return mHeightMode;
   8090         }
   8091 
   8092         /**
   8093          * Return the width of the parent RecyclerView
   8094          *
   8095          * @return Width in pixels
   8096          */
   8097         public int getWidth() {
   8098             return mWidth;
   8099         }
   8100 
   8101         /**
   8102          * Return the height of the parent RecyclerView
   8103          *
   8104          * @return Height in pixels
   8105          */
   8106         public int getHeight() {
   8107             return mHeight;
   8108         }
   8109 
   8110         /**
   8111          * Return the left padding of the parent RecyclerView
   8112          *
   8113          * @return Padding in pixels
   8114          */
   8115         public int getPaddingLeft() {
   8116             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
   8117         }
   8118 
   8119         /**
   8120          * Return the top padding of the parent RecyclerView
   8121          *
   8122          * @return Padding in pixels
   8123          */
   8124         public int getPaddingTop() {
   8125             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
   8126         }
   8127 
   8128         /**
   8129          * Return the right padding of the parent RecyclerView
   8130          *
   8131          * @return Padding in pixels
   8132          */
   8133         public int getPaddingRight() {
   8134             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
   8135         }
   8136 
   8137         /**
   8138          * Return the bottom padding of the parent RecyclerView
   8139          *
   8140          * @return Padding in pixels
   8141          */
   8142         public int getPaddingBottom() {
   8143             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
   8144         }
   8145 
   8146         /**
   8147          * Return the start padding of the parent RecyclerView
   8148          *
   8149          * @return Padding in pixels
   8150          */
   8151         public int getPaddingStart() {
   8152             return mRecyclerView != null ? mRecyclerView.getPaddingStart() : 0;
   8153         }
   8154 
   8155         /**
   8156          * Return the end padding of the parent RecyclerView
   8157          *
   8158          * @return Padding in pixels
   8159          */
   8160         public int getPaddingEnd() {
   8161             return mRecyclerView != null ? mRecyclerView.getPaddingEnd() : 0;
   8162         }
   8163 
   8164         /**
   8165          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
   8166          *
   8167          * @return True if the RecyclerView has focus, false otherwise.
   8168          * @see View#isFocused()
   8169          */
   8170         public boolean isFocused() {
   8171             return mRecyclerView != null && mRecyclerView.isFocused();
   8172         }
   8173 
   8174         /**
   8175          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
   8176          *
   8177          * @return true if the RecyclerView has or contains focus
   8178          * @see View#hasFocus()
   8179          */
   8180         public boolean hasFocus() {
   8181             return mRecyclerView != null && mRecyclerView.hasFocus();
   8182         }
   8183 
   8184         /**
   8185          * Returns the item View which has or contains focus.
   8186          *
   8187          * @return A direct child of RecyclerView which has focus or contains the focused child.
   8188          */
   8189         public View getFocusedChild() {
   8190             if (mRecyclerView == null) {
   8191                 return null;
   8192             }
   8193             final View focused = mRecyclerView.getFocusedChild();
   8194             if (focused == null || mChildHelper.isHidden(focused)) {
   8195                 return null;
   8196             }
   8197             return focused;
   8198         }
   8199 
   8200         /**
   8201          * Returns the number of items in the adapter bound to the parent RecyclerView.
   8202          * <p>
   8203          * Note that this number is not necessarily equal to
   8204          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
   8205          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
   8206          * For more details, check the documentation for
   8207          * {@link State#getItemCount() State#getItemCount()}.
   8208          *
   8209          * @return The number of items in the bound adapter
   8210          * @see State#getItemCount()
   8211          */
   8212         public int getItemCount() {
   8213             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
   8214             return a != null ? a.getItemCount() : 0;
   8215         }
   8216 
   8217         /**
   8218          * Offset all child views attached to the parent RecyclerView by dx pixels along
   8219          * the horizontal axis.
   8220          *
   8221          * @param dx Pixels to offset by
   8222          */
   8223         public void offsetChildrenHorizontal(int dx) {
   8224             if (mRecyclerView != null) {
   8225                 mRecyclerView.offsetChildrenHorizontal(dx);
   8226             }
   8227         }
   8228 
   8229         /**
   8230          * Offset all child views attached to the parent RecyclerView by dy pixels along
   8231          * the vertical axis.
   8232          *
   8233          * @param dy Pixels to offset by
   8234          */
   8235         public void offsetChildrenVertical(int dy) {
   8236             if (mRecyclerView != null) {
   8237                 mRecyclerView.offsetChildrenVertical(dy);
   8238             }
   8239         }
   8240 
   8241         /**
   8242          * Flags a view so that it will not be scrapped or recycled.
   8243          * <p>
   8244          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
   8245          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
   8246          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
   8247          * ignore the child.
   8248          * <p>
   8249          * Before this child can be recycled again, you have to call
   8250          * {@link #stopIgnoringView(View)}.
   8251          * <p>
   8252          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   8253          *
   8254          * @param view View to ignore.
   8255          * @see #stopIgnoringView(View)
   8256          */
   8257         public void ignoreView(View view) {
   8258             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
   8259                 // checking this because calling this method on a recycled or detached view may
   8260                 // cause loss of state.
   8261                 throw new IllegalArgumentException("View should be fully attached to be ignored");
   8262             }
   8263             final ViewHolder vh = getChildViewHolderInt(view);
   8264             vh.addFlags(ViewHolder.FLAG_IGNORE);
   8265             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
   8266         }
   8267 
   8268         /**
   8269          * View can be scrapped and recycled again.
   8270          * <p>
   8271          * Note that calling this method removes all information in the view holder.
   8272          * <p>
   8273          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   8274          *
   8275          * @param view View to ignore.
   8276          */
   8277         public void stopIgnoringView(View view) {
   8278             final ViewHolder vh = getChildViewHolderInt(view);
   8279             vh.stopIgnoring();
   8280             vh.resetInternal();
   8281             vh.addFlags(ViewHolder.FLAG_INVALID);
   8282         }
   8283 
   8284         /**
   8285          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
   8286          * into the given Recycler. The Recycler may prefer to reuse scrap views before
   8287          * other views that were previously recycled.
   8288          *
   8289          * @param recycler Recycler to scrap views into
   8290          */
   8291         public void detachAndScrapAttachedViews(Recycler recycler) {
   8292             final int childCount = getChildCount();
   8293             for (int i = childCount - 1; i >= 0; i--) {
   8294                 final View v = getChildAt(i);
   8295                 scrapOrRecycleView(recycler, i, v);
   8296             }
   8297         }
   8298 
   8299         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
   8300             final ViewHolder viewHolder = getChildViewHolderInt(view);
   8301             if (viewHolder.shouldIgnore()) {
   8302                 if (DEBUG) {
   8303                     Log.d(TAG, "ignoring view " + viewHolder);
   8304                 }
   8305                 return;
   8306             }
   8307             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
   8308                     && !mRecyclerView.mAdapter.hasStableIds()) {
   8309                 removeViewAt(index);
   8310                 recycler.recycleViewHolderInternal(viewHolder);
   8311             } else {
   8312                 detachViewAt(index);
   8313                 recycler.scrapView(view);
   8314                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
   8315             }
   8316         }
   8317 
   8318         /**
   8319          * Recycles the scrapped views.
   8320          * <p>
   8321          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
   8322          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
   8323          * call remove and invalidate RecyclerView to ensure UI update.
   8324          *
   8325          * @param recycler Recycler
   8326          */
   8327         void removeAndRecycleScrapInt(Recycler recycler) {
   8328             final int scrapCount = recycler.getScrapCount();
   8329             // Loop backward, recycler might be changed by removeDetachedView()
   8330             for (int i = scrapCount - 1; i >= 0; i--) {
   8331                 final View scrap = recycler.getScrapViewAt(i);
   8332                 final ViewHolder vh = getChildViewHolderInt(scrap);
   8333                 if (vh.shouldIgnore()) {
   8334                     continue;
   8335                 }
   8336                 // If the scrap view is animating, we need to cancel them first. If we cancel it
   8337                 // here, ItemAnimator callback may recycle it which will cause double recycling.
   8338                 // To avoid this, we mark it as not recycleable before calling the item animator.
   8339                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
   8340                 // the view) may recycle it too, so we guard it before we call user APIs.
   8341                 vh.setIsRecyclable(false);
   8342                 if (vh.isTmpDetached()) {
   8343                     mRecyclerView.removeDetachedView(scrap, false);
   8344                 }
   8345                 if (mRecyclerView.mItemAnimator != null) {
   8346                     mRecyclerView.mItemAnimator.endAnimation(vh);
   8347                 }
   8348                 vh.setIsRecyclable(true);
   8349                 recycler.quickRecycleScrapView(scrap);
   8350             }
   8351             recycler.clearScrap();
   8352             if (scrapCount > 0) {
   8353                 mRecyclerView.invalidate();
   8354             }
   8355         }
   8356 
   8357 
   8358         /**
   8359          * Measure a child view using standard measurement policy, taking the padding
   8360          * of the parent RecyclerView and any added item decorations into account.
   8361          *
   8362          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   8363          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   8364          *
   8365          * @param child Child view to measure
   8366          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   8367          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   8368          */
   8369         public void measureChild(View child, int widthUsed, int heightUsed) {
   8370             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8371 
   8372             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   8373             widthUsed += insets.left + insets.right;
   8374             heightUsed += insets.top + insets.bottom;
   8375             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
   8376                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
   8377                     canScrollHorizontally());
   8378             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
   8379                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
   8380                     canScrollVertically());
   8381             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
   8382                 child.measure(widthSpec, heightSpec);
   8383             }
   8384         }
   8385 
   8386         /**
   8387          * RecyclerView internally does its own View measurement caching which should help with
   8388          * WRAP_CONTENT.
   8389          * <p>
   8390          * Use this method if the View is already measured once in this layout pass.
   8391          */
   8392         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
   8393             return !mMeasurementCacheEnabled
   8394                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
   8395                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
   8396         }
   8397 
   8398         // we may consider making this public
   8399         /**
   8400          * RecyclerView internally does its own View measurement caching which should help with
   8401          * WRAP_CONTENT.
   8402          * <p>
   8403          * Use this method if the View is not yet measured and you need to decide whether to
   8404          * measure this View or not.
   8405          */
   8406         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
   8407             return child.isLayoutRequested()
   8408                     || !mMeasurementCacheEnabled
   8409                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
   8410                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
   8411         }
   8412 
   8413         /**
   8414          * In addition to the View Framework's measurement cache, RecyclerView uses its own
   8415          * additional measurement cache for its children to avoid re-measuring them when not
   8416          * necessary. It is on by default but it can be turned off via
   8417          * {@link #setMeasurementCacheEnabled(boolean)}.
   8418          *
   8419          * @return True if measurement cache is enabled, false otherwise.
   8420          *
   8421          * @see #setMeasurementCacheEnabled(boolean)
   8422          */
   8423         public boolean isMeasurementCacheEnabled() {
   8424             return mMeasurementCacheEnabled;
   8425         }
   8426 
   8427         /**
   8428          * Sets whether RecyclerView should use its own measurement cache for the children. This is
   8429          * a more aggressive cache than the framework uses.
   8430          *
   8431          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
   8432          *
   8433          * @see #isMeasurementCacheEnabled()
   8434          */
   8435         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
   8436             mMeasurementCacheEnabled = measurementCacheEnabled;
   8437         }
   8438 
   8439         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
   8440             final int specMode = MeasureSpec.getMode(spec);
   8441             final int specSize = MeasureSpec.getSize(spec);
   8442             if (dimension > 0 && childSize != dimension) {
   8443                 return false;
   8444             }
   8445             switch (specMode) {
   8446                 case MeasureSpec.UNSPECIFIED:
   8447                     return true;
   8448                 case MeasureSpec.AT_MOST:
   8449                     return specSize >= childSize;
   8450                 case MeasureSpec.EXACTLY:
   8451                     return  specSize == childSize;
   8452             }
   8453             return false;
   8454         }
   8455 
   8456         /**
   8457          * Measure a child view using standard measurement policy, taking the padding
   8458          * of the parent RecyclerView, any added item decorations and the child margins
   8459          * into account.
   8460          *
   8461          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   8462          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   8463          *
   8464          * @param child Child view to measure
   8465          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   8466          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   8467          */
   8468         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
   8469             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8470 
   8471             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   8472             widthUsed += insets.left + insets.right;
   8473             heightUsed += insets.top + insets.bottom;
   8474 
   8475             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
   8476                     getPaddingLeft() + getPaddingRight()
   8477                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
   8478                     canScrollHorizontally());
   8479             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
   8480                     getPaddingTop() + getPaddingBottom()
   8481                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
   8482                     canScrollVertically());
   8483             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
   8484                 child.measure(widthSpec, heightSpec);
   8485             }
   8486         }
   8487 
   8488         /**
   8489          * Calculate a MeasureSpec value for measuring a child view in one dimension.
   8490          *
   8491          * @param parentSize Size of the parent view where the child will be placed
   8492          * @param padding Total space currently consumed by other elements of the parent
   8493          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
   8494          *                       Generally obtained from the child view's LayoutParams
   8495          * @param canScroll true if the parent RecyclerView can scroll in this dimension
   8496          *
   8497          * @return a MeasureSpec value for the child view
   8498          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
   8499          */
   8500         @Deprecated
   8501         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
   8502                 boolean canScroll) {
   8503             int size = Math.max(0, parentSize - padding);
   8504             int resultSize = 0;
   8505             int resultMode = 0;
   8506             if (canScroll) {
   8507                 if (childDimension >= 0) {
   8508                     resultSize = childDimension;
   8509                     resultMode = MeasureSpec.EXACTLY;
   8510                 } else {
   8511                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
   8512                     // instead using UNSPECIFIED.
   8513                     resultSize = 0;
   8514                     resultMode = MeasureSpec.UNSPECIFIED;
   8515                 }
   8516             } else {
   8517                 if (childDimension >= 0) {
   8518                     resultSize = childDimension;
   8519                     resultMode = MeasureSpec.EXACTLY;
   8520                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   8521                     resultSize = size;
   8522                     // TODO this should be my spec.
   8523                     resultMode = MeasureSpec.EXACTLY;
   8524                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   8525                     resultSize = size;
   8526                     resultMode = MeasureSpec.AT_MOST;
   8527                 }
   8528             }
   8529             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
   8530         }
   8531 
   8532         /**
   8533          * Calculate a MeasureSpec value for measuring a child view in one dimension.
   8534          *
   8535          * @param parentSize Size of the parent view where the child will be placed
   8536          * @param parentMode The measurement spec mode of the parent
   8537          * @param padding Total space currently consumed by other elements of parent
   8538          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
   8539          *                       Generally obtained from the child view's LayoutParams
   8540          * @param canScroll true if the parent RecyclerView can scroll in this dimension
   8541          *
   8542          * @return a MeasureSpec value for the child view
   8543          */
   8544         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
   8545                 int childDimension, boolean canScroll) {
   8546             int size = Math.max(0, parentSize - padding);
   8547             int resultSize = 0;
   8548             int resultMode = 0;
   8549             if (canScroll) {
   8550                 if (childDimension >= 0) {
   8551                     resultSize = childDimension;
   8552                     resultMode = MeasureSpec.EXACTLY;
   8553                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   8554                     switch (parentMode) {
   8555                         case MeasureSpec.AT_MOST:
   8556                         case MeasureSpec.EXACTLY:
   8557                             resultSize = size;
   8558                             resultMode = parentMode;
   8559                             break;
   8560                         case MeasureSpec.UNSPECIFIED:
   8561                             resultSize = 0;
   8562                             resultMode = MeasureSpec.UNSPECIFIED;
   8563                             break;
   8564                     }
   8565                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   8566                     resultSize = 0;
   8567                     resultMode = MeasureSpec.UNSPECIFIED;
   8568                 }
   8569             } else {
   8570                 if (childDimension >= 0) {
   8571                     resultSize = childDimension;
   8572                     resultMode = MeasureSpec.EXACTLY;
   8573                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   8574                     resultSize = size;
   8575                     resultMode = parentMode;
   8576                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   8577                     resultSize = size;
   8578                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
   8579                         resultMode = MeasureSpec.AT_MOST;
   8580                     } else {
   8581                         resultMode = MeasureSpec.UNSPECIFIED;
   8582                     }
   8583 
   8584                 }
   8585             }
   8586             //noinspection WrongConstant
   8587             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
   8588         }
   8589 
   8590         /**
   8591          * Returns the measured width of the given child, plus the additional size of
   8592          * any insets applied by {@link ItemDecoration ItemDecorations}.
   8593          *
   8594          * @param child Child view to query
   8595          * @return child's measured width plus <code>ItemDecoration</code> insets
   8596          *
   8597          * @see View#getMeasuredWidth()
   8598          */
   8599         public int getDecoratedMeasuredWidth(View child) {
   8600             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8601             return child.getMeasuredWidth() + insets.left + insets.right;
   8602         }
   8603 
   8604         /**
   8605          * Returns the measured height 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 height plus <code>ItemDecoration</code> insets
   8610          *
   8611          * @see View#getMeasuredHeight()
   8612          */
   8613         public int getDecoratedMeasuredHeight(View child) {
   8614             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8615             return child.getMeasuredHeight() + insets.top + insets.bottom;
   8616         }
   8617 
   8618         /**
   8619          * Lay out the given child view within the RecyclerView using coordinates that
   8620          * include any current {@link ItemDecoration ItemDecorations}.
   8621          *
   8622          * <p>LayoutManagers should prefer working in sizes and coordinates that include
   8623          * item decoration insets whenever possible. This allows the LayoutManager to effectively
   8624          * ignore decoration insets within measurement and layout code. See the following
   8625          * methods:</p>
   8626          * <ul>
   8627          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
   8628          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
   8629          *     <li>{@link #measureChild(View, int, int)}</li>
   8630          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
   8631          *     <li>{@link #getDecoratedLeft(View)}</li>
   8632          *     <li>{@link #getDecoratedTop(View)}</li>
   8633          *     <li>{@link #getDecoratedRight(View)}</li>
   8634          *     <li>{@link #getDecoratedBottom(View)}</li>
   8635          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
   8636          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
   8637          * </ul>
   8638          *
   8639          * @param child Child to lay out
   8640          * @param left Left edge, with item decoration insets included
   8641          * @param top Top edge, with item decoration insets included
   8642          * @param right Right edge, with item decoration insets included
   8643          * @param bottom Bottom edge, with item decoration insets included
   8644          *
   8645          * @see View#layout(int, int, int, int)
   8646          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
   8647          */
   8648         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
   8649             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8650             child.layout(left + insets.left, top + insets.top, right - insets.right,
   8651                     bottom - insets.bottom);
   8652         }
   8653 
   8654         /**
   8655          * Lay out the given child view within the RecyclerView using coordinates that
   8656          * include any current {@link ItemDecoration ItemDecorations} and margins.
   8657          *
   8658          * <p>LayoutManagers should prefer working in sizes and coordinates that include
   8659          * item decoration insets whenever possible. This allows the LayoutManager to effectively
   8660          * ignore decoration insets within measurement and layout code. See the following
   8661          * methods:</p>
   8662          * <ul>
   8663          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
   8664          *     <li>{@link #measureChild(View, int, int)}</li>
   8665          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
   8666          *     <li>{@link #getDecoratedLeft(View)}</li>
   8667          *     <li>{@link #getDecoratedTop(View)}</li>
   8668          *     <li>{@link #getDecoratedRight(View)}</li>
   8669          *     <li>{@link #getDecoratedBottom(View)}</li>
   8670          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
   8671          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
   8672          * </ul>
   8673          *
   8674          * @param child Child to lay out
   8675          * @param left Left edge, with item decoration insets and left margin included
   8676          * @param top Top edge, with item decoration insets and top margin included
   8677          * @param right Right edge, with item decoration insets and right margin included
   8678          * @param bottom Bottom edge, with item decoration insets and bottom margin included
   8679          *
   8680          * @see View#layout(int, int, int, int)
   8681          * @see #layoutDecorated(View, int, int, int, int)
   8682          */
   8683         public void layoutDecoratedWithMargins(View child, int left, int top, int right,
   8684                 int bottom) {
   8685             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8686             final Rect insets = lp.mDecorInsets;
   8687             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
   8688                     right - insets.right - lp.rightMargin,
   8689                     bottom - insets.bottom - lp.bottomMargin);
   8690         }
   8691 
   8692         /**
   8693          * Calculates the bounding box of the View while taking into account its matrix changes
   8694          * (translation, scale etc) with respect to the RecyclerView.
   8695          * <p>
   8696          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
   8697          * the View's matrix so that the decor offsets also go through the same transformation.
   8698          *
   8699          * @param child The ItemView whose bounding box should be calculated.
   8700          * @param includeDecorInsets True if the decor insets should be included in the bounding box
   8701          * @param out The rectangle into which the output will be written.
   8702          */
   8703         public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
   8704             if (includeDecorInsets) {
   8705                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8706                 out.set(-insets.left, -insets.top,
   8707                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
   8708             } else {
   8709                 out.set(0, 0, child.getWidth(), child.getHeight());
   8710             }
   8711 
   8712             if (mRecyclerView != null) {
   8713                 final Matrix childMatrix = child.getMatrix();
   8714                 if (childMatrix != null && !childMatrix.isIdentity()) {
   8715                     final RectF tempRectF = mRecyclerView.mTempRectF;
   8716                     tempRectF.set(out);
   8717                     childMatrix.mapRect(tempRectF);
   8718                     out.set(
   8719                             (int) Math.floor(tempRectF.left),
   8720                             (int) Math.floor(tempRectF.top),
   8721                             (int) Math.ceil(tempRectF.right),
   8722                             (int) Math.ceil(tempRectF.bottom)
   8723                     );
   8724                 }
   8725             }
   8726             out.offset(child.getLeft(), child.getTop());
   8727         }
   8728 
   8729         /**
   8730          * Returns the bounds of the view including its decoration and margins.
   8731          *
   8732          * @param view The view element to check
   8733          * @param outBounds A rect that will receive the bounds of the element including its
   8734          *                  decoration and margins.
   8735          */
   8736         public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
   8737             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
   8738         }
   8739 
   8740         /**
   8741          * Returns the left edge of the given child view within its parent, offset by any applied
   8742          * {@link ItemDecoration ItemDecorations}.
   8743          *
   8744          * @param child Child to query
   8745          * @return Child left edge with offsets applied
   8746          * @see #getLeftDecorationWidth(View)
   8747          */
   8748         public int getDecoratedLeft(View child) {
   8749             return child.getLeft() - getLeftDecorationWidth(child);
   8750         }
   8751 
   8752         /**
   8753          * Returns the top edge of the given child view within its parent, offset by any applied
   8754          * {@link ItemDecoration ItemDecorations}.
   8755          *
   8756          * @param child Child to query
   8757          * @return Child top edge with offsets applied
   8758          * @see #getTopDecorationHeight(View)
   8759          */
   8760         public int getDecoratedTop(View child) {
   8761             return child.getTop() - getTopDecorationHeight(child);
   8762         }
   8763 
   8764         /**
   8765          * Returns the right edge of the given child view within its parent, offset by any applied
   8766          * {@link ItemDecoration ItemDecorations}.
   8767          *
   8768          * @param child Child to query
   8769          * @return Child right edge with offsets applied
   8770          * @see #getRightDecorationWidth(View)
   8771          */
   8772         public int getDecoratedRight(View child) {
   8773             return child.getRight() + getRightDecorationWidth(child);
   8774         }
   8775 
   8776         /**
   8777          * Returns the bottom edge of the given child view within its parent, offset by any applied
   8778          * {@link ItemDecoration ItemDecorations}.
   8779          *
   8780          * @param child Child to query
   8781          * @return Child bottom edge with offsets applied
   8782          * @see #getBottomDecorationHeight(View)
   8783          */
   8784         public int getDecoratedBottom(View child) {
   8785             return child.getBottom() + getBottomDecorationHeight(child);
   8786         }
   8787 
   8788         /**
   8789          * Calculates the item decor insets applied to the given child and updates the provided
   8790          * Rect instance with the inset values.
   8791          * <ul>
   8792          *     <li>The Rect's left is set to the total width of left decorations.</li>
   8793          *     <li>The Rect's top is set to the total height of top decorations.</li>
   8794          *     <li>The Rect's right is set to the total width of right decorations.</li>
   8795          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
   8796          * </ul>
   8797          * <p>
   8798          * Note that item decorations are automatically calculated when one of the LayoutManager's
   8799          * measure child methods is called. If you need to measure the child with custom specs via
   8800          * {@link View#measure(int, int)}, you can use this method to get decorations.
   8801          *
   8802          * @param child The child view whose decorations should be calculated
   8803          * @param outRect The Rect to hold result values
   8804          */
   8805         public void calculateItemDecorationsForChild(View child, Rect outRect) {
   8806             if (mRecyclerView == null) {
   8807                 outRect.set(0, 0, 0, 0);
   8808                 return;
   8809             }
   8810             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   8811             outRect.set(insets);
   8812         }
   8813 
   8814         /**
   8815          * Returns the total height of item decorations applied to child's top.
   8816          * <p>
   8817          * Note that this value is not updated until the View is measured or
   8818          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8819          *
   8820          * @param child Child to query
   8821          * @return The total height of item decorations applied to the child's top.
   8822          * @see #getDecoratedTop(View)
   8823          * @see #calculateItemDecorationsForChild(View, Rect)
   8824          */
   8825         public int getTopDecorationHeight(View child) {
   8826             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
   8827         }
   8828 
   8829         /**
   8830          * Returns the total height of item decorations applied to child's bottom.
   8831          * <p>
   8832          * Note that this value is not updated until the View is measured or
   8833          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8834          *
   8835          * @param child Child to query
   8836          * @return The total height of item decorations applied to the child's bottom.
   8837          * @see #getDecoratedBottom(View)
   8838          * @see #calculateItemDecorationsForChild(View, Rect)
   8839          */
   8840         public int getBottomDecorationHeight(View child) {
   8841             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
   8842         }
   8843 
   8844         /**
   8845          * Returns the total width of item decorations applied to child's left.
   8846          * <p>
   8847          * Note that this value is not updated until the View is measured or
   8848          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8849          *
   8850          * @param child Child to query
   8851          * @return The total width of item decorations applied to the child's left.
   8852          * @see #getDecoratedLeft(View)
   8853          * @see #calculateItemDecorationsForChild(View, Rect)
   8854          */
   8855         public int getLeftDecorationWidth(View child) {
   8856             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
   8857         }
   8858 
   8859         /**
   8860          * Returns the total width of item decorations applied to child's right.
   8861          * <p>
   8862          * Note that this value is not updated until the View is measured or
   8863          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8864          *
   8865          * @param child Child to query
   8866          * @return The total width of item decorations applied to the child's right.
   8867          * @see #getDecoratedRight(View)
   8868          * @see #calculateItemDecorationsForChild(View, Rect)
   8869          */
   8870         public int getRightDecorationWidth(View child) {
   8871             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
   8872         }
   8873 
   8874         /**
   8875          * Called when searching for a focusable view in the given direction has failed
   8876          * for the current content of the RecyclerView.
   8877          *
   8878          * <p>This is the LayoutManager's opportunity to populate views in the given direction
   8879          * to fulfill the request if it can. The LayoutManager should attach and return
   8880          * the view to be focused. The default implementation returns null.</p>
   8881          *
   8882          * @param focused   The currently focused view
   8883          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   8884          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   8885          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   8886          *                  or 0 for not applicable
   8887          * @param recycler  The recycler to use for obtaining views for currently offscreen items
   8888          * @param state     Transient state of RecyclerView
   8889          * @return The chosen view to be focused
   8890          */
   8891         @Nullable
   8892         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
   8893                 State state) {
   8894             return null;
   8895         }
   8896 
   8897         /**
   8898          * This method gives a LayoutManager an opportunity to intercept the initial focus search
   8899          * before the default behavior of {@link FocusFinder} is used. If this method returns
   8900          * null FocusFinder will attempt to find a focusable child view. If it fails
   8901          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
   8902          * will be called to give the LayoutManager an opportunity to add new views for items
   8903          * that did not have attached views representing them. The LayoutManager should not add
   8904          * or remove views from this method.
   8905          *
   8906          * @param focused The currently focused view
   8907          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   8908          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   8909          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   8910          * @return A descendant view to focus or null to fall back to default behavior.
   8911          *         The default implementation returns null.
   8912          */
   8913         public View onInterceptFocusSearch(View focused, int direction) {
   8914             return null;
   8915         }
   8916 
   8917         /**
   8918          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
   8919          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
   8920          * android.graphics.Rect, boolean)} for more details.
   8921          *
   8922          * <p>The base implementation will attempt to perform a standard programmatic scroll
   8923          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
   8924          *
   8925          * @param child The direct child making the request.
   8926          * @param rect  The rectangle in the child's coordinates the child
   8927          *              wishes to be on the screen.
   8928          * @param immediate True to forbid animated or delayed scrolling,
   8929          *                  false otherwise
   8930          * @return Whether the group scrolled to handle the operation
   8931          */
   8932         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
   8933                 boolean immediate) {
   8934             final int parentLeft = getPaddingLeft();
   8935             final int parentTop = getPaddingTop();
   8936             final int parentRight = getWidth() - getPaddingRight();
   8937             final int parentBottom = getHeight() - getPaddingBottom();
   8938             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
   8939             final int childTop = child.getTop() + rect.top - child.getScrollY();
   8940             final int childRight = childLeft + rect.width();
   8941             final int childBottom = childTop + rect.height();
   8942 
   8943             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
   8944             final int offScreenTop = Math.min(0, childTop - parentTop);
   8945             final int offScreenRight = Math.max(0, childRight - parentRight);
   8946             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
   8947 
   8948             // Favor the "start" layout direction over the end when bringing one side or the other
   8949             // of a large rect into view. If we decide to bring in end because start is already
   8950             // visible, limit the scroll such that start won't go out of bounds.
   8951             final int dx;
   8952             if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
   8953                 dx = offScreenRight != 0 ? offScreenRight
   8954                         : Math.max(offScreenLeft, childRight - parentRight);
   8955             } else {
   8956                 dx = offScreenLeft != 0 ? offScreenLeft
   8957                         : Math.min(childLeft - parentLeft, offScreenRight);
   8958             }
   8959 
   8960             // Favor bringing the top into view over the bottom. If top is already visible and
   8961             // we should scroll to make bottom visible, make sure top does not go out of bounds.
   8962             final int dy = offScreenTop != 0 ? offScreenTop
   8963                     : Math.min(childTop - parentTop, offScreenBottom);
   8964 
   8965             if (dx != 0 || dy != 0) {
   8966                 if (immediate) {
   8967                     parent.scrollBy(dx, dy);
   8968                 } else {
   8969                     parent.smoothScrollBy(dx, dy);
   8970                 }
   8971                 return true;
   8972             }
   8973             return false;
   8974         }
   8975 
   8976         /**
   8977          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
   8978          */
   8979         @Deprecated
   8980         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
   8981             // eat the request if we are in the middle of a scroll or layout
   8982             return isSmoothScrolling() || parent.isComputingLayout();
   8983         }
   8984 
   8985         /**
   8986          * Called when a descendant view of the RecyclerView requests focus.
   8987          *
   8988          * <p>A LayoutManager wishing to keep focused views aligned in a specific
   8989          * portion of the view may implement that behavior in an override of this method.</p>
   8990          *
   8991          * <p>If the LayoutManager executes different behavior that should override the default
   8992          * behavior of scrolling the focused child on screen instead of running alongside it,
   8993          * this method should return true.</p>
   8994          *
   8995          * @param parent  The RecyclerView hosting this LayoutManager
   8996          * @param state   Current state of RecyclerView
   8997          * @param child   Direct child of the RecyclerView containing the newly focused view
   8998          * @param focused The newly focused view. This may be the same view as child or it may be
   8999          *                null
   9000          * @return true if the default scroll behavior should be suppressed
   9001          */
   9002         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
   9003                 View focused) {
   9004             return onRequestChildFocus(parent, child, focused);
   9005         }
   9006 
   9007         /**
   9008          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
   9009          * The LayoutManager may use this opportunity to clear caches and configure state such
   9010          * that it can relayout appropriately with the new data and potentially new view types.
   9011          *
   9012          * <p>The default implementation removes all currently attached views.</p>
   9013          *
   9014          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
   9015          *                   adapter.
   9016          * @param newAdapter The new adapter instance. Might be null if
   9017          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
   9018          */
   9019         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
   9020         }
   9021 
   9022         /**
   9023          * Called to populate focusable views within the RecyclerView.
   9024          *
   9025          * <p>The LayoutManager implementation should return <code>true</code> if the default
   9026          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
   9027          * suppressed.</p>
   9028          *
   9029          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
   9030          * to fall back to the default ViewGroup behavior.</p>
   9031          *
   9032          * @param recyclerView The RecyclerView hosting this LayoutManager
   9033          * @param views List of output views. This method should add valid focusable views
   9034          *              to this list.
   9035          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   9036          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   9037          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   9038          * @param focusableMode The type of focusables to be added.
   9039          *
   9040          * @return true to suppress the default behavior, false to add default focusables after
   9041          *         this method returns.
   9042          *
   9043          * @see #FOCUSABLES_ALL
   9044          * @see #FOCUSABLES_TOUCH_MODE
   9045          */
   9046         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
   9047                 int direction, int focusableMode) {
   9048             return false;
   9049         }
   9050 
   9051         /**
   9052          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
   9053          * detailed information on what has actually changed.
   9054          *
   9055          * @param recyclerView
   9056          */
   9057         public void onItemsChanged(RecyclerView recyclerView) {
   9058         }
   9059 
   9060         /**
   9061          * Called when items have been added to the adapter. The LayoutManager may choose to
   9062          * requestLayout if the inserted items would require refreshing the currently visible set
   9063          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
   9064          *
   9065          * @param recyclerView
   9066          * @param positionStart
   9067          * @param itemCount
   9068          */
   9069         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
   9070         }
   9071 
   9072         /**
   9073          * Called when items have been removed from the adapter.
   9074          *
   9075          * @param recyclerView
   9076          * @param positionStart
   9077          * @param itemCount
   9078          */
   9079         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
   9080         }
   9081 
   9082         /**
   9083          * Called when items have been changed in the adapter.
   9084          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
   9085          * instead, then this callback will not be invoked.
   9086          *
   9087          * @param recyclerView
   9088          * @param positionStart
   9089          * @param itemCount
   9090          */
   9091         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
   9092         }
   9093 
   9094         /**
   9095          * Called when items have been changed in the adapter and with optional payload.
   9096          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
   9097          *
   9098          * @param recyclerView
   9099          * @param positionStart
   9100          * @param itemCount
   9101          * @param payload
   9102          */
   9103         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
   9104                 Object payload) {
   9105             onItemsUpdated(recyclerView, positionStart, itemCount);
   9106         }
   9107 
   9108         /**
   9109          * Called when an item is moved withing the adapter.
   9110          * <p>
   9111          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
   9112          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
   9113          * is called.
   9114          *
   9115          * @param recyclerView
   9116          * @param from
   9117          * @param to
   9118          * @param itemCount
   9119          */
   9120         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
   9121 
   9122         }
   9123 
   9124 
   9125         /**
   9126          * <p>Override this method if you want to support scroll bars.</p>
   9127          *
   9128          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
   9129          *
   9130          * <p>Default implementation returns 0.</p>
   9131          *
   9132          * @param state Current state of RecyclerView
   9133          * @return The horizontal extent of the scrollbar's thumb
   9134          * @see RecyclerView#computeHorizontalScrollExtent()
   9135          */
   9136         public int computeHorizontalScrollExtent(State state) {
   9137             return 0;
   9138         }
   9139 
   9140         /**
   9141          * <p>Override this method if you want to support scroll bars.</p>
   9142          *
   9143          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
   9144          *
   9145          * <p>Default implementation returns 0.</p>
   9146          *
   9147          * @param state Current State of RecyclerView where you can find total item count
   9148          * @return The horizontal offset of the scrollbar's thumb
   9149          * @see RecyclerView#computeHorizontalScrollOffset()
   9150          */
   9151         public int computeHorizontalScrollOffset(State state) {
   9152             return 0;
   9153         }
   9154 
   9155         /**
   9156          * <p>Override this method if you want to support scroll bars.</p>
   9157          *
   9158          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
   9159          *
   9160          * <p>Default implementation returns 0.</p>
   9161          *
   9162          * @param state Current State of RecyclerView where you can find total item count
   9163          * @return The total horizontal range represented by the vertical scrollbar
   9164          * @see RecyclerView#computeHorizontalScrollRange()
   9165          */
   9166         public int computeHorizontalScrollRange(State state) {
   9167             return 0;
   9168         }
   9169 
   9170         /**
   9171          * <p>Override this method if you want to support scroll bars.</p>
   9172          *
   9173          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
   9174          *
   9175          * <p>Default implementation returns 0.</p>
   9176          *
   9177          * @param state Current state of RecyclerView
   9178          * @return The vertical extent of the scrollbar's thumb
   9179          * @see RecyclerView#computeVerticalScrollExtent()
   9180          */
   9181         public int computeVerticalScrollExtent(State state) {
   9182             return 0;
   9183         }
   9184 
   9185         /**
   9186          * <p>Override this method if you want to support scroll bars.</p>
   9187          *
   9188          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
   9189          *
   9190          * <p>Default implementation returns 0.</p>
   9191          *
   9192          * @param state Current State of RecyclerView where you can find total item count
   9193          * @return The vertical offset of the scrollbar's thumb
   9194          * @see RecyclerView#computeVerticalScrollOffset()
   9195          */
   9196         public int computeVerticalScrollOffset(State state) {
   9197             return 0;
   9198         }
   9199 
   9200         /**
   9201          * <p>Override this method if you want to support scroll bars.</p>
   9202          *
   9203          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
   9204          *
   9205          * <p>Default implementation returns 0.</p>
   9206          *
   9207          * @param state Current State of RecyclerView where you can find total item count
   9208          * @return The total vertical range represented by the vertical scrollbar
   9209          * @see RecyclerView#computeVerticalScrollRange()
   9210          */
   9211         public int computeVerticalScrollRange(State state) {
   9212             return 0;
   9213         }
   9214 
   9215         /**
   9216          * Measure the attached RecyclerView. Implementations must call
   9217          * {@link #setMeasuredDimension(int, int)} before returning.
   9218          *
   9219          * <p>The default implementation will handle EXACTLY measurements and respect
   9220          * the minimum width and height properties of the host RecyclerView if measured
   9221          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
   9222          * will consume all available space.</p>
   9223          *
   9224          * @param recycler Recycler
   9225          * @param state Transient state of RecyclerView
   9226          * @param widthSpec Width {@link android.view.View.MeasureSpec}
   9227          * @param heightSpec Height {@link android.view.View.MeasureSpec}
   9228          */
   9229         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
   9230             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
   9231         }
   9232 
   9233         /**
   9234          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
   9235          * host RecyclerView.
   9236          *
   9237          * @param widthSize Measured width
   9238          * @param heightSize Measured height
   9239          */
   9240         public void setMeasuredDimension(int widthSize, int heightSize) {
   9241             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
   9242         }
   9243 
   9244         /**
   9245          * @return The host RecyclerView's {@link View#getMinimumWidth()}
   9246          */
   9247         public int getMinimumWidth() {
   9248             return mRecyclerView.getMinimumWidth();
   9249         }
   9250 
   9251         /**
   9252          * @return The host RecyclerView's {@link View#getMinimumHeight()}
   9253          */
   9254         public int getMinimumHeight() {
   9255             return mRecyclerView.getMinimumHeight();
   9256         }
   9257         /**
   9258          * <p>Called when the LayoutManager should save its state. This is a good time to save your
   9259          * scroll position, configuration and anything else that may be required to restore the same
   9260          * layout state if the LayoutManager is recreated.</p>
   9261          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
   9262          * restore. This will let you share information between your LayoutManagers but it is also
   9263          * your responsibility to make sure they use the same parcelable class.</p>
   9264          *
   9265          * @return Necessary information for LayoutManager to be able to restore its state
   9266          */
   9267         public Parcelable onSaveInstanceState() {
   9268             return null;
   9269         }
   9270 
   9271 
   9272         public void onRestoreInstanceState(Parcelable state) {
   9273 
   9274         }
   9275 
   9276         void stopSmoothScroller() {
   9277             if (mSmoothScroller != null) {
   9278                 mSmoothScroller.stop();
   9279             }
   9280         }
   9281 
   9282         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
   9283             if (mSmoothScroller == smoothScroller) {
   9284                 mSmoothScroller = null;
   9285             }
   9286         }
   9287 
   9288         /**
   9289          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
   9290          *
   9291          * @param state The new scroll state for RecyclerView
   9292          */
   9293         public void onScrollStateChanged(int state) {
   9294         }
   9295 
   9296         /**
   9297          * Removes all views and recycles them using the given recycler.
   9298          * <p>
   9299          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
   9300          * <p>
   9301          * If a View is marked as "ignored", it is not removed nor recycled.
   9302          *
   9303          * @param recycler Recycler to use to recycle children
   9304          * @see #removeAndRecycleView(View, Recycler)
   9305          * @see #removeAndRecycleViewAt(int, Recycler)
   9306          * @see #ignoreView(View)
   9307          */
   9308         public void removeAndRecycleAllViews(Recycler recycler) {
   9309             for (int i = getChildCount() - 1; i >= 0; i--) {
   9310                 final View view = getChildAt(i);
   9311                 if (!getChildViewHolderInt(view).shouldIgnore()) {
   9312                     removeAndRecycleViewAt(i, recycler);
   9313                 }
   9314             }
   9315         }
   9316 
   9317         // called by accessibility delegate
   9318         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
   9319             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
   9320         }
   9321 
   9322         /**
   9323          * Called by the AccessibilityDelegate when the information about the current layout should
   9324          * be populated.
   9325          * <p>
   9326          * Default implementation adds a {@link
   9327          * android.view.accessibility.AccessibilityNodeInfo.CollectionInfo}.
   9328          * <p>
   9329          * You should override
   9330          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   9331          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   9332          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
   9333          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
   9334          * more accurate accessibility information.
   9335          *
   9336          * @param recycler The Recycler that can be used to convert view positions into adapter
   9337          *                 positions
   9338          * @param state    The current state of RecyclerView
   9339          * @param info     The info that should be filled by the LayoutManager
   9340          * @see View#onInitializeAccessibilityNodeInfo(
   9341          *android.view.accessibility.AccessibilityNodeInfo)
   9342          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   9343          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   9344          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
   9345          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   9346          */
   9347         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
   9348                 AccessibilityNodeInfo info) {
   9349             if (mRecyclerView.canScrollVertically(-1)
   9350                     || mRecyclerView.canScrollHorizontally(-1)) {
   9351                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
   9352                 info.setScrollable(true);
   9353             }
   9354             if (mRecyclerView.canScrollVertically(1)
   9355                     || mRecyclerView.canScrollHorizontally(1)) {
   9356                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
   9357                 info.setScrollable(true);
   9358             }
   9359             final AccessibilityNodeInfo.CollectionInfo collectionInfo =
   9360                     AccessibilityNodeInfo.CollectionInfo
   9361                             .obtain(getRowCountForAccessibility(recycler, state),
   9362                                     getColumnCountForAccessibility(recycler, state),
   9363                                     isLayoutHierarchical(recycler, state),
   9364                                     getSelectionModeForAccessibility(recycler, state));
   9365             info.setCollectionInfo(collectionInfo);
   9366         }
   9367 
   9368         // called by accessibility delegate
   9369         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
   9370             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
   9371         }
   9372 
   9373         /**
   9374          * Called by the accessibility delegate to initialize an accessibility event.
   9375          * <p>
   9376          * Default implementation adds item count and scroll information to the event.
   9377          *
   9378          * @param recycler The Recycler that can be used to convert view positions into adapter
   9379          *                 positions
   9380          * @param state    The current state of RecyclerView
   9381          * @param event    The event instance to initialize
   9382          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
   9383          */
   9384         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
   9385                 AccessibilityEvent event) {
   9386             if (mRecyclerView == null || event == null) {
   9387                 return;
   9388             }
   9389             event.setScrollable(mRecyclerView.canScrollVertically(1)
   9390                     || mRecyclerView.canScrollVertically(-1)
   9391                     || mRecyclerView.canScrollHorizontally(-1)
   9392                     || mRecyclerView.canScrollHorizontally(1));
   9393 
   9394             if (mRecyclerView.mAdapter != null) {
   9395                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
   9396             }
   9397         }
   9398 
   9399         // called by accessibility delegate
   9400         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info) {
   9401             final ViewHolder vh = getChildViewHolderInt(host);
   9402             // avoid trying to create accessibility node info for removed children
   9403             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
   9404                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
   9405                         mRecyclerView.mState, host, info);
   9406             }
   9407         }
   9408 
   9409         /**
   9410          * Called by the AccessibilityDelegate when the accessibility information for a specific
   9411          * item should be populated.
   9412          * <p>
   9413          * Default implementation adds basic positioning information about the item.
   9414          *
   9415          * @param recycler The Recycler that can be used to convert view positions into adapter
   9416          *                 positions
   9417          * @param state    The current state of RecyclerView
   9418          * @param host     The child for which accessibility node info should be populated
   9419          * @param info     The info to fill out about the item
   9420          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
   9421          * android.view.accessibility.AccessibilityNodeInfo)
   9422          */
   9423         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
   9424                 View host, AccessibilityNodeInfo info) {
   9425             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
   9426             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
   9427             final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
   9428                     AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndexGuess, 1,
   9429                             columnIndexGuess, 1, false, false);
   9430             info.setCollectionItemInfo(itemInfo);
   9431         }
   9432 
   9433         /**
   9434          * A LayoutManager can call this method to force RecyclerView to run simple animations in
   9435          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
   9436          * change).
   9437          * <p>
   9438          * Note that, calling this method will not guarantee that RecyclerView will run animations
   9439          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
   9440          * not run any animations but will still clear this flag after the layout is complete.
   9441          *
   9442          */
   9443         public void requestSimpleAnimationsInNextLayout() {
   9444             mRequestedSimpleAnimations = true;
   9445         }
   9446 
   9447         /**
   9448          * Returns the selection mode for accessibility. Should be
   9449          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE},
   9450          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_SINGLE} or
   9451          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_MULTIPLE}.
   9452          * <p>
   9453          * Default implementation returns
   9454          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
   9455          *
   9456          * @param recycler The Recycler that can be used to convert view positions into adapter
   9457          *                 positions
   9458          * @param state    The current state of RecyclerView
   9459          * @return Selection mode for accessibility. Default implementation returns
   9460          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
   9461          */
   9462         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
   9463             return AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE;
   9464         }
   9465 
   9466         /**
   9467          * Returns the number of rows for accessibility.
   9468          * <p>
   9469          * Default implementation returns the number of items in the adapter if LayoutManager
   9470          * supports vertical scrolling or 1 if LayoutManager does not support vertical
   9471          * scrolling.
   9472          *
   9473          * @param recycler The Recycler that can be used to convert view positions into adapter
   9474          *                 positions
   9475          * @param state    The current state of RecyclerView
   9476          * @return The number of rows in LayoutManager for accessibility.
   9477          */
   9478         public int getRowCountForAccessibility(Recycler recycler, State state) {
   9479             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   9480                 return 1;
   9481             }
   9482             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
   9483         }
   9484 
   9485         /**
   9486          * Returns the number of columns for accessibility.
   9487          * <p>
   9488          * Default implementation returns the number of items in the adapter if LayoutManager
   9489          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
   9490          * scrolling.
   9491          *
   9492          * @param recycler The Recycler that can be used to convert view positions into adapter
   9493          *                 positions
   9494          * @param state    The current state of RecyclerView
   9495          * @return The number of rows in LayoutManager for accessibility.
   9496          */
   9497         public int getColumnCountForAccessibility(Recycler recycler, State state) {
   9498             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   9499                 return 1;
   9500             }
   9501             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
   9502         }
   9503 
   9504         /**
   9505          * Returns whether layout is hierarchical or not to be used for accessibility.
   9506          * <p>
   9507          * Default implementation returns false.
   9508          *
   9509          * @param recycler The Recycler that can be used to convert view positions into adapter
   9510          *                 positions
   9511          * @param state    The current state of RecyclerView
   9512          * @return True if layout is hierarchical.
   9513          */
   9514         public boolean isLayoutHierarchical(Recycler recycler, State state) {
   9515             return false;
   9516         }
   9517 
   9518         // called by accessibility delegate
   9519         boolean performAccessibilityAction(int action, Bundle args) {
   9520             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
   9521                     action, args);
   9522         }
   9523 
   9524         /**
   9525          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
   9526          *
   9527          * @param recycler  The Recycler that can be used to convert view positions into adapter
   9528          *                  positions
   9529          * @param state     The current state of RecyclerView
   9530          * @param action    The action to perform
   9531          * @param args      Optional action arguments
   9532          * @see View#performAccessibilityAction(int, android.os.Bundle)
   9533          */
   9534         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
   9535                 Bundle args) {
   9536             if (mRecyclerView == null) {
   9537                 return false;
   9538             }
   9539             int vScroll = 0, hScroll = 0;
   9540             switch (action) {
   9541                 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
   9542                     if (mRecyclerView.canScrollVertically(-1)) {
   9543                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
   9544                     }
   9545                     if (mRecyclerView.canScrollHorizontally(-1)) {
   9546                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
   9547                     }
   9548                     break;
   9549                 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
   9550                     if (mRecyclerView.canScrollVertically(1)) {
   9551                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
   9552                     }
   9553                     if (mRecyclerView.canScrollHorizontally(1)) {
   9554                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
   9555                     }
   9556                     break;
   9557             }
   9558             if (vScroll == 0 && hScroll == 0) {
   9559                 return false;
   9560             }
   9561             mRecyclerView.scrollBy(hScroll, vScroll);
   9562             return true;
   9563         }
   9564 
   9565         // called by accessibility delegate
   9566         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
   9567             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
   9568                     view, action, args);
   9569         }
   9570 
   9571         /**
   9572          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
   9573          * children of LayoutManager.
   9574          * <p>
   9575          * Default implementation does not do anything.
   9576          *
   9577          * @param recycler The Recycler that can be used to convert view positions into adapter
   9578          *                 positions
   9579          * @param state    The current state of RecyclerView
   9580          * @param view     The child view on which the action is performed
   9581          * @param action   The action to perform
   9582          * @param args     Optional action arguments
   9583          * @return true if action is handled
   9584          * @see View#performAccessibilityAction(int, android.os.Bundle)
   9585          */
   9586         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
   9587                 int action, Bundle args) {
   9588             return false;
   9589         }
   9590 
   9591         /**
   9592          * Parse the xml attributes to get the most common properties used by layout managers.
   9593          *
   9594          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
   9595          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
   9596          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
   9597          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
   9598          *
   9599          * @return an object containing the properties as specified in the attrs.
   9600          */
   9601         public static Properties getProperties(Context context, AttributeSet attrs,
   9602                 int defStyleAttr, int defStyleRes) {
   9603             Properties properties = new Properties();
   9604             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
   9605                     defStyleAttr, defStyleRes);
   9606             properties.orientation = a.getInt(R.styleable.RecyclerView_orientation, VERTICAL);
   9607             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
   9608             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
   9609             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
   9610             a.recycle();
   9611             return properties;
   9612         }
   9613 
   9614         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
   9615             setMeasureSpecs(
   9616                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
   9617                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
   9618             );
   9619         }
   9620 
   9621         /**
   9622          * Internal API to allow LayoutManagers to be measured twice.
   9623          * <p>
   9624          * This is not public because LayoutManagers should be able to handle their layouts in one
   9625          * pass but it is very convenient to make existing LayoutManagers support wrapping content
   9626          * when both orientations are undefined.
   9627          * <p>
   9628          * This API will be removed after default LayoutManagers properly implement wrap content in
   9629          * non-scroll orientation.
   9630          */
   9631         boolean shouldMeasureTwice() {
   9632             return false;
   9633         }
   9634 
   9635         boolean hasFlexibleChildInBothOrientations() {
   9636             final int childCount = getChildCount();
   9637             for (int i = 0; i < childCount; i++) {
   9638                 final View child = getChildAt(i);
   9639                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
   9640                 if (lp.width < 0 && lp.height < 0) {
   9641                     return true;
   9642                 }
   9643             }
   9644             return false;
   9645         }
   9646 
   9647         /**
   9648          * Some general properties that a LayoutManager may want to use.
   9649          */
   9650         public static class Properties {
   9651             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
   9652             public int orientation;
   9653             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
   9654             public int spanCount;
   9655             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
   9656             public boolean reverseLayout;
   9657             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
   9658             public boolean stackFromEnd;
   9659         }
   9660     }
   9661 
   9662     /**
   9663      * An ItemDecoration allows the application to add a special drawing and layout offset
   9664      * to specific item views from the adapter's data set. This can be useful for drawing dividers
   9665      * between items, highlights, visual grouping boundaries and more.
   9666      *
   9667      * <p>All ItemDecorations are drawn in the order they were added, before the item
   9668      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
   9669      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
   9670      * RecyclerView.State)}.</p>
   9671      */
   9672     public abstract static class ItemDecoration {
   9673         /**
   9674          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   9675          * Any content drawn by this method will be drawn before the item views are drawn,
   9676          * and will thus appear underneath the views.
   9677          *
   9678          * @param c Canvas to draw into
   9679          * @param parent RecyclerView this ItemDecoration is drawing into
   9680          * @param state The current state of RecyclerView
   9681          */
   9682         public void onDraw(Canvas c, RecyclerView parent, State state) {
   9683             onDraw(c, parent);
   9684         }
   9685 
   9686         /**
   9687          * @deprecated
   9688          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
   9689          */
   9690         @Deprecated
   9691         public void onDraw(Canvas c, RecyclerView parent) {
   9692         }
   9693 
   9694         /**
   9695          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   9696          * Any content drawn by this method will be drawn after the item views are drawn
   9697          * and will thus appear over the views.
   9698          *
   9699          * @param c Canvas to draw into
   9700          * @param parent RecyclerView this ItemDecoration is drawing into
   9701          * @param state The current state of RecyclerView.
   9702          */
   9703         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
   9704             onDrawOver(c, parent);
   9705         }
   9706 
   9707         /**
   9708          * @deprecated
   9709          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
   9710          */
   9711         @Deprecated
   9712         public void onDrawOver(Canvas c, RecyclerView parent) {
   9713         }
   9714 
   9715 
   9716         /**
   9717          * @deprecated
   9718          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
   9719          */
   9720         @Deprecated
   9721         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
   9722             outRect.set(0, 0, 0, 0);
   9723         }
   9724 
   9725         /**
   9726          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
   9727          * the number of pixels that the item view should be inset by, similar to padding or margin.
   9728          * The default implementation sets the bounds of outRect to 0 and returns.
   9729          *
   9730          * <p>
   9731          * If this ItemDecoration does not affect the positioning of item views, it should set
   9732          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
   9733          * before returning.
   9734          *
   9735          * <p>
   9736          * If you need to access Adapter for additional data, you can call
   9737          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
   9738          * View.
   9739          *
   9740          * @param outRect Rect to receive the output.
   9741          * @param view    The child view to decorate
   9742          * @param parent  RecyclerView this ItemDecoration is decorating
   9743          * @param state   The current state of RecyclerView.
   9744          */
   9745         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
   9746             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
   9747                     parent);
   9748         }
   9749     }
   9750 
   9751     /**
   9752      * An OnItemTouchListener allows the application to intercept touch events in progress at the
   9753      * view hierarchy level of the RecyclerView before those touch events are considered for
   9754      * RecyclerView's own scrolling behavior.
   9755      *
   9756      * <p>This can be useful for applications that wish to implement various forms of gestural
   9757      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
   9758      * a touch interaction already in progress even if the RecyclerView is already handling that
   9759      * gesture stream itself for the purposes of scrolling.</p>
   9760      *
   9761      * @see SimpleOnItemTouchListener
   9762      */
   9763     public interface OnItemTouchListener {
   9764         /**
   9765          * Silently observe and/or take over touch events sent to the RecyclerView
   9766          * before they are handled by either the RecyclerView itself or its child views.
   9767          *
   9768          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
   9769          * in the order in which each listener was added, before any other touch processing
   9770          * by the RecyclerView itself or child views occurs.</p>
   9771          *
   9772          * @param e MotionEvent describing the touch event. All coordinates are in
   9773          *          the RecyclerView's coordinate system.
   9774          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
   9775          *         to continue with the current behavior and continue observing future events in
   9776          *         the gesture.
   9777          */
   9778         boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
   9779 
   9780         /**
   9781          * Process a touch event as part of a gesture that was claimed by returning true from
   9782          * a previous call to {@link #onInterceptTouchEvent}.
   9783          *
   9784          * @param e MotionEvent describing the touch event. All coordinates are in
   9785          *          the RecyclerView's coordinate system.
   9786          */
   9787         void onTouchEvent(RecyclerView rv, MotionEvent e);
   9788 
   9789         /**
   9790          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
   9791          * intercept touch events with
   9792          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
   9793          *
   9794          * @param disallowIntercept True if the child does not want the parent to
   9795          *            intercept touch events.
   9796          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
   9797          */
   9798         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
   9799     }
   9800 
   9801     /**
   9802      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
   9803      * and default return values.
   9804      * <p>
   9805      * You may prefer to extend this class if you don't need to override all methods. Another
   9806      * benefit of using this class is future compatibility. As the interface may change, we'll
   9807      * always provide a default implementation on this class so that your code won't break when
   9808      * you update to a new version of the support library.
   9809      */
   9810     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
   9811         @Override
   9812         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
   9813             return false;
   9814         }
   9815 
   9816         @Override
   9817         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
   9818         }
   9819 
   9820         @Override
   9821         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   9822         }
   9823     }
   9824 
   9825 
   9826     /**
   9827      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
   9828      * has occurred on that RecyclerView.
   9829      * <p>
   9830      * @see RecyclerView#addOnScrollListener(OnScrollListener)
   9831      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
   9832      *
   9833      */
   9834     public abstract static class OnScrollListener {
   9835         /**
   9836          * Callback method to be invoked when RecyclerView's scroll state changes.
   9837          *
   9838          * @param recyclerView The RecyclerView whose scroll state has changed.
   9839          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
   9840          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
   9841          */
   9842         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
   9843 
   9844         /**
   9845          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
   9846          * called after the scroll has completed.
   9847          * <p>
   9848          * This callback will also be called if visible item range changes after a layout
   9849          * calculation. In that case, dx and dy will be 0.
   9850          *
   9851          * @param recyclerView The RecyclerView which scrolled.
   9852          * @param dx The amount of horizontal scroll.
   9853          * @param dy The amount of vertical scroll.
   9854          */
   9855         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
   9856     }
   9857 
   9858     /**
   9859      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
   9860      * a view is recycled.
   9861      *
   9862      * @see RecyclerView#setRecyclerListener(RecyclerListener)
   9863      */
   9864     public interface RecyclerListener {
   9865 
   9866         /**
   9867          * This method is called whenever the view in the ViewHolder is recycled.
   9868          *
   9869          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   9870          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   9871          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   9872          * its adapter position.
   9873          *
   9874          * @param holder The ViewHolder containing the view that was recycled
   9875          */
   9876         void onViewRecycled(ViewHolder holder);
   9877     }
   9878 
   9879     /**
   9880      * A Listener interface that can be attached to a RecylcerView to get notified
   9881      * whenever a ViewHolder is attached to or detached from RecyclerView.
   9882      */
   9883     public interface OnChildAttachStateChangeListener {
   9884 
   9885         /**
   9886          * Called when a view is attached to the RecyclerView.
   9887          *
   9888          * @param view The View which is attached to the RecyclerView
   9889          */
   9890         void onChildViewAttachedToWindow(View view);
   9891 
   9892         /**
   9893          * Called when a view is detached from RecyclerView.
   9894          *
   9895          * @param view The View which is being detached from the RecyclerView
   9896          */
   9897         void onChildViewDetachedFromWindow(View view);
   9898     }
   9899 
   9900     /**
   9901      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
   9902      *
   9903      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
   9904      * potentially expensive {@link View#findViewById(int)} results.</p>
   9905      *
   9906      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
   9907      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
   9908      * their own custom ViewHolder implementations to store data that makes binding view contents
   9909      * easier. Implementations should assume that individual item views will hold strong references
   9910      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
   9911      * strong references to extra off-screen item views for caching purposes</p>
   9912      */
   9913     public abstract static class ViewHolder {
   9914         public final View itemView;
   9915         WeakReference<RecyclerView> mNestedRecyclerView;
   9916         int mPosition = NO_POSITION;
   9917         int mOldPosition = NO_POSITION;
   9918         long mItemId = NO_ID;
   9919         int mItemViewType = INVALID_TYPE;
   9920         int mPreLayoutPosition = NO_POSITION;
   9921 
   9922         // The item that this holder is shadowing during an item change event/animation
   9923         ViewHolder mShadowedHolder = null;
   9924         // The item that is shadowing this holder during an item change event/animation
   9925         ViewHolder mShadowingHolder = null;
   9926 
   9927         /**
   9928          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
   9929          * are all valid.
   9930          */
   9931         static final int FLAG_BOUND = 1 << 0;
   9932 
   9933         /**
   9934          * The data this ViewHolder's view reflects is stale and needs to be rebound
   9935          * by the adapter. mPosition and mItemId are consistent.
   9936          */
   9937         static final int FLAG_UPDATE = 1 << 1;
   9938 
   9939         /**
   9940          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
   9941          * are not to be trusted and may no longer match the item view type.
   9942          * This ViewHolder must be fully rebound to different data.
   9943          */
   9944         static final int FLAG_INVALID = 1 << 2;
   9945 
   9946         /**
   9947          * This ViewHolder points at data that represents an item previously removed from the
   9948          * data set. Its view may still be used for things like outgoing animations.
   9949          */
   9950         static final int FLAG_REMOVED = 1 << 3;
   9951 
   9952         /**
   9953          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
   9954          * and is intended to keep views around during animations.
   9955          */
   9956         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
   9957 
   9958         /**
   9959          * This ViewHolder is returned from scrap which means we are expecting an addView call
   9960          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
   9961          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
   9962          * the RecyclerView.
   9963          */
   9964         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
   9965 
   9966         /**
   9967          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
   9968          * it unless LayoutManager is replaced.
   9969          * It is still fully visible to the LayoutManager.
   9970          */
   9971         static final int FLAG_IGNORE = 1 << 7;
   9972 
   9973         /**
   9974          * When the View is detached form the parent, we set this flag so that we can take correct
   9975          * action when we need to remove it or add it back.
   9976          */
   9977         static final int FLAG_TMP_DETACHED = 1 << 8;
   9978 
   9979         /**
   9980          * Set when we can no longer determine the adapter position of this ViewHolder until it is
   9981          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
   9982          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
   9983          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
   9984          * re-calculated.
   9985          */
   9986         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
   9987 
   9988         /**
   9989          * Set when a addChangePayload(null) is called
   9990          */
   9991         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
   9992 
   9993         /**
   9994          * Used by ItemAnimator when a ViewHolder's position changes
   9995          */
   9996         static final int FLAG_MOVED = 1 << 11;
   9997 
   9998         /**
   9999          * Used by ItemAnimator when a ViewHolder appears in pre-layout
   10000          */
   10001         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
   10002 
   10003         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
   10004 
   10005         /**
   10006          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
   10007          * hidden list (as if it was scrap) without being recycled in between.
   10008          *
   10009          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
   10010          *   a) Animation ends, view is recycled and used from the recycle pool.
   10011          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
   10012          *
   10013          * This flag is used to represent "case b" where the ViewHolder is reused without being
   10014          * recycled (thus "bounced" from the hidden list). This state requires special handling
   10015          * because the ViewHolder must be added to pre layout maps for animations as if it was
   10016          * already there.
   10017          */
   10018         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
   10019 
   10020         private int mFlags;
   10021 
   10022         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
   10023 
   10024         List<Object> mPayloads = null;
   10025         List<Object> mUnmodifiedPayloads = null;
   10026 
   10027         private int mIsRecyclableCount = 0;
   10028 
   10029         // If non-null, view is currently considered scrap and may be reused for other data by the
   10030         // scrap container.
   10031         private Recycler mScrapContainer = null;
   10032         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
   10033         private boolean mInChangeScrap = false;
   10034 
   10035         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
   10036         // marked as unimportant for accessibility.
   10037         private int mWasImportantForAccessibilityBeforeHidden =
   10038                 View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   10039         // set if we defer the accessibility state change of the view holder
   10040         @VisibleForTesting
   10041         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
   10042 
   10043         /**
   10044          * Is set when VH is bound from the adapter and cleaned right before it is sent to
   10045          * {@link RecycledViewPool}.
   10046          */
   10047         RecyclerView mOwnerRecyclerView;
   10048 
   10049         public ViewHolder(View itemView) {
   10050             if (itemView == null) {
   10051                 throw new IllegalArgumentException("itemView may not be null");
   10052             }
   10053             this.itemView = itemView;
   10054         }
   10055 
   10056         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
   10057             addFlags(ViewHolder.FLAG_REMOVED);
   10058             offsetPosition(offset, applyToPreLayout);
   10059             mPosition = mNewPosition;
   10060         }
   10061 
   10062         void offsetPosition(int offset, boolean applyToPreLayout) {
   10063             if (mOldPosition == NO_POSITION) {
   10064                 mOldPosition = mPosition;
   10065             }
   10066             if (mPreLayoutPosition == NO_POSITION) {
   10067                 mPreLayoutPosition = mPosition;
   10068             }
   10069             if (applyToPreLayout) {
   10070                 mPreLayoutPosition += offset;
   10071             }
   10072             mPosition += offset;
   10073             if (itemView.getLayoutParams() != null) {
   10074                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
   10075             }
   10076         }
   10077 
   10078         void clearOldPosition() {
   10079             mOldPosition = NO_POSITION;
   10080             mPreLayoutPosition = NO_POSITION;
   10081         }
   10082 
   10083         void saveOldPosition() {
   10084             if (mOldPosition == NO_POSITION) {
   10085                 mOldPosition = mPosition;
   10086             }
   10087         }
   10088 
   10089         boolean shouldIgnore() {
   10090             return (mFlags & FLAG_IGNORE) != 0;
   10091         }
   10092 
   10093         /**
   10094          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
   10095          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
   10096          * {@link #getAdapterPosition()} depending on your use case.
   10097          *
   10098          * @see #getLayoutPosition()
   10099          * @see #getAdapterPosition()
   10100          */
   10101         @Deprecated
   10102         public final int getPosition() {
   10103             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   10104         }
   10105 
   10106         /**
   10107          * Returns the position of the ViewHolder in terms of the latest layout pass.
   10108          * <p>
   10109          * This position is mostly used by RecyclerView components to be consistent while
   10110          * RecyclerView lazily processes adapter updates.
   10111          * <p>
   10112          * For performance and animation reasons, RecyclerView batches all adapter updates until the
   10113          * next layout pass. This may cause mismatches between the Adapter position of the item and
   10114          * the position it had in the latest layout calculations.
   10115          * <p>
   10116          * LayoutManagers should always call this method while doing calculations based on item
   10117          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
   10118          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
   10119          * of the item.
   10120          * <p>
   10121          * If LayoutManager needs to call an external method that requires the adapter position of
   10122          * the item, it can use {@link #getAdapterPosition()} or
   10123          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
   10124          *
   10125          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
   10126          * @see #getAdapterPosition()
   10127          */
   10128         public final int getLayoutPosition() {
   10129             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   10130         }
   10131 
   10132         /**
   10133          * Returns the Adapter position of the item represented by this ViewHolder.
   10134          * <p>
   10135          * Note that this might be different than the {@link #getLayoutPosition()} if there are
   10136          * pending adapter updates but a new layout pass has not happened yet.
   10137          * <p>
   10138          * RecyclerView does not handle any adapter updates until the next layout traversal. This
   10139          * may create temporary inconsistencies between what user sees on the screen and what
   10140          * adapter contents have. This inconsistency is not important since it will be less than
   10141          * 16ms but it might be a problem if you want to use ViewHolder position to access the
   10142          * adapter. Sometimes, you may need to get the exact adapter position to do
   10143          * some actions in response to user events. In that case, you should use this method which
   10144          * will calculate the Adapter position of the ViewHolder.
   10145          * <p>
   10146          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
   10147          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
   10148          *
   10149          * @return The adapter position of the item if it still exists in the adapter.
   10150          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
   10151          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
   10152          * layout pass or the ViewHolder has already been recycled.
   10153          */
   10154         public final int getAdapterPosition() {
   10155             if (mOwnerRecyclerView == null) {
   10156                 return NO_POSITION;
   10157             }
   10158             return mOwnerRecyclerView.getAdapterPositionFor(this);
   10159         }
   10160 
   10161         /**
   10162          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
   10163          * to perform animations.
   10164          * <p>
   10165          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
   10166          * adapter index in the previous layout.
   10167          *
   10168          * @return The previous adapter index of the Item represented by this ViewHolder or
   10169          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
   10170          * complete).
   10171          */
   10172         public final int getOldPosition() {
   10173             return mOldPosition;
   10174         }
   10175 
   10176         /**
   10177          * Returns The itemId represented by this ViewHolder.
   10178          *
   10179          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
   10180          * otherwise
   10181          */
   10182         public final long getItemId() {
   10183             return mItemId;
   10184         }
   10185 
   10186         /**
   10187          * @return The view type of this ViewHolder.
   10188          */
   10189         public final int getItemViewType() {
   10190             return mItemViewType;
   10191         }
   10192 
   10193         boolean isScrap() {
   10194             return mScrapContainer != null;
   10195         }
   10196 
   10197         void unScrap() {
   10198             mScrapContainer.unscrapView(this);
   10199         }
   10200 
   10201         boolean wasReturnedFromScrap() {
   10202             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
   10203         }
   10204 
   10205         void clearReturnedFromScrapFlag() {
   10206             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
   10207         }
   10208 
   10209         void clearTmpDetachFlag() {
   10210             mFlags = mFlags & ~FLAG_TMP_DETACHED;
   10211         }
   10212 
   10213         void stopIgnoring() {
   10214             mFlags = mFlags & ~FLAG_IGNORE;
   10215         }
   10216 
   10217         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
   10218             mScrapContainer = recycler;
   10219             mInChangeScrap = isChangeScrap;
   10220         }
   10221 
   10222         boolean isInvalid() {
   10223             return (mFlags & FLAG_INVALID) != 0;
   10224         }
   10225 
   10226         boolean needsUpdate() {
   10227             return (mFlags & FLAG_UPDATE) != 0;
   10228         }
   10229 
   10230         boolean isBound() {
   10231             return (mFlags & FLAG_BOUND) != 0;
   10232         }
   10233 
   10234         boolean isRemoved() {
   10235             return (mFlags & FLAG_REMOVED) != 0;
   10236         }
   10237 
   10238         boolean hasAnyOfTheFlags(int flags) {
   10239             return (mFlags & flags) != 0;
   10240         }
   10241 
   10242         boolean isTmpDetached() {
   10243             return (mFlags & FLAG_TMP_DETACHED) != 0;
   10244         }
   10245 
   10246         boolean isAdapterPositionUnknown() {
   10247             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
   10248         }
   10249 
   10250         void setFlags(int flags, int mask) {
   10251             mFlags = (mFlags & ~mask) | (flags & mask);
   10252         }
   10253 
   10254         void addFlags(int flags) {
   10255             mFlags |= flags;
   10256         }
   10257 
   10258         void addChangePayload(Object payload) {
   10259             if (payload == null) {
   10260                 addFlags(FLAG_ADAPTER_FULLUPDATE);
   10261             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   10262                 createPayloadsIfNeeded();
   10263                 mPayloads.add(payload);
   10264             }
   10265         }
   10266 
   10267         private void createPayloadsIfNeeded() {
   10268             if (mPayloads == null) {
   10269                 mPayloads = new ArrayList<Object>();
   10270                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
   10271             }
   10272         }
   10273 
   10274         void clearPayload() {
   10275             if (mPayloads != null) {
   10276                 mPayloads.clear();
   10277             }
   10278             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
   10279         }
   10280 
   10281         List<Object> getUnmodifiedPayloads() {
   10282             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   10283                 if (mPayloads == null || mPayloads.size() == 0) {
   10284                     // Initial state,  no update being called.
   10285                     return FULLUPDATE_PAYLOADS;
   10286                 }
   10287                 // there are none-null payloads
   10288                 return mUnmodifiedPayloads;
   10289             } else {
   10290                 // a full update has been called.
   10291                 return FULLUPDATE_PAYLOADS;
   10292             }
   10293         }
   10294 
   10295         void resetInternal() {
   10296             mFlags = 0;
   10297             mPosition = NO_POSITION;
   10298             mOldPosition = NO_POSITION;
   10299             mItemId = NO_ID;
   10300             mPreLayoutPosition = NO_POSITION;
   10301             mIsRecyclableCount = 0;
   10302             mShadowedHolder = null;
   10303             mShadowingHolder = null;
   10304             clearPayload();
   10305             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   10306             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
   10307             clearNestedRecyclerViewIfNotNested(this);
   10308         }
   10309 
   10310         /**
   10311          * Called when the child view enters the hidden state
   10312          */
   10313         private void onEnteredHiddenState(RecyclerView parent) {
   10314             // While the view item is in hidden state, make it invisible for the accessibility.
   10315             mWasImportantForAccessibilityBeforeHidden =
   10316                     itemView.getImportantForAccessibility();
   10317             parent.setChildImportantForAccessibilityInternal(this,
   10318                     View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
   10319         }
   10320 
   10321         /**
   10322          * Called when the child view leaves the hidden state
   10323          */
   10324         private void onLeftHiddenState(RecyclerView parent) {
   10325             parent.setChildImportantForAccessibilityInternal(this,
   10326                     mWasImportantForAccessibilityBeforeHidden);
   10327             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   10328         }
   10329 
   10330         @Override
   10331         public String toString() {
   10332             final StringBuilder sb = new StringBuilder("ViewHolder{"
   10333                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
   10334                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
   10335             if (isScrap()) {
   10336                 sb.append(" scrap ")
   10337                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
   10338             }
   10339             if (isInvalid()) sb.append(" invalid");
   10340             if (!isBound()) sb.append(" unbound");
   10341             if (needsUpdate()) sb.append(" update");
   10342             if (isRemoved()) sb.append(" removed");
   10343             if (shouldIgnore()) sb.append(" ignored");
   10344             if (isTmpDetached()) sb.append(" tmpDetached");
   10345             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
   10346             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
   10347 
   10348             if (itemView.getParent() == null) sb.append(" no parent");
   10349             sb.append("}");
   10350             return sb.toString();
   10351         }
   10352 
   10353         /**
   10354          * Informs the recycler whether this item can be recycled. Views which are not
   10355          * recyclable will not be reused for other items until setIsRecyclable() is
   10356          * later set to true. Calls to setIsRecyclable() should always be paired (one
   10357          * call to setIsRecyclabe(false) should always be matched with a later call to
   10358          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
   10359          * reference-counted.
   10360          *
   10361          * @param recyclable Whether this item is available to be recycled. Default value
   10362          * is true.
   10363          *
   10364          * @see #isRecyclable()
   10365          */
   10366         public final void setIsRecyclable(boolean recyclable) {
   10367             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
   10368             if (mIsRecyclableCount < 0) {
   10369                 mIsRecyclableCount = 0;
   10370                 if (DEBUG) {
   10371                     throw new RuntimeException("isRecyclable decremented below 0: "
   10372                             + "unmatched pair of setIsRecyable() calls for " + this);
   10373                 }
   10374                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
   10375                         + "unmatched pair of setIsRecyable() calls for " + this);
   10376             } else if (!recyclable && mIsRecyclableCount == 1) {
   10377                 mFlags |= FLAG_NOT_RECYCLABLE;
   10378             } else if (recyclable && mIsRecyclableCount == 0) {
   10379                 mFlags &= ~FLAG_NOT_RECYCLABLE;
   10380             }
   10381             if (DEBUG) {
   10382                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
   10383             }
   10384         }
   10385 
   10386         /**
   10387          * @return true if this item is available to be recycled, false otherwise.
   10388          *
   10389          * @see #setIsRecyclable(boolean)
   10390          */
   10391         public final boolean isRecyclable() {
   10392             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
   10393                     && !itemView.hasTransientState();
   10394         }
   10395 
   10396         /**
   10397          * Returns whether we have animations referring to this view holder or not.
   10398          * This is similar to isRecyclable flag but does not check transient state.
   10399          */
   10400         private boolean shouldBeKeptAsChild() {
   10401             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
   10402         }
   10403 
   10404         /**
   10405          * @return True if ViewHolder is not referenced by RecyclerView animations but has
   10406          * transient state which will prevent it from being recycled.
   10407          */
   10408         private boolean doesTransientStatePreventRecycling() {
   10409             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && itemView.hasTransientState();
   10410         }
   10411 
   10412         boolean isUpdated() {
   10413             return (mFlags & FLAG_UPDATE) != 0;
   10414         }
   10415     }
   10416 
   10417     /**
   10418      * This method is here so that we can control the important for a11y changes and test it.
   10419      */
   10420     @VisibleForTesting
   10421     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
   10422             int importantForAccessibility) {
   10423         if (isComputingLayout()) {
   10424             viewHolder.mPendingAccessibilityState = importantForAccessibility;
   10425             mPendingAccessibilityImportanceChange.add(viewHolder);
   10426             return false;
   10427         }
   10428         viewHolder.itemView.setImportantForAccessibility(importantForAccessibility);
   10429         return true;
   10430     }
   10431 
   10432     void dispatchPendingImportantForAccessibilityChanges() {
   10433         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
   10434             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
   10435             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
   10436                 continue;
   10437             }
   10438             int state = viewHolder.mPendingAccessibilityState;
   10439             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
   10440                 //noinspection WrongConstant
   10441                 viewHolder.itemView.setImportantForAccessibility(state);
   10442                 viewHolder.mPendingAccessibilityState =
   10443                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
   10444             }
   10445         }
   10446         mPendingAccessibilityImportanceChange.clear();
   10447     }
   10448 
   10449     int getAdapterPositionFor(ViewHolder viewHolder) {
   10450         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
   10451                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
   10452                 || !viewHolder.isBound()) {
   10453             return RecyclerView.NO_POSITION;
   10454         }
   10455         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
   10456     }
   10457 
   10458     /**
   10459      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
   10460      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
   10461      * to create their own subclass of this <code>LayoutParams</code> class
   10462      * to store any additional required per-child view metadata about the layout.
   10463      */
   10464     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
   10465         ViewHolder mViewHolder;
   10466         final Rect mDecorInsets = new Rect();
   10467         boolean mInsetsDirty = true;
   10468         // Flag is set to true if the view is bound while it is detached from RV.
   10469         // In this case, we need to manually call invalidate after view is added to guarantee that
   10470         // invalidation is populated through the View hierarchy
   10471         boolean mPendingInvalidate = false;
   10472 
   10473         public LayoutParams(Context c, AttributeSet attrs) {
   10474             super(c, attrs);
   10475         }
   10476 
   10477         public LayoutParams(int width, int height) {
   10478             super(width, height);
   10479         }
   10480 
   10481         public LayoutParams(MarginLayoutParams source) {
   10482             super(source);
   10483         }
   10484 
   10485         public LayoutParams(ViewGroup.LayoutParams source) {
   10486             super(source);
   10487         }
   10488 
   10489         public LayoutParams(LayoutParams source) {
   10490             super((ViewGroup.LayoutParams) source);
   10491         }
   10492 
   10493         /**
   10494          * Returns true if the view this LayoutParams is attached to needs to have its content
   10495          * updated from the corresponding adapter.
   10496          *
   10497          * @return true if the view should have its content updated
   10498          */
   10499         public boolean viewNeedsUpdate() {
   10500             return mViewHolder.needsUpdate();
   10501         }
   10502 
   10503         /**
   10504          * Returns true if the view this LayoutParams is attached to is now representing
   10505          * potentially invalid data. A LayoutManager should scrap/recycle it.
   10506          *
   10507          * @return true if the view is invalid
   10508          */
   10509         public boolean isViewInvalid() {
   10510             return mViewHolder.isInvalid();
   10511         }
   10512 
   10513         /**
   10514          * Returns true if the adapter data item corresponding to the view this LayoutParams
   10515          * is attached to has been removed from the data set. A LayoutManager may choose to
   10516          * treat it differently in order to animate its outgoing or disappearing state.
   10517          *
   10518          * @return true if the item the view corresponds to was removed from the data set
   10519          */
   10520         public boolean isItemRemoved() {
   10521             return mViewHolder.isRemoved();
   10522         }
   10523 
   10524         /**
   10525          * Returns true if the adapter data item corresponding to the view this LayoutParams
   10526          * is attached to has been changed in the data set. A LayoutManager may choose to
   10527          * treat it differently in order to animate its changing state.
   10528          *
   10529          * @return true if the item the view corresponds to was changed in the data set
   10530          */
   10531         public boolean isItemChanged() {
   10532             return mViewHolder.isUpdated();
   10533         }
   10534 
   10535         /**
   10536          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
   10537          */
   10538         @Deprecated
   10539         public int getViewPosition() {
   10540             return mViewHolder.getPosition();
   10541         }
   10542 
   10543         /**
   10544          * Returns the adapter position that the view this LayoutParams is attached to corresponds
   10545          * to as of latest layout calculation.
   10546          *
   10547          * @return the adapter position this view as of latest layout pass
   10548          */
   10549         public int getViewLayoutPosition() {
   10550             return mViewHolder.getLayoutPosition();
   10551         }
   10552 
   10553         /**
   10554          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
   10555          * corresponds to.
   10556          *
   10557          * @return the up-to-date adapter position this view. It may return
   10558          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
   10559          * its up-to-date position cannot be calculated.
   10560          */
   10561         public int getViewAdapterPosition() {
   10562             return mViewHolder.getAdapterPosition();
   10563         }
   10564     }
   10565 
   10566     /**
   10567      * Observer base class for watching changes to an {@link Adapter}.
   10568      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
   10569      */
   10570     public abstract static class AdapterDataObserver {
   10571         public void onChanged() {
   10572             // Do nothing
   10573         }
   10574 
   10575         public void onItemRangeChanged(int positionStart, int itemCount) {
   10576             // do nothing
   10577         }
   10578 
   10579         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   10580             // fallback to onItemRangeChanged(positionStart, itemCount) if app
   10581             // does not override this method.
   10582             onItemRangeChanged(positionStart, itemCount);
   10583         }
   10584 
   10585         public void onItemRangeInserted(int positionStart, int itemCount) {
   10586             // do nothing
   10587         }
   10588 
   10589         public void onItemRangeRemoved(int positionStart, int itemCount) {
   10590             // do nothing
   10591         }
   10592 
   10593         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   10594             // do nothing
   10595         }
   10596     }
   10597 
   10598     /**
   10599      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
   10600      * provides methods to trigger a programmatic scroll.</p>
   10601      *
   10602      * @see LinearSmoothScroller
   10603      */
   10604     public abstract static class SmoothScroller {
   10605 
   10606         private int mTargetPosition = RecyclerView.NO_POSITION;
   10607 
   10608         private RecyclerView mRecyclerView;
   10609 
   10610         private LayoutManager mLayoutManager;
   10611 
   10612         private boolean mPendingInitialRun;
   10613 
   10614         private boolean mRunning;
   10615 
   10616         private View mTargetView;
   10617 
   10618         private final Action mRecyclingAction;
   10619 
   10620         public SmoothScroller() {
   10621             mRecyclingAction = new Action(0, 0);
   10622         }
   10623 
   10624         /**
   10625          * Starts a smooth scroll for the given target position.
   10626          * <p>In each animation step, {@link RecyclerView} will check
   10627          * for the target view and call either
   10628          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   10629          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
   10630          * SmoothScroller is stopped.</p>
   10631          *
   10632          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
   10633          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
   10634          * stop calling SmoothScroller in each animation step.</p>
   10635          */
   10636         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
   10637             mRecyclerView = recyclerView;
   10638             mLayoutManager = layoutManager;
   10639             if (mTargetPosition == RecyclerView.NO_POSITION) {
   10640                 throw new IllegalArgumentException("Invalid target position");
   10641             }
   10642             mRecyclerView.mState.mTargetPosition = mTargetPosition;
   10643             mRunning = true;
   10644             mPendingInitialRun = true;
   10645             mTargetView = findViewByPosition(getTargetPosition());
   10646             onStart();
   10647             mRecyclerView.mViewFlinger.postOnAnimation();
   10648         }
   10649 
   10650         public void setTargetPosition(int targetPosition) {
   10651             mTargetPosition = targetPosition;
   10652         }
   10653 
   10654         /**
   10655          * @return The LayoutManager to which this SmoothScroller is attached. Will return
   10656          * <code>null</code> after the SmoothScroller is stopped.
   10657          */
   10658         @Nullable
   10659         public LayoutManager getLayoutManager() {
   10660             return mLayoutManager;
   10661         }
   10662 
   10663         /**
   10664          * Stops running the SmoothScroller in each animation callback. Note that this does not
   10665          * cancel any existing {@link Action} updated by
   10666          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   10667          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
   10668          */
   10669         protected final void stop() {
   10670             if (!mRunning) {
   10671                 return;
   10672             }
   10673             onStop();
   10674             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
   10675             mTargetView = null;
   10676             mTargetPosition = RecyclerView.NO_POSITION;
   10677             mPendingInitialRun = false;
   10678             mRunning = false;
   10679             // trigger a cleanup
   10680             mLayoutManager.onSmoothScrollerStopped(this);
   10681             // clear references to avoid any potential leak by a custom smooth scroller
   10682             mLayoutManager = null;
   10683             mRecyclerView = null;
   10684         }
   10685 
   10686         /**
   10687          * Returns true if SmoothScroller has been started but has not received the first
   10688          * animation
   10689          * callback yet.
   10690          *
   10691          * @return True if this SmoothScroller is waiting to start
   10692          */
   10693         public boolean isPendingInitialRun() {
   10694             return mPendingInitialRun;
   10695         }
   10696 
   10697 
   10698         /**
   10699          * @return True if SmoothScroller is currently active
   10700          */
   10701         public boolean isRunning() {
   10702             return mRunning;
   10703         }
   10704 
   10705         /**
   10706          * Returns the adapter position of the target item
   10707          *
   10708          * @return Adapter position of the target item or
   10709          * {@link RecyclerView#NO_POSITION} if no target view is set.
   10710          */
   10711         public int getTargetPosition() {
   10712             return mTargetPosition;
   10713         }
   10714 
   10715         private void onAnimation(int dx, int dy) {
   10716             final RecyclerView recyclerView = mRecyclerView;
   10717             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
   10718                 stop();
   10719             }
   10720             mPendingInitialRun = false;
   10721             if (mTargetView != null) {
   10722                 // verify target position
   10723                 if (getChildPosition(mTargetView) == mTargetPosition) {
   10724                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
   10725                     mRecyclingAction.runIfNecessary(recyclerView);
   10726                     stop();
   10727                 } else {
   10728                     Log.e(TAG, "Passed over target position while smooth scrolling.");
   10729                     mTargetView = null;
   10730                 }
   10731             }
   10732             if (mRunning) {
   10733                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
   10734                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
   10735                 mRecyclingAction.runIfNecessary(recyclerView);
   10736                 if (hadJumpTarget) {
   10737                     // It is not stopped so needs to be restarted
   10738                     if (mRunning) {
   10739                         mPendingInitialRun = true;
   10740                         recyclerView.mViewFlinger.postOnAnimation();
   10741                     } else {
   10742                         stop(); // done
   10743                     }
   10744                 }
   10745             }
   10746         }
   10747 
   10748         /**
   10749          * @see RecyclerView#getChildLayoutPosition(android.view.View)
   10750          */
   10751         public int getChildPosition(View view) {
   10752             return mRecyclerView.getChildLayoutPosition(view);
   10753         }
   10754 
   10755         /**
   10756          * @see RecyclerView.LayoutManager#getChildCount()
   10757          */
   10758         public int getChildCount() {
   10759             return mRecyclerView.mLayout.getChildCount();
   10760         }
   10761 
   10762         /**
   10763          * @see RecyclerView.LayoutManager#findViewByPosition(int)
   10764          */
   10765         public View findViewByPosition(int position) {
   10766             return mRecyclerView.mLayout.findViewByPosition(position);
   10767         }
   10768 
   10769         /**
   10770          * @see RecyclerView#scrollToPosition(int)
   10771          * @deprecated Use {@link Action#jumpTo(int)}.
   10772          */
   10773         @Deprecated
   10774         public void instantScrollToPosition(int position) {
   10775             mRecyclerView.scrollToPosition(position);
   10776         }
   10777 
   10778         protected void onChildAttachedToWindow(View child) {
   10779             if (getChildPosition(child) == getTargetPosition()) {
   10780                 mTargetView = child;
   10781                 if (DEBUG) {
   10782                     Log.d(TAG, "smooth scroll target view has been attached");
   10783                 }
   10784             }
   10785         }
   10786 
   10787         /**
   10788          * Normalizes the vector.
   10789          * @param scrollVector The vector that points to the target scroll position
   10790          */
   10791         protected void normalize(PointF scrollVector) {
   10792             final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
   10793                     * scrollVector.y);
   10794             scrollVector.x /= magnitude;
   10795             scrollVector.y /= magnitude;
   10796         }
   10797 
   10798         /**
   10799          * Called when smooth scroll is started. This might be a good time to do setup.
   10800          */
   10801         protected abstract void onStart();
   10802 
   10803         /**
   10804          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
   10805          * @see #stop()
   10806          */
   10807         protected abstract void onStop();
   10808 
   10809         /**
   10810          * <p>RecyclerView will call this method each time it scrolls until it can find the target
   10811          * position in the layout.</p>
   10812          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
   10813          * provided {@link Action} to define the next scroll.</p>
   10814          *
   10815          * @param dx        Last scroll amount horizontally
   10816          * @param dy        Last scroll amount vertically
   10817          * @param state     Transient state of RecyclerView
   10818          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
   10819          *                  update this object.
   10820          */
   10821         protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
   10822 
   10823         /**
   10824          * Called when the target position is laid out. This is the last callback SmoothScroller
   10825          * will receive and it should update the provided {@link Action} to define the scroll
   10826          * details towards the target view.
   10827          * @param targetView    The view element which render the target position.
   10828          * @param state         Transient state of RecyclerView
   10829          * @param action        Action instance that you should update to define final scroll action
   10830          *                      towards the targetView
   10831          */
   10832         protected abstract void onTargetFound(View targetView, State state, Action action);
   10833 
   10834         /**
   10835          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
   10836          */
   10837         public static class Action {
   10838 
   10839             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
   10840 
   10841             private int mDx;
   10842 
   10843             private int mDy;
   10844 
   10845             private int mDuration;
   10846 
   10847             private int mJumpToPosition = NO_POSITION;
   10848 
   10849             private Interpolator mInterpolator;
   10850 
   10851             private boolean mChanged = false;
   10852 
   10853             // we track this variable to inform custom implementer if they are updating the action
   10854             // in every animation callback
   10855             private int mConsecutiveUpdates = 0;
   10856 
   10857             /**
   10858              * @param dx Pixels to scroll horizontally
   10859              * @param dy Pixels to scroll vertically
   10860              */
   10861             public Action(int dx, int dy) {
   10862                 this(dx, dy, UNDEFINED_DURATION, null);
   10863             }
   10864 
   10865             /**
   10866              * @param dx       Pixels to scroll horizontally
   10867              * @param dy       Pixels to scroll vertically
   10868              * @param duration Duration of the animation in milliseconds
   10869              */
   10870             public Action(int dx, int dy, int duration) {
   10871                 this(dx, dy, 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              * @param interpolator Interpolator to be used when calculating scroll position in each
   10879              *                     animation step
   10880              */
   10881             public Action(int dx, int dy, int duration, Interpolator interpolator) {
   10882                 mDx = dx;
   10883                 mDy = dy;
   10884                 mDuration = duration;
   10885                 mInterpolator = interpolator;
   10886             }
   10887 
   10888             /**
   10889              * Instead of specifying pixels to scroll, use the target position to jump using
   10890              * {@link RecyclerView#scrollToPosition(int)}.
   10891              * <p>
   10892              * You may prefer using this method if scroll target is really far away and you prefer
   10893              * to jump to a location and smooth scroll afterwards.
   10894              * <p>
   10895              * Note that calling this method takes priority over other update methods such as
   10896              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
   10897              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
   10898              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
   10899              * frame.
   10900              *
   10901              * @param targetPosition The target item position to scroll to using instant scrolling.
   10902              */
   10903             public void jumpTo(int targetPosition) {
   10904                 mJumpToPosition = targetPosition;
   10905             }
   10906 
   10907             boolean hasJumpTarget() {
   10908                 return mJumpToPosition >= 0;
   10909             }
   10910 
   10911             void runIfNecessary(RecyclerView recyclerView) {
   10912                 if (mJumpToPosition >= 0) {
   10913                     final int position = mJumpToPosition;
   10914                     mJumpToPosition = NO_POSITION;
   10915                     recyclerView.jumpToPositionForSmoothScroller(position);
   10916                     mChanged = false;
   10917                     return;
   10918                 }
   10919                 if (mChanged) {
   10920                     validate();
   10921                     if (mInterpolator == null) {
   10922                         if (mDuration == UNDEFINED_DURATION) {
   10923                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
   10924                         } else {
   10925                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
   10926                         }
   10927                     } else {
   10928                         recyclerView.mViewFlinger.smoothScrollBy(
   10929                                 mDx, mDy, mDuration, mInterpolator);
   10930                     }
   10931                     mConsecutiveUpdates++;
   10932                     if (mConsecutiveUpdates > 10) {
   10933                         // A new action is being set in every animation step. This looks like a bad
   10934                         // implementation. Inform developer.
   10935                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
   10936                                 + " you are not changing it unless necessary");
   10937                     }
   10938                     mChanged = false;
   10939                 } else {
   10940                     mConsecutiveUpdates = 0;
   10941                 }
   10942             }
   10943 
   10944             private void validate() {
   10945                 if (mInterpolator != null && mDuration < 1) {
   10946                     throw new IllegalStateException("If you provide an interpolator, you must"
   10947                             + " set a positive duration");
   10948                 } else if (mDuration < 1) {
   10949                     throw new IllegalStateException("Scroll duration must be a positive number");
   10950                 }
   10951             }
   10952 
   10953             public int getDx() {
   10954                 return mDx;
   10955             }
   10956 
   10957             public void setDx(int dx) {
   10958                 mChanged = true;
   10959                 mDx = dx;
   10960             }
   10961 
   10962             public int getDy() {
   10963                 return mDy;
   10964             }
   10965 
   10966             public void setDy(int dy) {
   10967                 mChanged = true;
   10968                 mDy = dy;
   10969             }
   10970 
   10971             public int getDuration() {
   10972                 return mDuration;
   10973             }
   10974 
   10975             public void setDuration(int duration) {
   10976                 mChanged = true;
   10977                 mDuration = duration;
   10978             }
   10979 
   10980             public Interpolator getInterpolator() {
   10981                 return mInterpolator;
   10982             }
   10983 
   10984             /**
   10985              * Sets the interpolator to calculate scroll steps
   10986              * @param interpolator The interpolator to use. If you specify an interpolator, you must
   10987              *                     also set the duration.
   10988              * @see #setDuration(int)
   10989              */
   10990             public void setInterpolator(Interpolator interpolator) {
   10991                 mChanged = true;
   10992                 mInterpolator = interpolator;
   10993             }
   10994 
   10995             /**
   10996              * Updates the action with given parameters.
   10997              * @param dx Pixels to scroll horizontally
   10998              * @param dy Pixels to scroll vertically
   10999              * @param duration Duration of the animation in milliseconds
   11000              * @param interpolator Interpolator to be used when calculating scroll position in each
   11001              *                     animation step
   11002              */
   11003             public void update(int dx, int dy, int duration, Interpolator interpolator) {
   11004                 mDx = dx;
   11005                 mDy = dy;
   11006                 mDuration = duration;
   11007                 mInterpolator = interpolator;
   11008                 mChanged = true;
   11009             }
   11010         }
   11011 
   11012         /**
   11013          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
   11014          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
   11015          */
   11016         public interface ScrollVectorProvider {
   11017             /**
   11018              * Should calculate the vector that points to the direction where the target position
   11019              * can be found.
   11020              * <p>
   11021              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
   11022              * the target position.
   11023              * <p>
   11024              * The magnitude of the vector is not important. It is always normalized before being
   11025              * used by the {@link LinearSmoothScroller}.
   11026              * <p>
   11027              * LayoutManager should not check whether the position exists in the adapter or not.
   11028              *
   11029              * @param targetPosition the target position to which the returned vector should point
   11030              *
   11031              * @return the scroll vector for a given position.
   11032              */
   11033             PointF computeScrollVectorForPosition(int targetPosition);
   11034         }
   11035     }
   11036 
   11037     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
   11038         public boolean hasObservers() {
   11039             return !mObservers.isEmpty();
   11040         }
   11041 
   11042         public void notifyChanged() {
   11043             // since onChanged() is implemented by the app, it could do anything, including
   11044             // removing itself from {@link mObservers} - and that could cause problems if
   11045             // an iterator is used on the ArrayList {@link mObservers}.
   11046             // to avoid such problems, just march thru the list in the reverse order.
   11047             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11048                 mObservers.get(i).onChanged();
   11049             }
   11050         }
   11051 
   11052         public void notifyItemRangeChanged(int positionStart, int itemCount) {
   11053             notifyItemRangeChanged(positionStart, itemCount, null);
   11054         }
   11055 
   11056         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
   11057             // since onItemRangeChanged() is implemented by the app, it could do anything, including
   11058             // removing itself from {@link mObservers} - and that could cause problems if
   11059             // an iterator is used on the ArrayList {@link mObservers}.
   11060             // to avoid such problems, just march thru the list in the reverse order.
   11061             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11062                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
   11063             }
   11064         }
   11065 
   11066         public void notifyItemRangeInserted(int positionStart, int itemCount) {
   11067             // since onItemRangeInserted() is implemented by the app, it could do anything,
   11068             // including removing itself from {@link mObservers} - and that could cause problems if
   11069             // an iterator is used on the ArrayList {@link mObservers}.
   11070             // to avoid such problems, just march thru the list in the reverse order.
   11071             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11072                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
   11073             }
   11074         }
   11075 
   11076         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
   11077             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
   11078             // removing itself from {@link mObservers} - and that could cause problems if
   11079             // an iterator is used on the ArrayList {@link mObservers}.
   11080             // to avoid such problems, just march thru the list in the reverse order.
   11081             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11082                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
   11083             }
   11084         }
   11085 
   11086         public void notifyItemMoved(int fromPosition, int toPosition) {
   11087             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11088                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
   11089             }
   11090         }
   11091     }
   11092 
   11093     /**
   11094      * This is public so that the CREATOR can be access on cold launch.
   11095      * @hide
   11096      */
   11097     public static class SavedState extends AbsSavedState {
   11098 
   11099         Parcelable mLayoutState;
   11100 
   11101         /**
   11102          * called by CREATOR
   11103          */
   11104         SavedState(Parcel in) {
   11105             super(in);
   11106             mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
   11107         }
   11108 
   11109         /**
   11110          * Called by onSaveInstanceState
   11111          */
   11112         SavedState(Parcelable superState) {
   11113             super(superState);
   11114         }
   11115 
   11116         @Override
   11117         public void writeToParcel(Parcel dest, int flags) {
   11118             super.writeToParcel(dest, flags);
   11119             dest.writeParcelable(mLayoutState, 0);
   11120         }
   11121 
   11122         void copyFrom(SavedState other) {
   11123             mLayoutState = other.mLayoutState;
   11124         }
   11125 
   11126         public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
   11127                     @Override
   11128                     public SavedState createFromParcel(Parcel in) {
   11129                         return new SavedState(in);
   11130                     }
   11131 
   11132                     @Override
   11133                     public SavedState[] newArray(int size) {
   11134                         return new SavedState[size];
   11135                     }
   11136                 };
   11137     }
   11138     /**
   11139      * <p>Contains useful information about the current RecyclerView state like target scroll
   11140      * position or view focus. State object can also keep arbitrary data, identified by resource
   11141      * ids.</p>
   11142      * <p>Often times, RecyclerView components will need to pass information between each other.
   11143      * To provide a well defined data bus between components, RecyclerView passes the same State
   11144      * object to component callbacks and these components can use it to exchange data.</p>
   11145      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
   11146      * data between your components without needing to manage their lifecycles.</p>
   11147      */
   11148     public static class State {
   11149         static final int STEP_START = 1;
   11150         static final int STEP_LAYOUT = 1 << 1;
   11151         static final int STEP_ANIMATIONS = 1 << 2;
   11152 
   11153         void assertLayoutStep(int accepted) {
   11154             if ((accepted & mLayoutStep) == 0) {
   11155                 throw new IllegalStateException("Layout state should be one of "
   11156                         + Integer.toBinaryString(accepted) + " but it is "
   11157                         + Integer.toBinaryString(mLayoutStep));
   11158             }
   11159         }
   11160 
   11161 
   11162         /** Owned by SmoothScroller */
   11163         private int mTargetPosition = RecyclerView.NO_POSITION;
   11164 
   11165         private SparseArray<Object> mData;
   11166 
   11167         ////////////////////////////////////////////////////////////////////////////////////////////
   11168         // Fields below are carried from one layout pass to the next
   11169         ////////////////////////////////////////////////////////////////////////////////////////////
   11170 
   11171         /**
   11172          * Number of items adapter had in the previous layout.
   11173          */
   11174         int mPreviousLayoutItemCount = 0;
   11175 
   11176         /**
   11177          * Number of items that were NOT laid out but has been deleted from the adapter after the
   11178          * previous layout.
   11179          */
   11180         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
   11181 
   11182         ////////////////////////////////////////////////////////////////////////////////////////////
   11183         // Fields below must be updated or cleared before they are used (generally before a pass)
   11184         ////////////////////////////////////////////////////////////////////////////////////////////
   11185 
   11186         @IntDef(flag = true, value = {
   11187                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
   11188         })
   11189         @Retention(RetentionPolicy.SOURCE)
   11190         @interface LayoutState {}
   11191 
   11192         @LayoutState
   11193         int mLayoutStep = STEP_START;
   11194 
   11195         /**
   11196          * Number of items adapter has.
   11197          */
   11198         int mItemCount = 0;
   11199 
   11200         boolean mStructureChanged = false;
   11201 
   11202         boolean mInPreLayout = false;
   11203 
   11204         boolean mTrackOldChangeHolders = false;
   11205 
   11206         boolean mIsMeasuring = false;
   11207 
   11208         ////////////////////////////////////////////////////////////////////////////////////////////
   11209         // Fields below are always reset outside of the pass (or passes) that use them
   11210         ////////////////////////////////////////////////////////////////////////////////////////////
   11211 
   11212         boolean mRunSimpleAnimations = false;
   11213 
   11214         boolean mRunPredictiveAnimations = false;
   11215 
   11216         /**
   11217          * This data is saved before a layout calculation happens. After the layout is finished,
   11218          * if the previously focused view has been replaced with another view for the same item, we
   11219          * move the focus to the new item automatically.
   11220          */
   11221         int mFocusedItemPosition;
   11222         long mFocusedItemId;
   11223         // when a sub child has focus, record its id and see if we can directly request focus on
   11224         // that one instead
   11225         int mFocusedSubChildId;
   11226 
   11227         ////////////////////////////////////////////////////////////////////////////////////////////
   11228 
   11229         State reset() {
   11230             mTargetPosition = RecyclerView.NO_POSITION;
   11231             if (mData != null) {
   11232                 mData.clear();
   11233             }
   11234             mItemCount = 0;
   11235             mStructureChanged = false;
   11236             mIsMeasuring = false;
   11237             return this;
   11238         }
   11239 
   11240         /**
   11241          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
   11242          * prior to any layout passes.
   11243          *
   11244          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
   11245          * that Recycler#getViewForPosition() can function safely.</p>
   11246          */
   11247         void prepareForNestedPrefetch(Adapter adapter) {
   11248             mLayoutStep = STEP_START;
   11249             mItemCount = adapter.getItemCount();
   11250             mStructureChanged = false;
   11251             mInPreLayout = false;
   11252             mTrackOldChangeHolders = false;
   11253             mIsMeasuring = false;
   11254         }
   11255 
   11256         /**
   11257          * Returns true if the RecyclerView is currently measuring the layout. This value is
   11258          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
   11259          * has non-exact measurement specs.
   11260          * <p>
   11261          * Note that if the LayoutManager supports predictive animations and it is calculating the
   11262          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
   11263          * {@code onMeasure} call. This is because pre-layout means the previous state of the
   11264          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
   11265          * LayoutManager is always guaranteed to receive another call to
   11266          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
   11267          *
   11268          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
   11269          */
   11270         public boolean isMeasuring() {
   11271             return mIsMeasuring;
   11272         }
   11273 
   11274         /**
   11275          * Returns true if
   11276          * @return
   11277          */
   11278         public boolean isPreLayout() {
   11279             return mInPreLayout;
   11280         }
   11281 
   11282         /**
   11283          * Returns whether RecyclerView will run predictive animations in this layout pass
   11284          * or not.
   11285          *
   11286          * @return true if RecyclerView is calculating predictive animations to be run at the end
   11287          *         of the layout pass.
   11288          */
   11289         public boolean willRunPredictiveAnimations() {
   11290             return mRunPredictiveAnimations;
   11291         }
   11292 
   11293         /**
   11294          * Returns whether RecyclerView will run simple animations in this layout pass
   11295          * or not.
   11296          *
   11297          * @return true if RecyclerView is calculating simple animations to be run at the end of
   11298          *         the layout pass.
   11299          */
   11300         public boolean willRunSimpleAnimations() {
   11301             return mRunSimpleAnimations;
   11302         }
   11303 
   11304         /**
   11305          * Removes the mapping from the specified id, if there was any.
   11306          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
   11307          *                   preserve cross functionality and avoid conflicts.
   11308          */
   11309         public void remove(int resourceId) {
   11310             if (mData == null) {
   11311                 return;
   11312             }
   11313             mData.remove(resourceId);
   11314         }
   11315 
   11316         /**
   11317          * Gets the Object mapped from the specified id, or <code>null</code>
   11318          * if no such data exists.
   11319          *
   11320          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
   11321          *                   to
   11322          *                   preserve cross functionality and avoid conflicts.
   11323          */
   11324         public <T> T get(int resourceId) {
   11325             if (mData == null) {
   11326                 return null;
   11327             }
   11328             return (T) mData.get(resourceId);
   11329         }
   11330 
   11331         /**
   11332          * Adds a mapping from the specified id to the specified value, replacing the previous
   11333          * mapping from the specified key if there was one.
   11334          *
   11335          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
   11336          *                   preserve cross functionality and avoid conflicts.
   11337          * @param data       The data you want to associate with the resourceId.
   11338          */
   11339         public void put(int resourceId, Object data) {
   11340             if (mData == null) {
   11341                 mData = new SparseArray<Object>();
   11342             }
   11343             mData.put(resourceId, data);
   11344         }
   11345 
   11346         /**
   11347          * If scroll is triggered to make a certain item visible, this value will return the
   11348          * adapter index of that item.
   11349          * @return Adapter index of the target item or
   11350          * {@link RecyclerView#NO_POSITION} if there is no target
   11351          * position.
   11352          */
   11353         public int getTargetScrollPosition() {
   11354             return mTargetPosition;
   11355         }
   11356 
   11357         /**
   11358          * Returns if current scroll has a target position.
   11359          * @return true if scroll is being triggered to make a certain position visible
   11360          * @see #getTargetScrollPosition()
   11361          */
   11362         public boolean hasTargetScrollPosition() {
   11363             return mTargetPosition != RecyclerView.NO_POSITION;
   11364         }
   11365 
   11366         /**
   11367          * @return true if the structure of the data set has changed since the last call to
   11368          *         onLayoutChildren, false otherwise
   11369          */
   11370         public boolean didStructureChange() {
   11371             return mStructureChanged;
   11372         }
   11373 
   11374         /**
   11375          * Returns the total number of items that can be laid out. Note that this number is not
   11376          * necessarily equal to the number of items in the adapter, so you should always use this
   11377          * number for your position calculations and never access the adapter directly.
   11378          * <p>
   11379          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
   11380          * data changes on existing Views. These calculations are used to decide which animations
   11381          * should be run.
   11382          * <p>
   11383          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
   11384          * present the correct state to LayoutManager in pre-layout pass.
   11385          * <p>
   11386          * For example, a newly added item is not included in pre-layout item count because
   11387          * pre-layout reflects the contents of the adapter before the item is added. Behind the
   11388          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
   11389          * LayoutManager does not know about the new item's existence in pre-layout. The item will
   11390          * be available in second layout pass and will be included in the item count. Similar
   11391          * adjustments are made for moved and removed items as well.
   11392          * <p>
   11393          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
   11394          *
   11395          * @return The number of items currently available
   11396          * @see LayoutManager#getItemCount()
   11397          */
   11398         public int getItemCount() {
   11399             return mInPreLayout
   11400                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
   11401                     : mItemCount;
   11402         }
   11403 
   11404         @Override
   11405         public String toString() {
   11406             return "State{"
   11407                     + "mTargetPosition=" + mTargetPosition
   11408                     + ", mData=" + mData
   11409                     + ", mItemCount=" + mItemCount
   11410                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
   11411                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
   11412                     + mDeletedInvisibleItemCountSincePreviousLayout
   11413                     + ", mStructureChanged=" + mStructureChanged
   11414                     + ", mInPreLayout=" + mInPreLayout
   11415                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
   11416                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
   11417                     + '}';
   11418         }
   11419     }
   11420 
   11421     /**
   11422      * This class defines the behavior of fling if the developer wishes to handle it.
   11423      * <p>
   11424      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
   11425      *
   11426      * @see #setOnFlingListener(OnFlingListener)
   11427      */
   11428     public abstract static class OnFlingListener {
   11429 
   11430         /**
   11431          * Override this to handle a fling given the velocities in both x and y directions.
   11432          * Note that this method will only be called if the associated {@link LayoutManager}
   11433          * supports scrolling and the fling is not handled by nested scrolls first.
   11434          *
   11435          * @param velocityX the fling velocity on the X axis
   11436          * @param velocityY the fling velocity on the Y axis
   11437          *
   11438          * @return true if the fling washandled, false otherwise.
   11439          */
   11440         public abstract boolean onFling(int velocityX, int velocityY);
   11441     }
   11442 
   11443     /**
   11444      * Internal listener that manages items after animations finish. This is how items are
   11445      * retained (not recycled) during animations, but allowed to be recycled afterwards.
   11446      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
   11447      * method on the animator's listener when it is done animating any item.
   11448      */
   11449     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
   11450 
   11451         ItemAnimatorRestoreListener() {
   11452         }
   11453 
   11454         @Override
   11455         public void onAnimationFinished(ViewHolder item) {
   11456             item.setIsRecyclable(true);
   11457             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
   11458                 item.mShadowedHolder = null;
   11459             }
   11460             // always null this because an OldViewHolder can never become NewViewHolder w/o being
   11461             // recycled.
   11462             item.mShadowingHolder = null;
   11463             if (!item.shouldBeKeptAsChild()) {
   11464                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
   11465                     removeDetachedView(item.itemView, false);
   11466                 }
   11467             }
   11468         }
   11469     }
   11470 
   11471     /**
   11472      * This class defines the animations that take place on items as changes are made
   11473      * to the adapter.
   11474      *
   11475      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
   11476      * ViewHolder items. The RecyclerView will manage retaining these items while they
   11477      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
   11478      * when a ViewHolder's animation is finished. In other words, there must be a matching
   11479      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
   11480      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
   11481      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11482      * animateChange()}
   11483      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
   11484      * and
   11485      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11486      * animateDisappearance()} call.
   11487      *
   11488      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
   11489      *
   11490      * @see #setItemAnimator(ItemAnimator)
   11491      */
   11492     @SuppressWarnings("UnusedParameters")
   11493     public abstract static class ItemAnimator {
   11494 
   11495         /**
   11496          * The Item represented by this ViewHolder is updated.
   11497          * <p>
   11498          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11499          */
   11500         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
   11501 
   11502         /**
   11503          * The Item represented by this ViewHolder is removed from the adapter.
   11504          * <p>
   11505          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11506          */
   11507         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
   11508 
   11509         /**
   11510          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
   11511          * represented by this ViewHolder is invalid.
   11512          * <p>
   11513          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11514          */
   11515         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
   11516 
   11517         /**
   11518          * The position of the Item represented by this ViewHolder has been changed. This flag is
   11519          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
   11520          * any adapter change that may have a side effect on this item. (e.g. The item before this
   11521          * one has been removed from the Adapter).
   11522          * <p>
   11523          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11524          */
   11525         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
   11526 
   11527         /**
   11528          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
   11529          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
   11530          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
   11531          * to add new items in pre-layout to specify their virtual location when they are invisible
   11532          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
   11533          * <p>
   11534          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11535          */
   11536         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
   11537                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
   11538 
   11539         /**
   11540          * The set of flags that might be passed to
   11541          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11542          */
   11543         @IntDef(flag = true, value = {
   11544                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
   11545                 FLAG_APPEARED_IN_PRE_LAYOUT
   11546         })
   11547         @Retention(RetentionPolicy.SOURCE)
   11548         public @interface AdapterChanges {}
   11549         private ItemAnimatorListener mListener = null;
   11550         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
   11551                 new ArrayList<ItemAnimatorFinishedListener>();
   11552 
   11553         private long mAddDuration = 120;
   11554         private long mRemoveDuration = 120;
   11555         private long mMoveDuration = 250;
   11556         private long mChangeDuration = 250;
   11557 
   11558         /**
   11559          * Gets the current duration for which all move animations will run.
   11560          *
   11561          * @return The current move duration
   11562          */
   11563         public long getMoveDuration() {
   11564             return mMoveDuration;
   11565         }
   11566 
   11567         /**
   11568          * Sets the duration for which all move animations will run.
   11569          *
   11570          * @param moveDuration The move duration
   11571          */
   11572         public void setMoveDuration(long moveDuration) {
   11573             mMoveDuration = moveDuration;
   11574         }
   11575 
   11576         /**
   11577          * Gets the current duration for which all add animations will run.
   11578          *
   11579          * @return The current add duration
   11580          */
   11581         public long getAddDuration() {
   11582             return mAddDuration;
   11583         }
   11584 
   11585         /**
   11586          * Sets the duration for which all add animations will run.
   11587          *
   11588          * @param addDuration The add duration
   11589          */
   11590         public void setAddDuration(long addDuration) {
   11591             mAddDuration = addDuration;
   11592         }
   11593 
   11594         /**
   11595          * Gets the current duration for which all remove animations will run.
   11596          *
   11597          * @return The current remove duration
   11598          */
   11599         public long getRemoveDuration() {
   11600             return mRemoveDuration;
   11601         }
   11602 
   11603         /**
   11604          * Sets the duration for which all remove animations will run.
   11605          *
   11606          * @param removeDuration The remove duration
   11607          */
   11608         public void setRemoveDuration(long removeDuration) {
   11609             mRemoveDuration = removeDuration;
   11610         }
   11611 
   11612         /**
   11613          * Gets the current duration for which all change animations will run.
   11614          *
   11615          * @return The current change duration
   11616          */
   11617         public long getChangeDuration() {
   11618             return mChangeDuration;
   11619         }
   11620 
   11621         /**
   11622          * Sets the duration for which all change animations will run.
   11623          *
   11624          * @param changeDuration The change duration
   11625          */
   11626         public void setChangeDuration(long changeDuration) {
   11627             mChangeDuration = changeDuration;
   11628         }
   11629 
   11630         /**
   11631          * Internal only:
   11632          * Sets the listener that must be called when the animator is finished
   11633          * animating the item (or immediately if no animation happens). This is set
   11634          * internally and is not intended to be set by external code.
   11635          *
   11636          * @param listener The listener that must be called.
   11637          */
   11638         void setListener(ItemAnimatorListener listener) {
   11639             mListener = listener;
   11640         }
   11641 
   11642         /**
   11643          * Called by the RecyclerView before the layout begins. Item animator should record
   11644          * necessary information about the View before it is potentially rebound, moved or removed.
   11645          * <p>
   11646          * The data returned from this method will be passed to the related <code>animate**</code>
   11647          * methods.
   11648          * <p>
   11649          * Note that this method may be called after pre-layout phase if LayoutManager adds new
   11650          * Views to the layout in pre-layout pass.
   11651          * <p>
   11652          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
   11653          * the View and the adapter change flags.
   11654          *
   11655          * @param state       The current State of RecyclerView which includes some useful data
   11656          *                    about the layout that will be calculated.
   11657          * @param viewHolder  The ViewHolder whose information should be recorded.
   11658          * @param changeFlags Additional information about what changes happened in the Adapter
   11659          *                    about the Item represented by this ViewHolder. For instance, if
   11660          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
   11661          * @param payloads    The payload list that was previously passed to
   11662          *                    {@link Adapter#notifyItemChanged(int, Object)} or
   11663          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
   11664          *
   11665          * @return An ItemHolderInfo instance that preserves necessary information about the
   11666          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
   11667          * after layout is complete.
   11668          *
   11669          * @see #recordPostLayoutInformation(State, ViewHolder)
   11670          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11671          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11672          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11673          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11674          */
   11675         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
   11676                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
   11677                 @NonNull List<Object> payloads) {
   11678             return obtainHolderInfo().setFrom(viewHolder);
   11679         }
   11680 
   11681         /**
   11682          * Called by the RecyclerView after the layout is complete. Item animator should record
   11683          * necessary information about the View's final state.
   11684          * <p>
   11685          * The data returned from this method will be passed to the related <code>animate**</code>
   11686          * methods.
   11687          * <p>
   11688          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
   11689          * the View.
   11690          *
   11691          * @param state      The current State of RecyclerView which includes some useful data about
   11692          *                   the layout that will be calculated.
   11693          * @param viewHolder The ViewHolder whose information should be recorded.
   11694          *
   11695          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
   11696          * This object will be passed back to related <code>animate**</code> methods when
   11697          * RecyclerView decides how items should be animated.
   11698          *
   11699          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11700          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11701          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11702          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11703          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11704          */
   11705         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
   11706                 @NonNull ViewHolder viewHolder) {
   11707             return obtainHolderInfo().setFrom(viewHolder);
   11708         }
   11709 
   11710         /**
   11711          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
   11712          * <p>
   11713          * This means that the View was a child of the LayoutManager when layout started but has
   11714          * been removed by the LayoutManager. It might have been removed from the adapter or simply
   11715          * become invisible due to other factors. You can distinguish these two cases by checking
   11716          * the change flags that were passed to
   11717          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11718          * <p>
   11719          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
   11720          * animation callback method which will be called by the RecyclerView depends on the
   11721          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
   11722          * LayoutManager's decision whether to layout the changed version of a disappearing
   11723          * ViewHolder or not. RecyclerView will call
   11724          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11725          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
   11726          * returns {@code false} from
   11727          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
   11728          * LayoutManager lays out a new disappearing view that holds the updated information.
   11729          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
   11730          * <p>
   11731          * If LayoutManager supports predictive animations, it might provide a target disappear
   11732          * location for the View by laying it out in that location. When that happens,
   11733          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
   11734          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
   11735          * <p>
   11736          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   11737          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   11738          * decides not to animate the view).
   11739          *
   11740          * @param viewHolder    The ViewHolder which should be animated
   11741          * @param preLayoutInfo The information that was returned from
   11742          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11743          * @param postLayoutInfo The information that was returned from
   11744          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
   11745          *                       null if the LayoutManager did not layout the item.
   11746          *
   11747          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11748          * false otherwise.
   11749          */
   11750         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
   11751                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
   11752 
   11753         /**
   11754          * Called by the RecyclerView when a ViewHolder is added to the layout.
   11755          * <p>
   11756          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
   11757          * but has  been added by the LayoutManager. It might be newly added to the adapter or
   11758          * simply become visible due to other factors.
   11759          * <p>
   11760          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   11761          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   11762          * decides not to animate the view).
   11763          *
   11764          * @param viewHolder     The ViewHolder which should be animated
   11765          * @param preLayoutInfo  The information that was returned from
   11766          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11767          *                       Might be null if Item was just added to the adapter or
   11768          *                       LayoutManager does not support predictive animations or it could
   11769          *                       not predict that this ViewHolder will become visible.
   11770          * @param postLayoutInfo The information that was returned from {@link
   11771          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11772          *
   11773          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11774          * false otherwise.
   11775          */
   11776         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
   11777                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   11778 
   11779         /**
   11780          * Called by the RecyclerView when a ViewHolder is present in both before and after the
   11781          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
   11782          * for it or a {@link Adapter#notifyDataSetChanged()} call.
   11783          * <p>
   11784          * This ViewHolder still represents the same data that it was representing when the layout
   11785          * started but its position / size may be changed by the LayoutManager.
   11786          * <p>
   11787          * If the Item's layout position didn't change, RecyclerView still calls this method because
   11788          * it does not track this information (or does not necessarily know that an animation is
   11789          * not required). Your ItemAnimator should handle this case and if there is nothing to
   11790          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
   11791          * <code>false</code>.
   11792          * <p>
   11793          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   11794          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   11795          * decides not to animate the view).
   11796          *
   11797          * @param viewHolder     The ViewHolder which should be animated
   11798          * @param preLayoutInfo  The information that was returned from
   11799          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11800          * @param postLayoutInfo The information that was returned from {@link
   11801          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11802          *
   11803          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11804          * false otherwise.
   11805          */
   11806         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
   11807                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   11808 
   11809         /**
   11810          * Called by the RecyclerView when an adapter item is present both before and after the
   11811          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
   11812          * for it. This method may also be called when
   11813          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
   11814          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
   11815          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
   11816          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
   11817          * called for the new ViewHolder and the old one will be recycled.
   11818          * <p>
   11819          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
   11820          * a good possibility that item contents didn't really change but it is rebound from the
   11821          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
   11822          * screen didn't change and your animator should handle this case as well and avoid creating
   11823          * unnecessary animations.
   11824          * <p>
   11825          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
   11826          * previous presentation of the item as-is and supply a new ViewHolder for the updated
   11827          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
   11828          * This is useful if you don't know the contents of the Item and would like
   11829          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
   11830          * <p>
   11831          * When you are writing a custom item animator for your layout, it might be more performant
   11832          * and elegant to re-use the same ViewHolder and animate the content changes manually.
   11833          * <p>
   11834          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
   11835          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
   11836          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
   11837          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
   11838          * which represent the same Item. In that case, only the new ViewHolder is visible
   11839          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
   11840          * <p>
   11841          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
   11842          * ViewHolder when their animation is complete
   11843          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
   11844          * animate the view).
   11845          * <p>
   11846          *  If oldHolder and newHolder are the same instance, you should call
   11847          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
   11848          * <p>
   11849          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
   11850          * animation callback method which will be called by the RecyclerView depends on the
   11851          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
   11852          * LayoutManager's decision whether to layout the changed version of a disappearing
   11853          * ViewHolder or not. RecyclerView will call
   11854          * {@code animateChange} instead of
   11855          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11856          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
   11857          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
   11858          * LayoutManager lays out a new disappearing view that holds the updated information.
   11859          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
   11860          *
   11861          * @param oldHolder     The ViewHolder before the layout is started, might be the same
   11862          *                      instance with newHolder.
   11863          * @param newHolder     The ViewHolder after the layout is finished, might be the same
   11864          *                      instance with oldHolder.
   11865          * @param preLayoutInfo  The information that was returned from
   11866          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11867          * @param postLayoutInfo The information that was returned from {@link
   11868          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11869          *
   11870          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11871          * false otherwise.
   11872          */
   11873         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
   11874                 @NonNull ViewHolder newHolder,
   11875                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   11876 
   11877         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
   11878             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
   11879             if (viewHolder.isInvalid()) {
   11880                 return FLAG_INVALIDATED;
   11881             }
   11882             if ((flags & FLAG_INVALIDATED) == 0) {
   11883                 final int oldPos = viewHolder.getOldPosition();
   11884                 final int pos = viewHolder.getAdapterPosition();
   11885                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
   11886                     flags |= FLAG_MOVED;
   11887                 }
   11888             }
   11889             return flags;
   11890         }
   11891 
   11892         /**
   11893          * Called when there are pending animations waiting to be started. This state
   11894          * is governed by the return values from
   11895          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11896          * animateAppearance()},
   11897          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11898          * animateChange()}
   11899          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11900          * animatePersistence()}, and
   11901          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11902          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
   11903          * called later to start the associated animations. runPendingAnimations() will be scheduled
   11904          * to be run on the next frame.
   11905          */
   11906         public abstract void runPendingAnimations();
   11907 
   11908         /**
   11909          * Method called when an animation on a view should be ended immediately.
   11910          * This could happen when other events, like scrolling, occur, so that
   11911          * animating views can be quickly put into their proper end locations.
   11912          * Implementations should ensure that any animations running on the item
   11913          * are canceled and affected properties are set to their end values.
   11914          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
   11915          * animation since the animations are effectively done when this method is called.
   11916          *
   11917          * @param item The item for which an animation should be stopped.
   11918          */
   11919         public abstract void endAnimation(ViewHolder item);
   11920 
   11921         /**
   11922          * Method called when all item animations should be ended immediately.
   11923          * This could happen when other events, like scrolling, occur, so that
   11924          * animating views can be quickly put into their proper end locations.
   11925          * Implementations should ensure that any animations running on any items
   11926          * are canceled and affected properties are set to their end values.
   11927          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
   11928          * animation since the animations are effectively done when this method is called.
   11929          */
   11930         public abstract void endAnimations();
   11931 
   11932         /**
   11933          * Method which returns whether there are any item animations currently running.
   11934          * This method can be used to determine whether to delay other actions until
   11935          * animations end.
   11936          *
   11937          * @return true if there are any item animations currently running, false otherwise.
   11938          */
   11939         public abstract boolean isRunning();
   11940 
   11941         /**
   11942          * Method to be called by subclasses when an animation is finished.
   11943          * <p>
   11944          * For each call RecyclerView makes to
   11945          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11946          * animateAppearance()},
   11947          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11948          * animatePersistence()}, or
   11949          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11950          * animateDisappearance()}, there
   11951          * should
   11952          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
   11953          * <p>
   11954          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11955          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
   11956          * and <code>newHolder</code>  (if they are not the same instance).
   11957          *
   11958          * @param viewHolder The ViewHolder whose animation is finished.
   11959          * @see #onAnimationFinished(ViewHolder)
   11960          */
   11961         public final void dispatchAnimationFinished(ViewHolder viewHolder) {
   11962             onAnimationFinished(viewHolder);
   11963             if (mListener != null) {
   11964                 mListener.onAnimationFinished(viewHolder);
   11965             }
   11966         }
   11967 
   11968         /**
   11969          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
   11970          * ItemAnimator.
   11971          *
   11972          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
   11973          *                   animations running on this ViewHolder.
   11974          * @see #dispatchAnimationFinished(ViewHolder)
   11975          */
   11976         public void onAnimationFinished(ViewHolder viewHolder) {
   11977         }
   11978 
   11979         /**
   11980          * Method to be called by subclasses when an animation is started.
   11981          * <p>
   11982          * For each call RecyclerView makes to
   11983          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11984          * animateAppearance()},
   11985          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11986          * animatePersistence()}, or
   11987          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11988          * animateDisappearance()}, there should be a matching
   11989          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
   11990          * <p>
   11991          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11992          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
   11993          * and <code>newHolder</code> (if they are not the same instance).
   11994          * <p>
   11995          * If your ItemAnimator decides not to animate a ViewHolder, it should call
   11996          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
   11997          * {@link #dispatchAnimationStarted(ViewHolder)}.
   11998          *
   11999          * @param viewHolder The ViewHolder whose animation is starting.
   12000          * @see #onAnimationStarted(ViewHolder)
   12001          */
   12002         public final void dispatchAnimationStarted(ViewHolder viewHolder) {
   12003             onAnimationStarted(viewHolder);
   12004         }
   12005 
   12006         /**
   12007          * Called when a new animation is started on the given ViewHolder.
   12008          *
   12009          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
   12010          *                   might already be animating and this might be another animation.
   12011          * @see #dispatchAnimationStarted(ViewHolder)
   12012          */
   12013         public void onAnimationStarted(ViewHolder viewHolder) {
   12014 
   12015         }
   12016 
   12017         /**
   12018          * Like {@link #isRunning()}, this method returns whether there are any item
   12019          * animations currently running. Additionally, the listener passed in will be called
   12020          * when there are no item animations running, either immediately (before the method
   12021          * returns) if no animations are currently running, or when the currently running
   12022          * animations are {@link #dispatchAnimationsFinished() finished}.
   12023          *
   12024          * <p>Note that the listener is transient - it is either called immediately and not
   12025          * stored at all, or stored only until it is called when running animations
   12026          * are finished sometime later.</p>
   12027          *
   12028          * @param listener A listener to be called immediately if no animations are running
   12029          * or later when currently-running animations have finished. A null listener is
   12030          * equivalent to calling {@link #isRunning()}.
   12031          * @return true if there are any item animations currently running, false otherwise.
   12032          */
   12033         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
   12034             boolean running = isRunning();
   12035             if (listener != null) {
   12036                 if (!running) {
   12037                     listener.onAnimationsFinished();
   12038                 } else {
   12039                     mFinishedListeners.add(listener);
   12040                 }
   12041             }
   12042             return running;
   12043         }
   12044 
   12045         /**
   12046          * When an item is changed, ItemAnimator can decide whether it wants to re-use
   12047          * the same ViewHolder for animations or RecyclerView should create a copy of the
   12048          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
   12049          * <p>
   12050          * Note that this method will only be called if the {@link ViewHolder} still has the same
   12051          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
   12052          * both {@link ViewHolder}s in the
   12053          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
   12054          * <p>
   12055          * If your application is using change payloads, you can override
   12056          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
   12057          *
   12058          * @param viewHolder The ViewHolder which represents the changed item's old content.
   12059          *
   12060          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
   12061          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
   12062          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
   12063          *
   12064          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
   12065          */
   12066         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
   12067             return true;
   12068         }
   12069 
   12070         /**
   12071          * When an item is changed, ItemAnimator can decide whether it wants to re-use
   12072          * the same ViewHolder for animations or RecyclerView should create a copy of the
   12073          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
   12074          * <p>
   12075          * Note that this method will only be called if the {@link ViewHolder} still has the same
   12076          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
   12077          * both {@link ViewHolder}s in the
   12078          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
   12079          *
   12080          * @param viewHolder The ViewHolder which represents the changed item's old content.
   12081          * @param payloads A non-null list of merged payloads that were sent with change
   12082          *                 notifications. Can be empty if the adapter is invalidated via
   12083          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
   12084          *                 payloads will be passed into
   12085          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
   12086          *                 method <b>if</b> this method returns <code>true</code>.
   12087          *
   12088          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
   12089          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
   12090          *         ItemAnimator to animate. Default implementation calls
   12091          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
   12092          *
   12093          * @see #canReuseUpdatedViewHolder(ViewHolder)
   12094          */
   12095         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
   12096                 @NonNull List<Object> payloads) {
   12097             return canReuseUpdatedViewHolder(viewHolder);
   12098         }
   12099 
   12100         /**
   12101          * This method should be called by ItemAnimator implementations to notify
   12102          * any listeners that all pending and active item animations are finished.
   12103          */
   12104         public final void dispatchAnimationsFinished() {
   12105             final int count = mFinishedListeners.size();
   12106             for (int i = 0; i < count; ++i) {
   12107                 mFinishedListeners.get(i).onAnimationsFinished();
   12108             }
   12109             mFinishedListeners.clear();
   12110         }
   12111 
   12112         /**
   12113          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
   12114          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
   12115          * <p>
   12116          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
   12117          * your own instances.
   12118          *
   12119          * @return A new {@link ItemHolderInfo}.
   12120          */
   12121         public ItemHolderInfo obtainHolderInfo() {
   12122             return new ItemHolderInfo();
   12123         }
   12124 
   12125         /**
   12126          * The interface to be implemented by listeners to animation events from this
   12127          * ItemAnimator. This is used internally and is not intended for developers to
   12128          * create directly.
   12129          */
   12130         interface ItemAnimatorListener {
   12131             void onAnimationFinished(ViewHolder item);
   12132         }
   12133 
   12134         /**
   12135          * This interface is used to inform listeners when all pending or running animations
   12136          * in an ItemAnimator are finished. This can be used, for example, to delay an action
   12137          * in a data set until currently-running animations are complete.
   12138          *
   12139          * @see #isRunning(ItemAnimatorFinishedListener)
   12140          */
   12141         public interface ItemAnimatorFinishedListener {
   12142             /**
   12143              * Notifies when all pending or running animations in an ItemAnimator are finished.
   12144              */
   12145             void onAnimationsFinished();
   12146         }
   12147 
   12148         /**
   12149          * A simple data structure that holds information about an item's bounds.
   12150          * This information is used in calculating item animations. Default implementation of
   12151          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
   12152          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
   12153          * structure. You can extend this class if you would like to keep more information about
   12154          * the Views.
   12155          * <p>
   12156          * If you want to provide your own implementation but still use `super` methods to record
   12157          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
   12158          * instances.
   12159          */
   12160         public static class ItemHolderInfo {
   12161 
   12162             /**
   12163              * The left edge of the View (excluding decorations)
   12164              */
   12165             public int left;
   12166 
   12167             /**
   12168              * The top edge of the View (excluding decorations)
   12169              */
   12170             public int top;
   12171 
   12172             /**
   12173              * The right edge of the View (excluding decorations)
   12174              */
   12175             public int right;
   12176 
   12177             /**
   12178              * The bottom edge of the View (excluding decorations)
   12179              */
   12180             public int bottom;
   12181 
   12182             /**
   12183              * The change flags that were passed to
   12184              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
   12185              */
   12186             @AdapterChanges
   12187             public int changeFlags;
   12188 
   12189             public ItemHolderInfo() {
   12190             }
   12191 
   12192             /**
   12193              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
   12194              * the given ViewHolder. Clears all {@link #changeFlags}.
   12195              *
   12196              * @param holder The ViewHolder whose bounds should be copied.
   12197              * @return This {@link ItemHolderInfo}
   12198              */
   12199             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
   12200                 return setFrom(holder, 0);
   12201             }
   12202 
   12203             /**
   12204              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
   12205              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
   12206              *
   12207              * @param holder The ViewHolder whose bounds should be copied.
   12208              * @param flags  The adapter change flags that were passed into
   12209              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
   12210              *               List)}.
   12211              * @return This {@link ItemHolderInfo}
   12212              */
   12213             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
   12214                     @AdapterChanges int flags) {
   12215                 final View view = holder.itemView;
   12216                 this.left = view.getLeft();
   12217                 this.top = view.getTop();
   12218                 this.right = view.getRight();
   12219                 this.bottom = view.getBottom();
   12220                 return this;
   12221             }
   12222         }
   12223     }
   12224 
   12225     @Override
   12226     protected int getChildDrawingOrder(int childCount, int i) {
   12227         if (mChildDrawingOrderCallback == null) {
   12228             return super.getChildDrawingOrder(childCount, i);
   12229         } else {
   12230             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
   12231         }
   12232     }
   12233 
   12234     /**
   12235      * A callback interface that can be used to alter the drawing order of RecyclerView children.
   12236      * <p>
   12237      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
   12238      * that applies to that method also applies to this callback. For example, changing the drawing
   12239      * order of two views will not have any effect if their elevation values are different since
   12240      * elevation overrides the result of this callback.
   12241      */
   12242     public interface ChildDrawingOrderCallback {
   12243         /**
   12244          * Returns the index of the child to draw for this iteration. Override this
   12245          * if you want to change the drawing order of children. By default, it
   12246          * returns i.
   12247          *
   12248          * @param i The current iteration.
   12249          * @return The index of the child to draw this iteration.
   12250          *
   12251          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
   12252          */
   12253         int onGetChildDrawingOrder(int childCount, int i);
   12254     }
   12255 }
   12256