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 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
    137 
    138     static final String TAG = "RecyclerView";
    139 
    140     static final boolean DEBUG = false;
    141 
    142     private static final int[]  NESTED_SCROLLING_ATTRS = { android.R.attr.nestedScrollingEnabled };
    143 
    144     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
    145 
    146     /**
    147      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
    148      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
    149      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
    150      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
    151      * this criteria.
    152      */
    153     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
    154             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
    155     /**
    156      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
    157      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
    158      * 0 when mode is unspecified.
    159      */
    160     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
    161 
    162     static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
    163 
    164     /**
    165      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
    166      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
    167      */
    168     private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
    169 
    170     /**
    171      * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
    172      * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
    173      */
    174     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
    175 
    176     /**
    177      * on API 15-, a focused child can still be considered a focused child of RV even after
    178      * it's being removed or its focusable flag is set to false. This is because when this focused
    179      * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
    180      * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
    181      * to request focus on a new child, which will clear the focus on the old (detached) child as a
    182      * side-effect.
    183      */
    184     private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
    185 
    186     static final boolean DISPATCH_TEMP_DETACH = false;
    187     public static final int HORIZONTAL = 0;
    188     public static final int VERTICAL = 1;
    189 
    190     public static final int NO_POSITION = -1;
    191     public static final long NO_ID = -1;
    192     public static final int INVALID_TYPE = -1;
    193 
    194     /**
    195      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    196      * that the RecyclerView should use the standard touch slop for smooth,
    197      * continuous scrolling.
    198      */
    199     public static final int TOUCH_SLOP_DEFAULT = 0;
    200 
    201     /**
    202      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    203      * that the RecyclerView should use the standard touch slop for scrolling
    204      * widgets that snap to a page or other coarse-grained barrier.
    205      */
    206     public static final int TOUCH_SLOP_PAGING = 1;
    207 
    208     static final int MAX_SCROLL_DURATION = 2000;
    209 
    210     /**
    211      * RecyclerView is calculating a scroll.
    212      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
    213      * it. Try to avoid using EditText, focusable views or handle them with care.
    214      */
    215     static final String TRACE_SCROLL_TAG = "RV Scroll";
    216 
    217     /**
    218      * OnLayout has been called by the View system.
    219      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
    220      * update themselves directly. This will cause a full re-layout but when it happens via the
    221      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
    222      */
    223     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
    224 
    225     /**
    226      * NotifyDataSetChanged or equal has been called.
    227      * If this is taking a long time, try sending granular notify adapter changes instead of just
    228      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
    229      * might help.
    230      */
    231     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
    232 
    233     /**
    234      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
    235      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
    236      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
    237      * methods.
    238      */
    239     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
    240 
    241     /**
    242      * RecyclerView is rebinding a View.
    243      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
    244      * doing extra operations in onBindViewHolder call.
    245      */
    246     static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
    247 
    248     /**
    249      * RecyclerView is attempting to pre-populate off screen views.
    250      */
    251     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
    252 
    253     /**
    254      * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
    255      * RecyclerView.
    256      */
    257     static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
    258 
    259     /**
    260      * RecyclerView is creating a new View.
    261      * If too many of these present in Systrace:
    262      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
    263      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
    264      * > Adapter#onFailedToRecycleView(ViewHolder)})
    265      *
    266      * - There might be too many item view types.
    267      * > Try merging them
    268      *
    269      * - There might be too many itemChange animations and not enough space in RecyclerPool.
    270      * >Try increasing your pool size and item cache size.
    271      */
    272     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
    273     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
    274             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
    275 
    276     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
    277 
    278     final Recycler mRecycler = new Recycler();
    279 
    280     private SavedState mPendingSavedState;
    281 
    282     /**
    283      * Handles adapter updates
    284      */
    285     AdapterHelper mAdapterHelper;
    286 
    287     /**
    288      * Handles abstraction between LayoutManager children and RecyclerView children
    289      */
    290     ChildHelper mChildHelper;
    291 
    292     /**
    293      * Keeps data about views to be used for animations
    294      */
    295     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
    296 
    297     /**
    298      * Prior to L, there is no way to query this variable which is why we override the setter and
    299      * track it here.
    300      */
    301     boolean mClipToPadding;
    302 
    303     /**
    304      * Note: this Runnable is only ever posted if:
    305      * 1) We've been through first layout
    306      * 2) We know we have a fixed size (mHasFixedSize)
    307      * 3) We're attached
    308      */
    309     final Runnable mUpdateChildViewsRunnable = new Runnable() {
    310         @Override
    311         public void run() {
    312             if (!mFirstLayoutComplete || isLayoutRequested()) {
    313                 // a layout request will happen, we should not do layout here.
    314                 return;
    315             }
    316             if (!mIsAttached) {
    317                 requestLayout();
    318                 // if we are not attached yet, mark us as requiring layout and skip
    319                 return;
    320             }
    321             if (mLayoutFrozen) {
    322                 mLayoutRequestEaten = true;
    323                 return; //we'll process updates when ice age ends.
    324             }
    325             consumePendingUpdateOperations();
    326         }
    327     };
    328 
    329     final Rect mTempRect = new Rect();
    330     private final Rect mTempRect2 = new Rect();
    331     final RectF mTempRectF = new RectF();
    332     Adapter mAdapter;
    333     @VisibleForTesting LayoutManager mLayout;
    334     RecyclerListener mRecyclerListener;
    335     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
    336     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
    337             new ArrayList<>();
    338     private OnItemTouchListener mActiveOnItemTouchListener;
    339     boolean mIsAttached;
    340     boolean mHasFixedSize;
    341     @VisibleForTesting boolean mFirstLayoutComplete;
    342 
    343     // Counting lock to control whether we should ignore requestLayout calls from children or not.
    344     private int mEatRequestLayout = 0;
    345 
    346     boolean mLayoutRequestEaten;
    347     boolean mLayoutFrozen;
    348     private boolean mIgnoreMotionEventTillDown;
    349 
    350     // binary OR of change events that were eaten during a layout or scroll.
    351     private int mEatenAccessibilityChangeFlags;
    352     boolean mAdapterUpdateDuringMeasure;
    353 
    354     private final AccessibilityManager mAccessibilityManager;
    355     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
    356 
    357     /**
    358      * Set to true when an adapter data set changed notification is received.
    359      * In that case, we cannot run any animations since we don't know what happened until layout.
    360      *
    361      * Attached items are invalid until next layout, at which point layout will animate/replace
    362      * items as necessary, building up content from the (effectively) new adapter from scratch.
    363      *
    364      * Cached items must be discarded when setting this to true, so that the cache may be freely
    365      * used by prefetching until the next layout occurs.
    366      *
    367      * @see #setDataSetChangedAfterLayout()
    368      */
    369     boolean mDataSetHasChangedAfterLayout = false;
    370 
    371     /**
    372      * This variable is incremented during a dispatchLayout and/or scroll.
    373      * Some methods should not be called during these periods (e.g. adapter data change).
    374      * Doing so will create hard to find bugs so we better check it and throw an exception.
    375      *
    376      * @see #assertInLayoutOrScroll(String)
    377      * @see #assertNotInLayoutOrScroll(String)
    378      */
    379     private int mLayoutOrScrollCounter = 0;
    380 
    381     /**
    382      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
    383      * (for API compatibility).
    384      * <p>
    385      * It is a bad practice for a developer to update the data in a scroll callback since it is
    386      * potentially called during a layout.
    387      */
    388     private int mDispatchScrollCounter = 0;
    389 
    390     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
    391 
    392     ItemAnimator mItemAnimator = new DefaultItemAnimator();
    393 
    394     private static final int INVALID_POINTER = -1;
    395 
    396     /**
    397      * The RecyclerView is not currently scrolling.
    398      * @see #getScrollState()
    399      */
    400     public static final int SCROLL_STATE_IDLE = 0;
    401 
    402     /**
    403      * The RecyclerView is currently being dragged by outside input such as user touch input.
    404      * @see #getScrollState()
    405      */
    406     public static final int SCROLL_STATE_DRAGGING = 1;
    407 
    408     /**
    409      * The RecyclerView is currently animating to a final position while not under
    410      * outside control.
    411      * @see #getScrollState()
    412      */
    413     public static final int SCROLL_STATE_SETTLING = 2;
    414 
    415     static final long FOREVER_NS = Long.MAX_VALUE;
    416 
    417     // Touch/scrolling handling
    418 
    419     private int mScrollState = SCROLL_STATE_IDLE;
    420     private int mScrollPointerId = INVALID_POINTER;
    421     private VelocityTracker mVelocityTracker;
    422     private int mInitialTouchX;
    423     private int mInitialTouchY;
    424     private int mLastTouchX;
    425     private int mLastTouchY;
    426     private int mTouchSlop;
    427     private OnFlingListener mOnFlingListener;
    428     private final int mMinFlingVelocity;
    429     private final int mMaxFlingVelocity;
    430     // This value is used when handling generic motion events.
    431     private float mScrollFactor = Float.MIN_VALUE;
    432     private boolean mPreserveFocusAfterLayout = true;
    433 
    434     final ViewFlinger mViewFlinger = new ViewFlinger();
    435 
    436     GapWorker mGapWorker;
    437     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
    438             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
    439 
    440     final State mState = new State();
    441 
    442     private OnScrollListener mScrollListener;
    443     private List<OnScrollListener> mScrollListeners;
    444 
    445     // For use in item animations
    446     boolean mItemsAddedOrRemoved = false;
    447     boolean mItemsChanged = false;
    448     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
    449             new ItemAnimatorRestoreListener();
    450     boolean mPostedAnimatorRunner = false;
    451     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
    452     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
    453 
    454     // simple array to keep min and max child position during a layout calculation
    455     // preserved not to create a new one in each layout pass
    456     private final int[] mMinMaxLayoutPositions = new int[2];
    457 
    458     private final int[] mScrollOffset = new int[2];
    459     private final int[] mScrollConsumed = new int[2];
    460     private final int[] mNestedOffsets = new int[2];
    461 
    462     /**
    463      * These are views that had their a11y importance changed during a layout. We defer these events
    464      * until the end of the layout because a11y service may make sync calls back to the RV while
    465      * the View's state is undefined.
    466      */
    467     @VisibleForTesting
    468     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList();
    469 
    470     private Runnable mItemAnimatorRunner = new Runnable() {
    471         @Override
    472         public void run() {
    473             if (mItemAnimator != null) {
    474                 mItemAnimator.runPendingAnimations();
    475             }
    476             mPostedAnimatorRunner = false;
    477         }
    478     };
    479 
    480     static final Interpolator sQuinticInterpolator = new Interpolator() {
    481         @Override
    482         public float getInterpolation(float t) {
    483             t -= 1.0f;
    484             return t * t * t * t * t + 1.0f;
    485         }
    486     };
    487 
    488     /**
    489      * The callback to convert view info diffs into animations.
    490      */
    491     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
    492             new ViewInfoStore.ProcessCallback() {
    493         @Override
    494         public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
    495                 @Nullable ItemHolderInfo postInfo) {
    496             mRecycler.unscrapView(viewHolder);
    497             animateDisappearance(viewHolder, info, postInfo);
    498         }
    499         @Override
    500         public void processAppeared(ViewHolder viewHolder,
    501                 ItemHolderInfo preInfo, ItemHolderInfo info) {
    502             animateAppearance(viewHolder, preInfo, info);
    503         }
    504 
    505         @Override
    506         public void processPersistent(ViewHolder viewHolder,
    507                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
    508             viewHolder.setIsRecyclable(false);
    509             if (mDataSetHasChangedAfterLayout) {
    510                 // since it was rebound, use change instead as we'll be mapping them from
    511                 // stable ids. If stable ids were false, we would not be running any
    512                 // animations
    513                 if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo, postInfo)) {
    514                     postAnimationRunner();
    515                 }
    516             } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
    517                 postAnimationRunner();
    518             }
    519         }
    520         @Override
    521         public void unused(ViewHolder viewHolder) {
    522             mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
    523         }
    524     };
    525 
    526     public RecyclerView(Context context) {
    527         this(context, null);
    528     }
    529 
    530     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
    531         this(context, attrs, 0);
    532     }
    533 
    534     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    535         super(context, attrs, defStyle);
    536         if (attrs != null) {
    537             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
    538             mClipToPadding = a.getBoolean(0, true);
    539             a.recycle();
    540         } else {
    541             mClipToPadding = true;
    542         }
    543         setScrollContainer(true);
    544         setFocusableInTouchMode(true);
    545 
    546         final ViewConfiguration vc = ViewConfiguration.get(context);
    547         mTouchSlop = vc.getScaledTouchSlop();
    548         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
    549         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
    550         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
    551 
    552         mItemAnimator.setListener(mItemAnimatorListener);
    553         initAdapterManager();
    554         initChildrenHelper();
    555         // If not explicitly specified this view is important for accessibility.
    556         if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    557             setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
    558         }
    559         mAccessibilityManager = (AccessibilityManager) getContext()
    560                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
    561         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
    562         // Create the layoutManager if specified.
    563 
    564         boolean nestedScrollingEnabled = true;
    565 
    566         if (attrs != null) {
    567             int defStyleRes = 0;
    568             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
    569                     defStyle, defStyleRes);
    570             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
    571             int descendantFocusability = a.getInt(
    572                     R.styleable.RecyclerView_descendantFocusability, -1);
    573             if (descendantFocusability == -1) {
    574                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    575             }
    576             a.recycle();
    577             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
    578 
    579             if (Build.VERSION.SDK_INT >= 21) {
    580                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
    581                         defStyle, defStyleRes);
    582                 nestedScrollingEnabled = a.getBoolean(0, true);
    583                 a.recycle();
    584             }
    585         } else {
    586             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    587         }
    588 
    589         // Re-set whether nested scrolling is enabled so that it is set on all API levels
    590         setNestedScrollingEnabled(nestedScrollingEnabled);
    591     }
    592 
    593     /**
    594      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
    595      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
    596      */
    597     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
    598         return mAccessibilityDelegate;
    599     }
    600 
    601     /**
    602      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
    603      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
    604      */
    605     public void setAccessibilityDelegateCompat(
    606             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
    607         mAccessibilityDelegate = accessibilityDelegate;
    608         setAccessibilityDelegate(mAccessibilityDelegate);
    609     }
    610 
    611     /**
    612      * Instantiate and set a LayoutManager, if specified in the attributes.
    613      */
    614     private void createLayoutManager(Context context, String className, AttributeSet attrs,
    615             int defStyleAttr, int defStyleRes) {
    616         if (className != null) {
    617             className = className.trim();
    618             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
    619                 className = getFullClassName(context, className);
    620                 try {
    621                     ClassLoader classLoader;
    622                     if (isInEditMode()) {
    623                         // Stupid layoutlib cannot handle simple class loaders.
    624                         classLoader = this.getClass().getClassLoader();
    625                     } else {
    626                         classLoader = context.getClassLoader();
    627                     }
    628                     Class<? extends LayoutManager> layoutManagerClass =
    629                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
    630                     Constructor<? extends LayoutManager> constructor;
    631                     Object[] constructorArgs = null;
    632                     try {
    633                         constructor = layoutManagerClass
    634                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
    635                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
    636                     } catch (NoSuchMethodException e) {
    637                         try {
    638                             constructor = layoutManagerClass.getConstructor();
    639                         } catch (NoSuchMethodException e1) {
    640                             e1.initCause(e);
    641                             throw new IllegalStateException(attrs.getPositionDescription()
    642                                     + ": Error creating LayoutManager " + className, e1);
    643                         }
    644                     }
    645                     constructor.setAccessible(true);
    646                     setLayoutManager(constructor.newInstance(constructorArgs));
    647                 } catch (ClassNotFoundException e) {
    648                     throw new IllegalStateException(attrs.getPositionDescription()
    649                             + ": Unable to find LayoutManager " + className, e);
    650                 } catch (InvocationTargetException e) {
    651                     throw new IllegalStateException(attrs.getPositionDescription()
    652                             + ": Could not instantiate the LayoutManager: " + className, e);
    653                 } catch (InstantiationException e) {
    654                     throw new IllegalStateException(attrs.getPositionDescription()
    655                             + ": Could not instantiate the LayoutManager: " + className, e);
    656                 } catch (IllegalAccessException e) {
    657                     throw new IllegalStateException(attrs.getPositionDescription()
    658                             + ": Cannot access non-public constructor " + className, e);
    659                 } catch (ClassCastException e) {
    660                     throw new IllegalStateException(attrs.getPositionDescription()
    661                             + ": Class is not a LayoutManager " + className, e);
    662                 }
    663             }
    664         }
    665     }
    666 
    667     private String getFullClassName(Context context, String className) {
    668         if (className.charAt(0) == '.') {
    669             return context.getPackageName() + className;
    670         }
    671         if (className.contains(".")) {
    672             return className;
    673         }
    674         return RecyclerView.class.getPackage().getName() + '.' + className;
    675     }
    676 
    677     private void initChildrenHelper() {
    678         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
    679             @Override
    680             public int getChildCount() {
    681                 return RecyclerView.this.getChildCount();
    682             }
    683 
    684             @Override
    685             public void addView(View child, int index) {
    686                 RecyclerView.this.addView(child, index);
    687                 dispatchChildAttached(child);
    688             }
    689 
    690             @Override
    691             public int indexOfChild(View view) {
    692                 return RecyclerView.this.indexOfChild(view);
    693             }
    694 
    695             @Override
    696             public void removeViewAt(int index) {
    697                 final View child = RecyclerView.this.getChildAt(index);
    698                 if (child != null) {
    699                     dispatchChildDetached(child);
    700                 }
    701                 RecyclerView.this.removeViewAt(index);
    702             }
    703 
    704             @Override
    705             public View getChildAt(int offset) {
    706                 return RecyclerView.this.getChildAt(offset);
    707             }
    708 
    709             @Override
    710             public void removeAllViews() {
    711                 final int count = getChildCount();
    712                 for (int i = 0; i < count; i++) {
    713                     dispatchChildDetached(getChildAt(i));
    714                 }
    715                 RecyclerView.this.removeAllViews();
    716             }
    717 
    718             @Override
    719             public ViewHolder getChildViewHolder(View view) {
    720                 return getChildViewHolderInt(view);
    721             }
    722 
    723             @Override
    724             public void attachViewToParent(View child, int index,
    725                     ViewGroup.LayoutParams layoutParams) {
    726                 final ViewHolder vh = getChildViewHolderInt(child);
    727                 if (vh != null) {
    728                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
    729                         throw new IllegalArgumentException("Called attach on a child which is not"
    730                                 + " detached: " + vh);
    731                     }
    732                     if (DEBUG) {
    733                         Log.d(TAG, "reAttach " + vh);
    734                     }
    735                     vh.clearTmpDetachFlag();
    736                 }
    737                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
    738             }
    739 
    740             @Override
    741             public void detachViewFromParent(int offset) {
    742                 final View view = getChildAt(offset);
    743                 if (view != null) {
    744                     final ViewHolder vh = getChildViewHolderInt(view);
    745                     if (vh != null) {
    746                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
    747                             throw new IllegalArgumentException("called detach on an already"
    748                                     + " detached child " + vh);
    749                         }
    750                         if (DEBUG) {
    751                             Log.d(TAG, "tmpDetach " + vh);
    752                         }
    753                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
    754                     }
    755                 }
    756                 RecyclerView.this.detachViewFromParent(offset);
    757             }
    758 
    759             @Override
    760             public void onEnteredHiddenState(View child) {
    761                 final ViewHolder vh = getChildViewHolderInt(child);
    762                 if (vh != null) {
    763                     vh.onEnteredHiddenState(RecyclerView.this);
    764                 }
    765             }
    766 
    767             @Override
    768             public void onLeftHiddenState(View child) {
    769                 final ViewHolder vh = getChildViewHolderInt(child);
    770                 if (vh != null) {
    771                     vh.onLeftHiddenState(RecyclerView.this);
    772                 }
    773             }
    774         });
    775     }
    776 
    777     void initAdapterManager() {
    778         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
    779             @Override
    780             public ViewHolder findViewHolder(int position) {
    781                 final ViewHolder vh = findViewHolderForPosition(position, true);
    782                 if (vh == null) {
    783                     return null;
    784                 }
    785                 // ensure it is not hidden because for adapter helper, the only thing matter is that
    786                 // LM thinks view is a child.
    787                 if (mChildHelper.isHidden(vh.itemView)) {
    788                     if (DEBUG) {
    789                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
    790                     }
    791                     return null;
    792                 }
    793                 return vh;
    794             }
    795 
    796             @Override
    797             public void offsetPositionsForRemovingInvisible(int start, int count) {
    798                 offsetPositionRecordsForRemove(start, count, true);
    799                 mItemsAddedOrRemoved = true;
    800                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
    801             }
    802 
    803             @Override
    804             public void offsetPositionsForRemovingLaidOutOrNewView(
    805                     int positionStart, int itemCount) {
    806                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
    807                 mItemsAddedOrRemoved = true;
    808             }
    809 
    810             @Override
    811             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
    812                 viewRangeUpdate(positionStart, itemCount, payload);
    813                 mItemsChanged = true;
    814             }
    815 
    816             @Override
    817             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
    818                 dispatchUpdate(op);
    819             }
    820 
    821             void dispatchUpdate(AdapterHelper.UpdateOp op) {
    822                 switch (op.cmd) {
    823                     case AdapterHelper.UpdateOp.ADD:
    824                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
    825                         break;
    826                     case AdapterHelper.UpdateOp.REMOVE:
    827                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
    828                         break;
    829                     case AdapterHelper.UpdateOp.UPDATE:
    830                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
    831                                 op.payload);
    832                         break;
    833                     case AdapterHelper.UpdateOp.MOVE:
    834                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
    835                         break;
    836                 }
    837             }
    838 
    839             @Override
    840             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
    841                 dispatchUpdate(op);
    842             }
    843 
    844             @Override
    845             public void offsetPositionsForAdd(int positionStart, int itemCount) {
    846                 offsetPositionRecordsForInsert(positionStart, itemCount);
    847                 mItemsAddedOrRemoved = true;
    848             }
    849 
    850             @Override
    851             public void offsetPositionsForMove(int from, int to) {
    852                 offsetPositionRecordsForMove(from, to);
    853                 // should we create mItemsMoved ?
    854                 mItemsAddedOrRemoved = true;
    855             }
    856         });
    857     }
    858 
    859     /**
    860      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
    861      * size is not affected by the adapter contents. RecyclerView can still change its size based
    862      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
    863      * size of its children or contents of its adapter (except the number of items in the adapter).
    864      * <p>
    865      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
    866      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
    867      *
    868      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
    869      */
    870     public void setHasFixedSize(boolean hasFixedSize) {
    871         mHasFixedSize = hasFixedSize;
    872     }
    873 
    874     /**
    875      * @return true if the app has specified that changes in adapter content cannot change
    876      * the size of the RecyclerView itself.
    877      */
    878     public boolean hasFixedSize() {
    879         return mHasFixedSize;
    880     }
    881 
    882     @Override
    883     public void setClipToPadding(boolean clipToPadding) {
    884         if (clipToPadding != mClipToPadding) {
    885             invalidateGlows();
    886         }
    887         mClipToPadding = clipToPadding;
    888         super.setClipToPadding(clipToPadding);
    889         if (mFirstLayoutComplete) {
    890             requestLayout();
    891         }
    892     }
    893 
    894     /**
    895      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
    896      * not clip) any EdgeEffect to the padded region, if padding is present.
    897      * <p>
    898      * By default, children are clipped to the padding of their parent
    899      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
    900      *
    901      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
    902      *         clip) any EdgeEffect to the padded region, false otherwise.
    903      *
    904      * @attr name android:clipToPadding
    905      */
    906     @Override
    907     public boolean getClipToPadding() {
    908         return mClipToPadding;
    909     }
    910 
    911     /**
    912      * Configure the scrolling touch slop for a specific use case.
    913      *
    914      * Set up the RecyclerView's scrolling motion threshold based on common usages.
    915      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
    916      *
    917      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
    918      *                     the intended usage of this RecyclerView
    919      */
    920     public void setScrollingTouchSlop(int slopConstant) {
    921         final ViewConfiguration vc = ViewConfiguration.get(getContext());
    922         switch (slopConstant) {
    923             default:
    924                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
    925                         + slopConstant + "; using default value");
    926                 // fall-through
    927             case TOUCH_SLOP_DEFAULT:
    928                 mTouchSlop = vc.getScaledTouchSlop();
    929                 break;
    930 
    931             case TOUCH_SLOP_PAGING:
    932                 mTouchSlop = vc.getScaledPagingTouchSlop();
    933                 break;
    934         }
    935     }
    936 
    937     /**
    938      * Swaps the current adapter with the provided one. It is similar to
    939      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
    940      * {@link ViewHolder} and does not clear the RecycledViewPool.
    941      * <p>
    942      * Note that it still calls onAdapterChanged callbacks.
    943      *
    944      * @param adapter The new adapter to set, or null to set no adapter.
    945      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
    946      *                                      Views. If adapters have stable ids and/or you want to
    947      *                                      animate the disappearing views, you may prefer to set
    948      *                                      this to false.
    949      * @see #setAdapter(Adapter)
    950      */
    951     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
    952         // bail out if layout is frozen
    953         setLayoutFrozen(false);
    954         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
    955         setDataSetChangedAfterLayout();
    956         requestLayout();
    957     }
    958     /**
    959      * Set a new adapter to provide child views on demand.
    960      * <p>
    961      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
    962      * only one adapter, it will be cleared.
    963      *
    964      * @param adapter The new adapter to set, or null to set no adapter.
    965      * @see #swapAdapter(Adapter, boolean)
    966      */
    967     public void setAdapter(Adapter adapter) {
    968         // bail out if layout is frozen
    969         setLayoutFrozen(false);
    970         setAdapterInternal(adapter, false, true);
    971         requestLayout();
    972     }
    973 
    974     /**
    975      * Removes and recycles all views - both those currently attached, and those in the Recycler.
    976      */
    977     void removeAndRecycleViews() {
    978         // end all running animations
    979         if (mItemAnimator != null) {
    980             mItemAnimator.endAnimations();
    981         }
    982         // Since animations are ended, mLayout.children should be equal to
    983         // recyclerView.children. This may not be true if item animator's end does not work as
    984         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
    985         // count.
    986         if (mLayout != null) {
    987             mLayout.removeAndRecycleAllViews(mRecycler);
    988             mLayout.removeAndRecycleScrapInt(mRecycler);
    989         }
    990         // we should clear it here before adapters are swapped to ensure correct callbacks.
    991         mRecycler.clear();
    992     }
    993 
    994     /**
    995      * Replaces the current adapter with the new one and triggers listeners.
    996      * @param adapter The new adapter
    997      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
    998      *                               item types with the current adapter (helps us avoid cache
    999      *                               invalidation).
   1000      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
   1001      *                               compatibleWithPrevious is false, this parameter is ignored.
   1002      */
   1003     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
   1004             boolean removeAndRecycleViews) {
   1005         if (mAdapter != null) {
   1006             mAdapter.unregisterAdapterDataObserver(mObserver);
   1007             mAdapter.onDetachedFromRecyclerView(this);
   1008         }
   1009         if (!compatibleWithPrevious || removeAndRecycleViews) {
   1010             removeAndRecycleViews();
   1011         }
   1012         mAdapterHelper.reset();
   1013         final Adapter oldAdapter = mAdapter;
   1014         mAdapter = adapter;
   1015         if (adapter != null) {
   1016             adapter.registerAdapterDataObserver(mObserver);
   1017             adapter.onAttachedToRecyclerView(this);
   1018         }
   1019         if (mLayout != null) {
   1020             mLayout.onAdapterChanged(oldAdapter, mAdapter);
   1021         }
   1022         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
   1023         mState.mStructureChanged = true;
   1024         markKnownViewsInvalid();
   1025     }
   1026 
   1027     /**
   1028      * Retrieves the previously set adapter or null if no adapter is set.
   1029      *
   1030      * @return The previously set adapter
   1031      * @see #setAdapter(Adapter)
   1032      */
   1033     public Adapter getAdapter() {
   1034         return mAdapter;
   1035     }
   1036 
   1037     /**
   1038      * Register a listener that will be notified whenever a child view is recycled.
   1039      *
   1040      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
   1041      * that a child view is no longer needed. If an application associates expensive
   1042      * or heavyweight data with item views, this may be a good place to release
   1043      * or free those resources.</p>
   1044      *
   1045      * @param listener Listener to register, or null to clear
   1046      */
   1047     public void setRecyclerListener(RecyclerListener listener) {
   1048         mRecyclerListener = listener;
   1049     }
   1050 
   1051     /**
   1052      * <p>Return the offset of the RecyclerView's text baseline from the its top
   1053      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
   1054      * this method returns -1.</p>
   1055      *
   1056      * @return the offset of the baseline within the RecyclerView's bounds or -1
   1057      *         if baseline alignment is not supported
   1058      */
   1059     @Override
   1060     public int getBaseline() {
   1061         if (mLayout != null) {
   1062             return mLayout.getBaseline();
   1063         } else {
   1064             return super.getBaseline();
   1065         }
   1066     }
   1067 
   1068     /**
   1069      * Register a listener that will be notified whenever a child view is attached to or detached
   1070      * from RecyclerView.
   1071      *
   1072      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
   1073      * that a child view is no longer needed. If an application associates expensive
   1074      * or heavyweight data with item views, this may be a good place to release
   1075      * or free those resources.</p>
   1076      *
   1077      * @param listener Listener to register
   1078      */
   1079     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
   1080         if (mOnChildAttachStateListeners == null) {
   1081             mOnChildAttachStateListeners = new ArrayList<>();
   1082         }
   1083         mOnChildAttachStateListeners.add(listener);
   1084     }
   1085 
   1086     /**
   1087      * Removes the provided listener from child attached state listeners list.
   1088      *
   1089      * @param listener Listener to unregister
   1090      */
   1091     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
   1092         if (mOnChildAttachStateListeners == null) {
   1093             return;
   1094         }
   1095         mOnChildAttachStateListeners.remove(listener);
   1096     }
   1097 
   1098     /**
   1099      * Removes all listeners that were added via
   1100      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
   1101      */
   1102     public void clearOnChildAttachStateChangeListeners() {
   1103         if (mOnChildAttachStateListeners != null) {
   1104             mOnChildAttachStateListeners.clear();
   1105         }
   1106     }
   1107 
   1108     /**
   1109      * Set the {@link LayoutManager} that this RecyclerView will use.
   1110      *
   1111      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
   1112      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
   1113      * layout arrangements for child views. These arrangements are controlled by the
   1114      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
   1115      *
   1116      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
   1117      *
   1118      * @param layout LayoutManager to use
   1119      */
   1120     public void setLayoutManager(LayoutManager layout) {
   1121         if (layout == mLayout) {
   1122             return;
   1123         }
   1124         stopScroll();
   1125         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
   1126         // chance that LayoutManagers will re-use views.
   1127         if (mLayout != null) {
   1128             // end all running animations
   1129             if (mItemAnimator != null) {
   1130                 mItemAnimator.endAnimations();
   1131             }
   1132             mLayout.removeAndRecycleAllViews(mRecycler);
   1133             mLayout.removeAndRecycleScrapInt(mRecycler);
   1134             mRecycler.clear();
   1135 
   1136             if (mIsAttached) {
   1137                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
   1138             }
   1139             mLayout.setRecyclerView(null);
   1140             mLayout = null;
   1141         } else {
   1142             mRecycler.clear();
   1143         }
   1144         // this is just a defensive measure for faulty item animators.
   1145         mChildHelper.removeAllViewsUnfiltered();
   1146         mLayout = layout;
   1147         if (layout != null) {
   1148             if (layout.mRecyclerView != null) {
   1149                 throw new IllegalArgumentException("LayoutManager " + layout
   1150                         + " is already attached to a RecyclerView: " + layout.mRecyclerView);
   1151             }
   1152             mLayout.setRecyclerView(this);
   1153             if (mIsAttached) {
   1154                 mLayout.dispatchAttachedToWindow(this);
   1155             }
   1156         }
   1157         mRecycler.updateViewCacheSize();
   1158         requestLayout();
   1159     }
   1160 
   1161     /**
   1162      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
   1163      * <p>
   1164      * If the {@link OnFlingListener} is set then it will receive
   1165      * calls to {@link #fling(int,int)} and will be able to intercept them.
   1166      *
   1167      * @param onFlingListener The {@link OnFlingListener} instance.
   1168      */
   1169     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
   1170         mOnFlingListener = onFlingListener;
   1171     }
   1172 
   1173     /**
   1174      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
   1175      *
   1176      * @return The {@link OnFlingListener} instance currently set (can be null).
   1177      */
   1178     @Nullable
   1179     public OnFlingListener getOnFlingListener() {
   1180         return mOnFlingListener;
   1181     }
   1182 
   1183     @Override
   1184     protected Parcelable onSaveInstanceState() {
   1185         SavedState state = new SavedState(super.onSaveInstanceState());
   1186         if (mPendingSavedState != null) {
   1187             state.copyFrom(mPendingSavedState);
   1188         } else if (mLayout != null) {
   1189             state.mLayoutState = mLayout.onSaveInstanceState();
   1190         } else {
   1191             state.mLayoutState = null;
   1192         }
   1193 
   1194         return state;
   1195     }
   1196 
   1197     @Override
   1198     protected void onRestoreInstanceState(Parcelable state) {
   1199         if (!(state instanceof SavedState)) {
   1200             super.onRestoreInstanceState(state);
   1201             return;
   1202         }
   1203 
   1204         mPendingSavedState = (SavedState) state;
   1205         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
   1206         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
   1207             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
   1208         }
   1209     }
   1210 
   1211     /**
   1212      * Override to prevent freezing of any views created by the adapter.
   1213      */
   1214     @Override
   1215     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
   1216         dispatchFreezeSelfOnly(container);
   1217     }
   1218 
   1219     /**
   1220      * Override to prevent thawing of any views created by the adapter.
   1221      */
   1222     @Override
   1223     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
   1224         dispatchThawSelfOnly(container);
   1225     }
   1226 
   1227     /**
   1228      * Adds a view to the animatingViews list.
   1229      * mAnimatingViews holds the child views that are currently being kept around
   1230      * purely for the purpose of being animated out of view. They are drawn as a regular
   1231      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
   1232      * as they are managed separately from the regular child views.
   1233      * @param viewHolder The ViewHolder to be removed
   1234      */
   1235     private void addAnimatingView(ViewHolder viewHolder) {
   1236         final View view = viewHolder.itemView;
   1237         final boolean alreadyParented = view.getParent() == this;
   1238         mRecycler.unscrapView(getChildViewHolder(view));
   1239         if (viewHolder.isTmpDetached()) {
   1240             // re-attach
   1241             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
   1242         } else if (!alreadyParented) {
   1243             mChildHelper.addView(view, true);
   1244         } else {
   1245             mChildHelper.hide(view);
   1246         }
   1247     }
   1248 
   1249     /**
   1250      * Removes a view from the animatingViews list.
   1251      * @param view The view to be removed
   1252      * @see #addAnimatingView(RecyclerView.ViewHolder)
   1253      * @return true if an animating view is removed
   1254      */
   1255     boolean removeAnimatingView(View view) {
   1256         eatRequestLayout();
   1257         final boolean removed = mChildHelper.removeViewIfHidden(view);
   1258         if (removed) {
   1259             final ViewHolder viewHolder = getChildViewHolderInt(view);
   1260             mRecycler.unscrapView(viewHolder);
   1261             mRecycler.recycleViewHolderInternal(viewHolder);
   1262             if (DEBUG) {
   1263                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
   1264             }
   1265         }
   1266         // only clear request eaten flag if we removed the view.
   1267         resumeRequestLayout(!removed);
   1268         return removed;
   1269     }
   1270 
   1271     /**
   1272      * Return the {@link LayoutManager} currently responsible for
   1273      * layout policy for this RecyclerView.
   1274      *
   1275      * @return The currently bound LayoutManager
   1276      */
   1277     public LayoutManager getLayoutManager() {
   1278         return mLayout;
   1279     }
   1280 
   1281     /**
   1282      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
   1283      * if no pool is set for this view a new one will be created. See
   1284      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
   1285      *
   1286      * @return The pool used to store recycled item views for reuse.
   1287      * @see #setRecycledViewPool(RecycledViewPool)
   1288      */
   1289     public RecycledViewPool getRecycledViewPool() {
   1290         return mRecycler.getRecycledViewPool();
   1291     }
   1292 
   1293     /**
   1294      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
   1295      * This can be useful if you have multiple RecyclerViews with adapters that use the same
   1296      * view types, for example if you have several data sets with the same kinds of item views
   1297      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
   1298      *
   1299      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
   1300      */
   1301     public void setRecycledViewPool(RecycledViewPool pool) {
   1302         mRecycler.setRecycledViewPool(pool);
   1303     }
   1304 
   1305     /**
   1306      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
   1307      *
   1308      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
   1309      *
   1310      * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
   1311      */
   1312     public void setViewCacheExtension(ViewCacheExtension extension) {
   1313         mRecycler.setViewCacheExtension(extension);
   1314     }
   1315 
   1316     /**
   1317      * Set the number of offscreen views to retain before adding them to the potentially shared
   1318      * {@link #getRecycledViewPool() recycled view pool}.
   1319      *
   1320      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
   1321      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
   1322      * to rebind them.</p>
   1323      *
   1324      * @param size Number of views to cache offscreen before returning them to the general
   1325      *             recycled view pool
   1326      */
   1327     public void setItemViewCacheSize(int size) {
   1328         mRecycler.setViewCacheSize(size);
   1329     }
   1330 
   1331     /**
   1332      * Return the current scrolling state of the RecyclerView.
   1333      *
   1334      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
   1335      * {@link #SCROLL_STATE_SETTLING}
   1336      */
   1337     public int getScrollState() {
   1338         return mScrollState;
   1339     }
   1340 
   1341     void setScrollState(int state) {
   1342         if (state == mScrollState) {
   1343             return;
   1344         }
   1345         if (DEBUG) {
   1346             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
   1347                     new Exception());
   1348         }
   1349         mScrollState = state;
   1350         if (state != SCROLL_STATE_SETTLING) {
   1351             stopScrollersInternal();
   1352         }
   1353         dispatchOnScrollStateChanged(state);
   1354     }
   1355 
   1356     /**
   1357      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1358      * affect both measurement and drawing of individual item views.
   1359      *
   1360      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1361      * be run/queried/drawn first for their effects on item views. Padding added to views
   1362      * will be nested; a padding added by an earlier decoration will mean further
   1363      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1364      * given area.</p>
   1365      *
   1366      * @param decor Decoration to add
   1367      * @param index Position in the decoration chain to insert this decoration at. If this value
   1368      *              is negative the decoration will be added at the end.
   1369      */
   1370     public void addItemDecoration(ItemDecoration decor, int index) {
   1371         if (mLayout != null) {
   1372             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
   1373                     + " layout");
   1374         }
   1375         if (mItemDecorations.isEmpty()) {
   1376             setWillNotDraw(false);
   1377         }
   1378         if (index < 0) {
   1379             mItemDecorations.add(decor);
   1380         } else {
   1381             mItemDecorations.add(index, decor);
   1382         }
   1383         markItemDecorInsetsDirty();
   1384         requestLayout();
   1385     }
   1386 
   1387     /**
   1388      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1389      * affect both measurement and drawing of individual item views.
   1390      *
   1391      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1392      * be run/queried/drawn first for their effects on item views. Padding added to views
   1393      * will be nested; a padding added by an earlier decoration will mean further
   1394      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1395      * given area.</p>
   1396      *
   1397      * @param decor Decoration to add
   1398      */
   1399     public void addItemDecoration(ItemDecoration decor) {
   1400         addItemDecoration(decor, -1);
   1401     }
   1402 
   1403     /**
   1404      * Remove an {@link ItemDecoration} from this RecyclerView.
   1405      *
   1406      * <p>The given decoration will no longer impact the measurement and drawing of
   1407      * item views.</p>
   1408      *
   1409      * @param decor Decoration to remove
   1410      * @see #addItemDecoration(ItemDecoration)
   1411      */
   1412     public void removeItemDecoration(ItemDecoration decor) {
   1413         if (mLayout != null) {
   1414             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
   1415                     + " layout");
   1416         }
   1417         mItemDecorations.remove(decor);
   1418         if (mItemDecorations.isEmpty()) {
   1419             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
   1420         }
   1421         markItemDecorInsetsDirty();
   1422         requestLayout();
   1423     }
   1424 
   1425     /**
   1426      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
   1427      * <p>
   1428      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
   1429      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
   1430      * true if childDrawingOrderCallback is not null, false otherwise.
   1431      * <p>
   1432      * Note that child drawing order may be overridden by View's elevation.
   1433      *
   1434      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
   1435      *                                  system.
   1436      */
   1437     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
   1438         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
   1439             return;
   1440         }
   1441         mChildDrawingOrderCallback = childDrawingOrderCallback;
   1442         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
   1443     }
   1444 
   1445     /**
   1446      * Set a listener that will be notified of any changes in scroll state or position.
   1447      *
   1448      * @param listener Listener to set or null to clear
   1449      *
   1450      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
   1451      *             {@link #removeOnScrollListener(OnScrollListener)}
   1452      */
   1453     @Deprecated
   1454     public void setOnScrollListener(OnScrollListener listener) {
   1455         mScrollListener = listener;
   1456     }
   1457 
   1458     /**
   1459      * Add a listener that will be notified of any changes in scroll state or position.
   1460      *
   1461      * <p>Components that add a listener should take care to remove it when finished.
   1462      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
   1463      * to remove all attached listeners.</p>
   1464      *
   1465      * @param listener listener to set or null to clear
   1466      */
   1467     public void addOnScrollListener(OnScrollListener listener) {
   1468         if (mScrollListeners == null) {
   1469             mScrollListeners = new ArrayList<>();
   1470         }
   1471         mScrollListeners.add(listener);
   1472     }
   1473 
   1474     /**
   1475      * Remove a listener that was notified of any changes in scroll state or position.
   1476      *
   1477      * @param listener listener to set or null to clear
   1478      */
   1479     public void removeOnScrollListener(OnScrollListener listener) {
   1480         if (mScrollListeners != null) {
   1481             mScrollListeners.remove(listener);
   1482         }
   1483     }
   1484 
   1485     /**
   1486      * Remove all secondary listener that were notified of any changes in scroll state or position.
   1487      */
   1488     public void clearOnScrollListeners() {
   1489         if (mScrollListeners != null) {
   1490             mScrollListeners.clear();
   1491         }
   1492     }
   1493 
   1494     /**
   1495      * Convenience method to scroll to a certain position.
   1496      *
   1497      * RecyclerView does not implement scrolling logic, rather forwards the call to
   1498      * {@link com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
   1499      * @param position Scroll to this adapter position
   1500      * @see com.android.internal.widget.RecyclerView.LayoutManager#scrollToPosition(int)
   1501      */
   1502     public void scrollToPosition(int position) {
   1503         if (mLayoutFrozen) {
   1504             return;
   1505         }
   1506         stopScroll();
   1507         if (mLayout == null) {
   1508             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
   1509                     + "Call setLayoutManager with a non-null argument.");
   1510             return;
   1511         }
   1512         mLayout.scrollToPosition(position);
   1513         awakenScrollBars();
   1514     }
   1515 
   1516     void jumpToPositionForSmoothScroller(int position) {
   1517         if (mLayout == null) {
   1518             return;
   1519         }
   1520         mLayout.scrollToPosition(position);
   1521         awakenScrollBars();
   1522     }
   1523 
   1524     /**
   1525      * Starts a smooth scroll to an adapter position.
   1526      * <p>
   1527      * To support smooth scrolling, you must override
   1528      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
   1529      * {@link SmoothScroller}.
   1530      * <p>
   1531      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
   1532      * provide a custom smooth scroll logic, override
   1533      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
   1534      * LayoutManager.
   1535      *
   1536      * @param position The adapter position to scroll to
   1537      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
   1538      */
   1539     public void smoothScrollToPosition(int position) {
   1540         if (mLayoutFrozen) {
   1541             return;
   1542         }
   1543         if (mLayout == null) {
   1544             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
   1545                     + "Call setLayoutManager with a non-null argument.");
   1546             return;
   1547         }
   1548         mLayout.smoothScrollToPosition(this, mState, position);
   1549     }
   1550 
   1551     @Override
   1552     public void scrollTo(int x, int y) {
   1553         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
   1554                 + "Use scrollToPosition instead");
   1555     }
   1556 
   1557     @Override
   1558     public void scrollBy(int x, int y) {
   1559         if (mLayout == null) {
   1560             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
   1561                     + "Call setLayoutManager with a non-null argument.");
   1562             return;
   1563         }
   1564         if (mLayoutFrozen) {
   1565             return;
   1566         }
   1567         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   1568         final boolean canScrollVertical = mLayout.canScrollVertically();
   1569         if (canScrollHorizontal || canScrollVertical) {
   1570             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
   1571         }
   1572     }
   1573 
   1574     /**
   1575      * Helper method reflect data changes to the state.
   1576      * <p>
   1577      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
   1578      * but data actually changed.
   1579      * <p>
   1580      * This method consumes all deferred changes to avoid that case.
   1581      */
   1582     void consumePendingUpdateOperations() {
   1583         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
   1584             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
   1585             dispatchLayout();
   1586             Trace.endSection();
   1587             return;
   1588         }
   1589         if (!mAdapterHelper.hasPendingUpdates()) {
   1590             return;
   1591         }
   1592 
   1593         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
   1594         // of the visible items is affected and if not, just ignore the change.
   1595         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
   1596                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
   1597                         | AdapterHelper.UpdateOp.MOVE)) {
   1598             Trace.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
   1599             eatRequestLayout();
   1600             onEnterLayoutOrScroll();
   1601             mAdapterHelper.preProcess();
   1602             if (!mLayoutRequestEaten) {
   1603                 if (hasUpdatedView()) {
   1604                     dispatchLayout();
   1605                 } else {
   1606                     // no need to layout, clean state
   1607                     mAdapterHelper.consumePostponedUpdates();
   1608                 }
   1609             }
   1610             resumeRequestLayout(true);
   1611             onExitLayoutOrScroll();
   1612             Trace.endSection();
   1613         } else if (mAdapterHelper.hasPendingUpdates()) {
   1614             Trace.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
   1615             dispatchLayout();
   1616             Trace.endSection();
   1617         }
   1618     }
   1619 
   1620     /**
   1621      * @return True if an existing view holder needs to be updated
   1622      */
   1623     private boolean hasUpdatedView() {
   1624         final int childCount = mChildHelper.getChildCount();
   1625         for (int i = 0; i < childCount; i++) {
   1626             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   1627             if (holder == null || holder.shouldIgnore()) {
   1628                 continue;
   1629             }
   1630             if (holder.isUpdated()) {
   1631                 return true;
   1632             }
   1633         }
   1634         return false;
   1635     }
   1636 
   1637     /**
   1638      * Does not perform bounds checking. Used by internal methods that have already validated input.
   1639      * <p>
   1640      * It also reports any unused scroll request to the related EdgeEffect.
   1641      *
   1642      * @param x The amount of horizontal scroll request
   1643      * @param y The amount of vertical scroll request
   1644      * @param ev The originating MotionEvent, or null if not from a touch event.
   1645      *
   1646      * @return Whether any scroll was consumed in either direction.
   1647      */
   1648     boolean scrollByInternal(int x, int y, MotionEvent ev) {
   1649         int unconsumedX = 0, unconsumedY = 0;
   1650         int consumedX = 0, consumedY = 0;
   1651 
   1652         consumePendingUpdateOperations();
   1653         if (mAdapter != null) {
   1654             eatRequestLayout();
   1655             onEnterLayoutOrScroll();
   1656             Trace.beginSection(TRACE_SCROLL_TAG);
   1657             if (x != 0) {
   1658                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
   1659                 unconsumedX = x - consumedX;
   1660             }
   1661             if (y != 0) {
   1662                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
   1663                 unconsumedY = y - consumedY;
   1664             }
   1665             Trace.endSection();
   1666             repositionShadowingViews();
   1667             onExitLayoutOrScroll();
   1668             resumeRequestLayout(false);
   1669         }
   1670         if (!mItemDecorations.isEmpty()) {
   1671             invalidate();
   1672         }
   1673 
   1674         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
   1675             // Update the last touch co-ords, taking any scroll offset into account
   1676             mLastTouchX -= mScrollOffset[0];
   1677             mLastTouchY -= mScrollOffset[1];
   1678             if (ev != null) {
   1679                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   1680             }
   1681             mNestedOffsets[0] += mScrollOffset[0];
   1682             mNestedOffsets[1] += mScrollOffset[1];
   1683         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   1684             if (ev != null) {
   1685                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
   1686             }
   1687             considerReleasingGlowsOnScroll(x, y);
   1688         }
   1689         if (consumedX != 0 || consumedY != 0) {
   1690             dispatchOnScrolled(consumedX, consumedY);
   1691         }
   1692         if (!awakenScrollBars()) {
   1693             invalidate();
   1694         }
   1695         return consumedX != 0 || consumedY != 0;
   1696     }
   1697 
   1698     /**
   1699      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
   1700      * range. This value is used to compute the length of the thumb within the scrollbar's track.
   1701      * </p>
   1702      *
   1703      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1704      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
   1705      *
   1706      * <p>Default implementation returns 0.</p>
   1707      *
   1708      * <p>If you want to support scroll bars, override
   1709      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
   1710      * LayoutManager. </p>
   1711      *
   1712      * @return The horizontal offset of the scrollbar's thumb
   1713      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
   1714      * (RecyclerView.State)
   1715      */
   1716     @Override
   1717     public int computeHorizontalScrollOffset() {
   1718         if (mLayout == null) {
   1719             return 0;
   1720         }
   1721         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
   1722     }
   1723 
   1724     /**
   1725      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
   1726      * horizontal range. This value is used to compute the length of the thumb within the
   1727      * scrollbar's track.</p>
   1728      *
   1729      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1730      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
   1731      *
   1732      * <p>Default implementation returns 0.</p>
   1733      *
   1734      * <p>If you want to support scroll bars, override
   1735      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
   1736      * LayoutManager.</p>
   1737      *
   1738      * @return The horizontal extent of the scrollbar's thumb
   1739      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
   1740      */
   1741     @Override
   1742     public int computeHorizontalScrollExtent() {
   1743         if (mLayout == null) {
   1744             return 0;
   1745         }
   1746         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
   1747     }
   1748 
   1749     /**
   1750      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
   1751      *
   1752      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1753      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
   1754      *
   1755      * <p>Default implementation returns 0.</p>
   1756      *
   1757      * <p>If you want to support scroll bars, override
   1758      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
   1759      * LayoutManager.</p>
   1760      *
   1761      * @return The total horizontal range represented by the vertical scrollbar
   1762      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
   1763      */
   1764     @Override
   1765     public int computeHorizontalScrollRange() {
   1766         if (mLayout == null) {
   1767             return 0;
   1768         }
   1769         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
   1770     }
   1771 
   1772     /**
   1773      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
   1774      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
   1775      *
   1776      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1777      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
   1778      *
   1779      * <p>Default implementation returns 0.</p>
   1780      *
   1781      * <p>If you want to support scroll bars, override
   1782      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
   1783      * LayoutManager.</p>
   1784      *
   1785      * @return The vertical offset of the scrollbar's thumb
   1786      * @see com.android.internal.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
   1787      * (RecyclerView.State)
   1788      */
   1789     @Override
   1790     public int computeVerticalScrollOffset() {
   1791         if (mLayout == null) {
   1792             return 0;
   1793         }
   1794         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
   1795     }
   1796 
   1797     /**
   1798      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
   1799      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
   1800      *
   1801      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1802      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
   1803      *
   1804      * <p>Default implementation returns 0.</p>
   1805      *
   1806      * <p>If you want to support scroll bars, override
   1807      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
   1808      * LayoutManager.</p>
   1809      *
   1810      * @return The vertical extent of the scrollbar's thumb
   1811      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
   1812      */
   1813     @Override
   1814     public int computeVerticalScrollExtent() {
   1815         if (mLayout == null) {
   1816             return 0;
   1817         }
   1818         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
   1819     }
   1820 
   1821     /**
   1822      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
   1823      *
   1824      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1825      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
   1826      *
   1827      * <p>Default implementation returns 0.</p>
   1828      *
   1829      * <p>If you want to support scroll bars, override
   1830      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
   1831      * LayoutManager.</p>
   1832      *
   1833      * @return The total vertical range represented by the vertical scrollbar
   1834      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
   1835      */
   1836     @Override
   1837     public int computeVerticalScrollRange() {
   1838         if (mLayout == null) {
   1839             return 0;
   1840         }
   1841         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
   1842     }
   1843 
   1844 
   1845     void eatRequestLayout() {
   1846         mEatRequestLayout++;
   1847         if (mEatRequestLayout == 1 && !mLayoutFrozen) {
   1848             mLayoutRequestEaten = false;
   1849         }
   1850     }
   1851 
   1852     void resumeRequestLayout(boolean performLayoutChildren) {
   1853         if (mEatRequestLayout < 1) {
   1854             //noinspection PointlessBooleanExpression
   1855             if (DEBUG) {
   1856                 throw new IllegalStateException("invalid eat request layout count");
   1857             }
   1858             mEatRequestLayout = 1;
   1859         }
   1860         if (!performLayoutChildren) {
   1861             // Reset the layout request eaten counter.
   1862             // This is necessary since eatRequest calls can be nested in which case the other
   1863             // call will override the inner one.
   1864             // for instance:
   1865             // eat layout for process adapter updates
   1866             //   eat layout for dispatchLayout
   1867             //     a bunch of req layout calls arrive
   1868 
   1869             mLayoutRequestEaten = false;
   1870         }
   1871         if (mEatRequestLayout == 1) {
   1872             // when layout is frozen we should delay dispatchLayout()
   1873             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen
   1874                     && mLayout != null && mAdapter != null) {
   1875                 dispatchLayout();
   1876             }
   1877             if (!mLayoutFrozen) {
   1878                 mLayoutRequestEaten = false;
   1879             }
   1880         }
   1881         mEatRequestLayout--;
   1882     }
   1883 
   1884     /**
   1885      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
   1886      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
   1887      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
   1888      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
   1889      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
   1890      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
   1891      * called.
   1892      *
   1893      * <p>
   1894      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
   1895      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
   1896      * RecyclerView, State, int)}.
   1897      * <p>
   1898      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
   1899      * stop frozen.
   1900      * <p>
   1901      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
   1902      * responsibility to call ItemAnimator.end().
   1903      *
   1904      * @param frozen   true to freeze layout and scroll, false to re-enable.
   1905      */
   1906     public void setLayoutFrozen(boolean frozen) {
   1907         if (frozen != mLayoutFrozen) {
   1908             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
   1909             if (!frozen) {
   1910                 mLayoutFrozen = false;
   1911                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
   1912                     requestLayout();
   1913                 }
   1914                 mLayoutRequestEaten = false;
   1915             } else {
   1916                 final long now = SystemClock.uptimeMillis();
   1917                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
   1918                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
   1919                 onTouchEvent(cancelEvent);
   1920                 mLayoutFrozen = true;
   1921                 mIgnoreMotionEventTillDown = true;
   1922                 stopScroll();
   1923             }
   1924         }
   1925     }
   1926 
   1927     /**
   1928      * Returns true if layout and scroll are frozen.
   1929      *
   1930      * @return true if layout and scroll are frozen
   1931      * @see #setLayoutFrozen(boolean)
   1932      */
   1933     public boolean isLayoutFrozen() {
   1934         return mLayoutFrozen;
   1935     }
   1936 
   1937     /**
   1938      * Animate a scroll by the given amount of pixels along either axis.
   1939      *
   1940      * @param dx Pixels to scroll horizontally
   1941      * @param dy Pixels to scroll vertically
   1942      */
   1943     public void smoothScrollBy(int dx, int dy) {
   1944         smoothScrollBy(dx, dy, null);
   1945     }
   1946 
   1947     /**
   1948      * Animate a scroll by the given amount of pixels along either axis.
   1949      *
   1950      * @param dx Pixels to scroll horizontally
   1951      * @param dy Pixels to scroll vertically
   1952      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
   1953      *                     {@code null}, RecyclerView is going to use the default interpolator.
   1954      */
   1955     public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
   1956         if (mLayout == null) {
   1957             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
   1958                     + "Call setLayoutManager with a non-null argument.");
   1959             return;
   1960         }
   1961         if (mLayoutFrozen) {
   1962             return;
   1963         }
   1964         if (!mLayout.canScrollHorizontally()) {
   1965             dx = 0;
   1966         }
   1967         if (!mLayout.canScrollVertically()) {
   1968             dy = 0;
   1969         }
   1970         if (dx != 0 || dy != 0) {
   1971             mViewFlinger.smoothScrollBy(dx, dy, interpolator);
   1972         }
   1973     }
   1974 
   1975     /**
   1976      * Begin a standard fling with an initial velocity along each axis in pixels per second.
   1977      * If the velocity given is below the system-defined minimum this method will return false
   1978      * and no fling will occur.
   1979      *
   1980      * @param velocityX Initial horizontal velocity in pixels per second
   1981      * @param velocityY Initial vertical velocity in pixels per second
   1982      * @return true if the fling was started, false if the velocity was too low to fling or
   1983      * LayoutManager does not support scrolling in the axis fling is issued.
   1984      *
   1985      * @see LayoutManager#canScrollVertically()
   1986      * @see LayoutManager#canScrollHorizontally()
   1987      */
   1988     public boolean fling(int velocityX, int velocityY) {
   1989         if (mLayout == null) {
   1990             Log.e(TAG, "Cannot fling without a LayoutManager set. "
   1991                     + "Call setLayoutManager with a non-null argument.");
   1992             return false;
   1993         }
   1994         if (mLayoutFrozen) {
   1995             return false;
   1996         }
   1997 
   1998         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   1999         final boolean canScrollVertical = mLayout.canScrollVertically();
   2000 
   2001         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
   2002             velocityX = 0;
   2003         }
   2004         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
   2005             velocityY = 0;
   2006         }
   2007         if (velocityX == 0 && velocityY == 0) {
   2008             // If we don't have any velocity, return false
   2009             return false;
   2010         }
   2011 
   2012         if (!dispatchNestedPreFling(velocityX, velocityY)) {
   2013             final boolean canScroll = canScrollHorizontal || canScrollVertical;
   2014             dispatchNestedFling(velocityX, velocityY, canScroll);
   2015 
   2016             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
   2017                 return true;
   2018             }
   2019 
   2020             if (canScroll) {
   2021                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
   2022                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
   2023                 mViewFlinger.fling(velocityX, velocityY);
   2024                 return true;
   2025             }
   2026         }
   2027         return false;
   2028     }
   2029 
   2030     /**
   2031      * Stop any current scroll in progress, such as one started by
   2032      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
   2033      */
   2034     public void stopScroll() {
   2035         setScrollState(SCROLL_STATE_IDLE);
   2036         stopScrollersInternal();
   2037     }
   2038 
   2039     /**
   2040      * Similar to {@link #stopScroll()} but does not set the state.
   2041      */
   2042     private void stopScrollersInternal() {
   2043         mViewFlinger.stop();
   2044         if (mLayout != null) {
   2045             mLayout.stopSmoothScroller();
   2046         }
   2047     }
   2048 
   2049     /**
   2050      * Returns the minimum velocity to start a fling.
   2051      *
   2052      * @return The minimum velocity to start a fling
   2053      */
   2054     public int getMinFlingVelocity() {
   2055         return mMinFlingVelocity;
   2056     }
   2057 
   2058 
   2059     /**
   2060      * Returns the maximum fling velocity used by this RecyclerView.
   2061      *
   2062      * @return The maximum fling velocity used by this RecyclerView.
   2063      */
   2064     public int getMaxFlingVelocity() {
   2065         return mMaxFlingVelocity;
   2066     }
   2067 
   2068     /**
   2069      * Apply a pull to relevant overscroll glow effects
   2070      */
   2071     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
   2072         boolean invalidate = false;
   2073         if (overscrollX < 0) {
   2074             ensureLeftGlow();
   2075             mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight());
   2076             invalidate = true;
   2077         } else if (overscrollX > 0) {
   2078             ensureRightGlow();
   2079             mRightGlow.onPull(overscrollX / getWidth(), y / getHeight());
   2080             invalidate = true;
   2081         }
   2082 
   2083         if (overscrollY < 0) {
   2084             ensureTopGlow();
   2085             mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth());
   2086             invalidate = true;
   2087         } else if (overscrollY > 0) {
   2088             ensureBottomGlow();
   2089             mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth());
   2090             invalidate = true;
   2091         }
   2092 
   2093         if (invalidate || overscrollX != 0 || overscrollY != 0) {
   2094             postInvalidateOnAnimation();
   2095         }
   2096     }
   2097 
   2098     private void releaseGlows() {
   2099         boolean needsInvalidate = false;
   2100         if (mLeftGlow != null) {
   2101             mLeftGlow.onRelease();
   2102             needsInvalidate = true;
   2103         }
   2104         if (mTopGlow != null) {
   2105             mTopGlow.onRelease();
   2106             needsInvalidate = true;
   2107         }
   2108         if (mRightGlow != null) {
   2109             mRightGlow.onRelease();
   2110             needsInvalidate = true;
   2111         }
   2112         if (mBottomGlow != null) {
   2113             mBottomGlow.onRelease();
   2114             needsInvalidate = true;
   2115         }
   2116         if (needsInvalidate) {
   2117             postInvalidateOnAnimation();
   2118         }
   2119     }
   2120 
   2121     void considerReleasingGlowsOnScroll(int dx, int dy) {
   2122         boolean needsInvalidate = false;
   2123         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
   2124             mLeftGlow.onRelease();
   2125             needsInvalidate = true;
   2126         }
   2127         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
   2128             mRightGlow.onRelease();
   2129             needsInvalidate = true;
   2130         }
   2131         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
   2132             mTopGlow.onRelease();
   2133             needsInvalidate = true;
   2134         }
   2135         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
   2136             mBottomGlow.onRelease();
   2137             needsInvalidate = true;
   2138         }
   2139         if (needsInvalidate) {
   2140             postInvalidateOnAnimation();
   2141         }
   2142     }
   2143 
   2144     void absorbGlows(int velocityX, int velocityY) {
   2145         if (velocityX < 0) {
   2146             ensureLeftGlow();
   2147             mLeftGlow.onAbsorb(-velocityX);
   2148         } else if (velocityX > 0) {
   2149             ensureRightGlow();
   2150             mRightGlow.onAbsorb(velocityX);
   2151         }
   2152 
   2153         if (velocityY < 0) {
   2154             ensureTopGlow();
   2155             mTopGlow.onAbsorb(-velocityY);
   2156         } else if (velocityY > 0) {
   2157             ensureBottomGlow();
   2158             mBottomGlow.onAbsorb(velocityY);
   2159         }
   2160 
   2161         if (velocityX != 0 || velocityY != 0) {
   2162             postInvalidateOnAnimation();
   2163         }
   2164     }
   2165 
   2166     void ensureLeftGlow() {
   2167         if (mLeftGlow != null) {
   2168             return;
   2169         }
   2170         mLeftGlow = new EdgeEffect(getContext());
   2171         if (mClipToPadding) {
   2172             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   2173                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   2174         } else {
   2175             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   2176         }
   2177     }
   2178 
   2179     void ensureRightGlow() {
   2180         if (mRightGlow != null) {
   2181             return;
   2182         }
   2183         mRightGlow = new EdgeEffect(getContext());
   2184         if (mClipToPadding) {
   2185             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   2186                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   2187         } else {
   2188             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   2189         }
   2190     }
   2191 
   2192     void ensureTopGlow() {
   2193         if (mTopGlow != null) {
   2194             return;
   2195         }
   2196         mTopGlow = new EdgeEffect(getContext());
   2197         if (mClipToPadding) {
   2198             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   2199                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   2200         } else {
   2201             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   2202         }
   2203 
   2204     }
   2205 
   2206     void ensureBottomGlow() {
   2207         if (mBottomGlow != null) {
   2208             return;
   2209         }
   2210         mBottomGlow = new EdgeEffect(getContext());
   2211         if (mClipToPadding) {
   2212             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   2213                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   2214         } else {
   2215             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   2216         }
   2217     }
   2218 
   2219     void invalidateGlows() {
   2220         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
   2221     }
   2222 
   2223     /**
   2224      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
   2225      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
   2226      * that differs from other ViewGroups.
   2227      * <p>
   2228      * It first does a focus search within the RecyclerView. If this search finds a View that is in
   2229      * the focus direction with respect to the currently focused View, RecyclerView returns that
   2230      * child as the next focus target. When it cannot find such child, it calls
   2231      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
   2232      * in the focus search direction. If LayoutManager adds a View that matches the
   2233      * focus search criteria, it will be returned as the focus search result. Otherwise,
   2234      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
   2235      * <p>
   2236      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
   2237      * is not in the focus direction is still valid focus target which may not be the desired
   2238      * behavior if the Adapter has more children in the focus direction. To handle this case,
   2239      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
   2240      * focus search in that direction. If there are no Views to gain focus, it will call
   2241      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
   2242      * focus search with the original (relative) direction. This allows RecyclerView to provide
   2243      * better candidates to the focus search while still allowing the view system to take focus from
   2244      * the RecyclerView and give it to a more suitable child if such child exists.
   2245      *
   2246      * @param focused The view that currently has focus
   2247      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   2248      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
   2249      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
   2250      *
   2251      * @return A new View that can be the next focus after the focused View
   2252      */
   2253     @Override
   2254     public View focusSearch(View focused, int direction) {
   2255         View result = mLayout.onInterceptFocusSearch(focused, direction);
   2256         if (result != null) {
   2257             return result;
   2258         }
   2259         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
   2260                 && !isComputingLayout() && !mLayoutFrozen;
   2261 
   2262         final FocusFinder ff = FocusFinder.getInstance();
   2263         if (canRunFocusFailure
   2264                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
   2265             // convert direction to absolute direction and see if we have a view there and if not
   2266             // tell LayoutManager to add if it can.
   2267             boolean needsFocusFailureLayout = false;
   2268             if (mLayout.canScrollVertically()) {
   2269                 final int absDir =
   2270                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
   2271                 final View found = ff.findNextFocus(this, focused, absDir);
   2272                 needsFocusFailureLayout = found == null;
   2273                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
   2274                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
   2275                     direction = absDir;
   2276                 }
   2277             }
   2278             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
   2279                 boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
   2280                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
   2281                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
   2282                 final View found = ff.findNextFocus(this, focused, absDir);
   2283                 needsFocusFailureLayout = found == null;
   2284                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
   2285                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
   2286                     direction = absDir;
   2287                 }
   2288             }
   2289             if (needsFocusFailureLayout) {
   2290                 consumePendingUpdateOperations();
   2291                 final View focusedItemView = findContainingItemView(focused);
   2292                 if (focusedItemView == null) {
   2293                     // panic, focused view is not a child anymore, cannot call super.
   2294                     return null;
   2295                 }
   2296                 eatRequestLayout();
   2297                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
   2298                 resumeRequestLayout(false);
   2299             }
   2300             result = ff.findNextFocus(this, focused, direction);
   2301         } else {
   2302             result = ff.findNextFocus(this, focused, direction);
   2303             if (result == null && canRunFocusFailure) {
   2304                 consumePendingUpdateOperations();
   2305                 final View focusedItemView = findContainingItemView(focused);
   2306                 if (focusedItemView == null) {
   2307                     // panic, focused view is not a child anymore, cannot call super.
   2308                     return null;
   2309                 }
   2310                 eatRequestLayout();
   2311                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
   2312                 resumeRequestLayout(false);
   2313             }
   2314         }
   2315         return isPreferredNextFocus(focused, result, direction)
   2316                 ? result : super.focusSearch(focused, direction);
   2317     }
   2318 
   2319     /**
   2320      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
   2321      * assign it as the next focus View instead of letting view hierarchy decide.
   2322      * A good candidate means a View that is aligned in the focus direction wrt the focused View
   2323      * and is not the RecyclerView itself.
   2324      * When this method returns false, RecyclerView will let the parent make the decision so the
   2325      * same View may still get the focus as a result of that search.
   2326      */
   2327     private boolean isPreferredNextFocus(View focused, View next, int direction) {
   2328         if (next == null || next == this) {
   2329             return false;
   2330         }
   2331         if (focused == null) {
   2332             return true;
   2333         }
   2334 
   2335         if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) {
   2336             final boolean rtl = mLayout.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
   2337             final int absHorizontal = (direction == View.FOCUS_FORWARD) ^ rtl
   2338                     ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
   2339             if (isPreferredNextFocusAbsolute(focused, next, absHorizontal)) {
   2340                 return true;
   2341             }
   2342             if (direction == View.FOCUS_FORWARD) {
   2343                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_DOWN);
   2344             } else {
   2345                 return isPreferredNextFocusAbsolute(focused, next, View.FOCUS_UP);
   2346             }
   2347         } else {
   2348             return isPreferredNextFocusAbsolute(focused, next, direction);
   2349         }
   2350 
   2351     }
   2352 
   2353     /**
   2354      * Logic taken from FocusSearch#isCandidate
   2355      */
   2356     private boolean isPreferredNextFocusAbsolute(View focused, View next, int direction) {
   2357         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
   2358         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
   2359         offsetDescendantRectToMyCoords(focused, mTempRect);
   2360         offsetDescendantRectToMyCoords(next, mTempRect2);
   2361         switch (direction) {
   2362             case View.FOCUS_LEFT:
   2363                 return (mTempRect.right > mTempRect2.right
   2364                         || mTempRect.left >= mTempRect2.right)
   2365                         && mTempRect.left > mTempRect2.left;
   2366             case View.FOCUS_RIGHT:
   2367                 return (mTempRect.left < mTempRect2.left
   2368                         || mTempRect.right <= mTempRect2.left)
   2369                         && mTempRect.right < mTempRect2.right;
   2370             case View.FOCUS_UP:
   2371                 return (mTempRect.bottom > mTempRect2.bottom
   2372                         || mTempRect.top >= mTempRect2.bottom)
   2373                         && mTempRect.top > mTempRect2.top;
   2374             case View.FOCUS_DOWN:
   2375                 return (mTempRect.top < mTempRect2.top
   2376                         || mTempRect.bottom <= mTempRect2.top)
   2377                         && mTempRect.bottom < mTempRect2.bottom;
   2378         }
   2379         throw new IllegalArgumentException("direction must be absolute. received:" + direction);
   2380     }
   2381 
   2382     @Override
   2383     public void requestChildFocus(View child, View focused) {
   2384         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
   2385             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
   2386 
   2387             // get item decor offsets w/o refreshing. If they are invalid, there will be another
   2388             // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
   2389             // View in viewport.
   2390             final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
   2391             if (focusedLayoutParams instanceof LayoutParams) {
   2392                 // if focused child has item decors, use them. Otherwise, ignore.
   2393                 final LayoutParams lp = (LayoutParams) focusedLayoutParams;
   2394                 if (!lp.mInsetsDirty) {
   2395                     final Rect insets = lp.mDecorInsets;
   2396                     mTempRect.left -= insets.left;
   2397                     mTempRect.right += insets.right;
   2398                     mTempRect.top -= insets.top;
   2399                     mTempRect.bottom += insets.bottom;
   2400                 }
   2401             }
   2402 
   2403             offsetDescendantRectToMyCoords(focused, mTempRect);
   2404             offsetRectIntoDescendantCoords(child, mTempRect);
   2405             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
   2406         }
   2407         super.requestChildFocus(child, focused);
   2408     }
   2409 
   2410     @Override
   2411     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
   2412         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
   2413     }
   2414 
   2415     @Override
   2416     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
   2417         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
   2418             super.addFocusables(views, direction, focusableMode);
   2419         }
   2420     }
   2421 
   2422     @Override
   2423     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
   2424         if (isComputingLayout()) {
   2425             // if we are in the middle of a layout calculation, don't let any child take focus.
   2426             // RV will handle it after layout calculation is finished.
   2427             return false;
   2428         }
   2429         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
   2430     }
   2431 
   2432     @Override
   2433     protected void onAttachedToWindow() {
   2434         super.onAttachedToWindow();
   2435         mLayoutOrScrollCounter = 0;
   2436         mIsAttached = true;
   2437         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
   2438         if (mLayout != null) {
   2439             mLayout.dispatchAttachedToWindow(this);
   2440         }
   2441         mPostedAnimatorRunner = false;
   2442 
   2443         if (ALLOW_THREAD_GAP_WORK) {
   2444             // Register with gap worker
   2445             mGapWorker = GapWorker.sGapWorker.get();
   2446             if (mGapWorker == null) {
   2447                 mGapWorker = new GapWorker();
   2448 
   2449                 // break 60 fps assumption if data from display appears valid
   2450                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
   2451                 Display display = getDisplay();
   2452                 float refreshRate = 60.0f;
   2453                 if (!isInEditMode() && display != null) {
   2454                     float displayRefreshRate = display.getRefreshRate();
   2455                     if (displayRefreshRate >= 30.0f) {
   2456                         refreshRate = displayRefreshRate;
   2457                     }
   2458                 }
   2459                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
   2460                 GapWorker.sGapWorker.set(mGapWorker);
   2461             }
   2462             mGapWorker.add(this);
   2463         }
   2464     }
   2465 
   2466     @Override
   2467     protected void onDetachedFromWindow() {
   2468         super.onDetachedFromWindow();
   2469         if (mItemAnimator != null) {
   2470             mItemAnimator.endAnimations();
   2471         }
   2472         stopScroll();
   2473         mIsAttached = false;
   2474         if (mLayout != null) {
   2475             mLayout.dispatchDetachedFromWindow(this, mRecycler);
   2476         }
   2477         mPendingAccessibilityImportanceChange.clear();
   2478         removeCallbacks(mItemAnimatorRunner);
   2479         mViewInfoStore.onDetach();
   2480 
   2481         if (ALLOW_THREAD_GAP_WORK) {
   2482             // Unregister with gap worker
   2483             mGapWorker.remove(this);
   2484             mGapWorker = null;
   2485         }
   2486     }
   2487 
   2488     /**
   2489      * Returns true if RecyclerView is attached to window.
   2490      */
   2491     // @override
   2492     public boolean isAttachedToWindow() {
   2493         return mIsAttached;
   2494     }
   2495 
   2496     /**
   2497      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2498      * {@link IllegalStateException} if it <b>is not</b>.
   2499      *
   2500      * @param message The message for the exception. Can be null.
   2501      * @see #assertNotInLayoutOrScroll(String)
   2502      */
   2503     void assertInLayoutOrScroll(String message) {
   2504         if (!isComputingLayout()) {
   2505             if (message == null) {
   2506                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
   2507                         + "computing a layout or scrolling");
   2508             }
   2509             throw new IllegalStateException(message);
   2510 
   2511         }
   2512     }
   2513 
   2514     /**
   2515      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2516      * {@link IllegalStateException} if it <b>is</b>.
   2517      *
   2518      * @param message The message for the exception. Can be null.
   2519      * @see #assertInLayoutOrScroll(String)
   2520      */
   2521     void assertNotInLayoutOrScroll(String message) {
   2522         if (isComputingLayout()) {
   2523             if (message == null) {
   2524                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
   2525                         + "computing a layout or scrolling");
   2526             }
   2527             throw new IllegalStateException(message);
   2528         }
   2529         if (mDispatchScrollCounter > 0) {
   2530             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might be run"
   2531                     + " during a measure & layout pass where you cannot change the RecyclerView"
   2532                     + " data. Any method call that might change the structure of the RecyclerView"
   2533                     + " or the adapter contents should be postponed to the next frame.",
   2534                     new IllegalStateException(""));
   2535         }
   2536     }
   2537 
   2538     /**
   2539      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
   2540      * to child views or this view's standard scrolling behavior.
   2541      *
   2542      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
   2543      * returns true from
   2544      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
   2545      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
   2546      * for each incoming MotionEvent until the end of the gesture.</p>
   2547      *
   2548      * @param listener Listener to add
   2549      * @see SimpleOnItemTouchListener
   2550      */
   2551     public void addOnItemTouchListener(OnItemTouchListener listener) {
   2552         mOnItemTouchListeners.add(listener);
   2553     }
   2554 
   2555     /**
   2556      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
   2557      *
   2558      * @param listener Listener to remove
   2559      */
   2560     public void removeOnItemTouchListener(OnItemTouchListener listener) {
   2561         mOnItemTouchListeners.remove(listener);
   2562         if (mActiveOnItemTouchListener == listener) {
   2563             mActiveOnItemTouchListener = null;
   2564         }
   2565     }
   2566 
   2567     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
   2568         final int action = e.getAction();
   2569         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
   2570             mActiveOnItemTouchListener = null;
   2571         }
   2572 
   2573         final int listenerCount = mOnItemTouchListeners.size();
   2574         for (int i = 0; i < listenerCount; i++) {
   2575             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2576             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
   2577                 mActiveOnItemTouchListener = listener;
   2578                 return true;
   2579             }
   2580         }
   2581         return false;
   2582     }
   2583 
   2584     private boolean dispatchOnItemTouch(MotionEvent e) {
   2585         final int action = e.getAction();
   2586         if (mActiveOnItemTouchListener != null) {
   2587             if (action == MotionEvent.ACTION_DOWN) {
   2588                 // Stale state from a previous gesture, we're starting a new one. Clear it.
   2589                 mActiveOnItemTouchListener = null;
   2590             } else {
   2591                 mActiveOnItemTouchListener.onTouchEvent(this, e);
   2592                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
   2593                     // Clean up for the next gesture.
   2594                     mActiveOnItemTouchListener = null;
   2595                 }
   2596                 return true;
   2597             }
   2598         }
   2599 
   2600         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
   2601         // as called from onInterceptTouchEvent; skip it.
   2602         if (action != MotionEvent.ACTION_DOWN) {
   2603             final int listenerCount = mOnItemTouchListeners.size();
   2604             for (int i = 0; i < listenerCount; i++) {
   2605                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2606                 if (listener.onInterceptTouchEvent(this, e)) {
   2607                     mActiveOnItemTouchListener = listener;
   2608                     return true;
   2609                 }
   2610             }
   2611         }
   2612         return false;
   2613     }
   2614 
   2615     @Override
   2616     public boolean onInterceptTouchEvent(MotionEvent e) {
   2617         if (mLayoutFrozen) {
   2618             // When layout is frozen,  RV does not intercept the motion event.
   2619             // A child view e.g. a button may still get the click.
   2620             return false;
   2621         }
   2622         if (dispatchOnItemTouchIntercept(e)) {
   2623             cancelTouch();
   2624             return true;
   2625         }
   2626 
   2627         if (mLayout == null) {
   2628             return false;
   2629         }
   2630 
   2631         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   2632         final boolean canScrollVertically = mLayout.canScrollVertically();
   2633 
   2634         if (mVelocityTracker == null) {
   2635             mVelocityTracker = VelocityTracker.obtain();
   2636         }
   2637         mVelocityTracker.addMovement(e);
   2638 
   2639         final int action = e.getActionMasked();
   2640         final int actionIndex = e.getActionIndex();
   2641 
   2642         switch (action) {
   2643             case MotionEvent.ACTION_DOWN:
   2644                 if (mIgnoreMotionEventTillDown) {
   2645                     mIgnoreMotionEventTillDown = false;
   2646                 }
   2647                 mScrollPointerId = e.getPointerId(0);
   2648                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   2649                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   2650 
   2651                 if (mScrollState == SCROLL_STATE_SETTLING) {
   2652                     getParent().requestDisallowInterceptTouchEvent(true);
   2653                     setScrollState(SCROLL_STATE_DRAGGING);
   2654                 }
   2655 
   2656                 // Clear the nested offsets
   2657                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
   2658 
   2659                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
   2660                 if (canScrollHorizontally) {
   2661                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
   2662                 }
   2663                 if (canScrollVertically) {
   2664                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
   2665                 }
   2666                 startNestedScroll(nestedScrollAxis);
   2667                 break;
   2668 
   2669             case MotionEvent.ACTION_POINTER_DOWN:
   2670                 mScrollPointerId = e.getPointerId(actionIndex);
   2671                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
   2672                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
   2673                 break;
   2674 
   2675             case MotionEvent.ACTION_MOVE: {
   2676                 final int index = e.findPointerIndex(mScrollPointerId);
   2677                 if (index < 0) {
   2678                     Log.e(TAG, "Error processing scroll; pointer index for id "
   2679                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   2680                     return false;
   2681                 }
   2682 
   2683                 final int x = (int) (e.getX(index) + 0.5f);
   2684                 final int y = (int) (e.getY(index) + 0.5f);
   2685                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   2686                     final int dx = x - mInitialTouchX;
   2687                     final int dy = y - mInitialTouchY;
   2688                     boolean startScroll = false;
   2689                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   2690                         mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
   2691                         startScroll = true;
   2692                     }
   2693                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   2694                         mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
   2695                         startScroll = true;
   2696                     }
   2697                     if (startScroll) {
   2698                         setScrollState(SCROLL_STATE_DRAGGING);
   2699                     }
   2700                 }
   2701             } break;
   2702 
   2703             case MotionEvent.ACTION_POINTER_UP: {
   2704                 onPointerUp(e);
   2705             } break;
   2706 
   2707             case MotionEvent.ACTION_UP: {
   2708                 mVelocityTracker.clear();
   2709                 stopNestedScroll();
   2710             } break;
   2711 
   2712             case MotionEvent.ACTION_CANCEL: {
   2713                 cancelTouch();
   2714             }
   2715         }
   2716         return mScrollState == SCROLL_STATE_DRAGGING;
   2717     }
   2718 
   2719     @Override
   2720     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   2721         final int listenerCount = mOnItemTouchListeners.size();
   2722         for (int i = 0; i < listenerCount; i++) {
   2723             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2724             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
   2725         }
   2726         super.requestDisallowInterceptTouchEvent(disallowIntercept);
   2727     }
   2728 
   2729     @Override
   2730     public boolean onTouchEvent(MotionEvent e) {
   2731         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
   2732             return false;
   2733         }
   2734         if (dispatchOnItemTouch(e)) {
   2735             cancelTouch();
   2736             return true;
   2737         }
   2738 
   2739         if (mLayout == null) {
   2740             return false;
   2741         }
   2742 
   2743         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   2744         final boolean canScrollVertically = mLayout.canScrollVertically();
   2745 
   2746         if (mVelocityTracker == null) {
   2747             mVelocityTracker = VelocityTracker.obtain();
   2748         }
   2749         boolean eventAddedToVelocityTracker = false;
   2750 
   2751         final MotionEvent vtev = MotionEvent.obtain(e);
   2752         final int action = e.getActionMasked();
   2753         final int actionIndex = e.getActionIndex();
   2754 
   2755         if (action == MotionEvent.ACTION_DOWN) {
   2756             mNestedOffsets[0] = mNestedOffsets[1] = 0;
   2757         }
   2758         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
   2759 
   2760         switch (action) {
   2761             case MotionEvent.ACTION_DOWN: {
   2762                 mScrollPointerId = e.getPointerId(0);
   2763                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   2764                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   2765 
   2766                 int nestedScrollAxis = View.SCROLL_AXIS_NONE;
   2767                 if (canScrollHorizontally) {
   2768                     nestedScrollAxis |= View.SCROLL_AXIS_HORIZONTAL;
   2769                 }
   2770                 if (canScrollVertically) {
   2771                     nestedScrollAxis |= View.SCROLL_AXIS_VERTICAL;
   2772                 }
   2773                 startNestedScroll(nestedScrollAxis);
   2774             } break;
   2775 
   2776             case MotionEvent.ACTION_POINTER_DOWN: {
   2777                 mScrollPointerId = e.getPointerId(actionIndex);
   2778                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
   2779                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
   2780             } break;
   2781 
   2782             case MotionEvent.ACTION_MOVE: {
   2783                 final int index = e.findPointerIndex(mScrollPointerId);
   2784                 if (index < 0) {
   2785                     Log.e(TAG, "Error processing scroll; pointer index for id "
   2786                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   2787                     return false;
   2788                 }
   2789 
   2790                 final int x = (int) (e.getX(index) + 0.5f);
   2791                 final int y = (int) (e.getY(index) + 0.5f);
   2792                 int dx = mLastTouchX - x;
   2793                 int dy = mLastTouchY - y;
   2794 
   2795                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
   2796                     dx -= mScrollConsumed[0];
   2797                     dy -= mScrollConsumed[1];
   2798                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   2799                     // Updated the nested offsets
   2800                     mNestedOffsets[0] += mScrollOffset[0];
   2801                     mNestedOffsets[1] += mScrollOffset[1];
   2802                 }
   2803 
   2804                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   2805                     boolean startScroll = false;
   2806                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   2807                         if (dx > 0) {
   2808                             dx -= mTouchSlop;
   2809                         } else {
   2810                             dx += mTouchSlop;
   2811                         }
   2812                         startScroll = true;
   2813                     }
   2814                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   2815                         if (dy > 0) {
   2816                             dy -= mTouchSlop;
   2817                         } else {
   2818                             dy += mTouchSlop;
   2819                         }
   2820                         startScroll = true;
   2821                     }
   2822                     if (startScroll) {
   2823                         setScrollState(SCROLL_STATE_DRAGGING);
   2824                     }
   2825                 }
   2826 
   2827                 if (mScrollState == SCROLL_STATE_DRAGGING) {
   2828                     mLastTouchX = x - mScrollOffset[0];
   2829                     mLastTouchY = y - mScrollOffset[1];
   2830 
   2831                     if (scrollByInternal(
   2832                             canScrollHorizontally ? dx : 0,
   2833                             canScrollVertically ? dy : 0,
   2834                             vtev)) {
   2835                         getParent().requestDisallowInterceptTouchEvent(true);
   2836                     }
   2837                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
   2838                         mGapWorker.postFromTraversal(this, dx, dy);
   2839                     }
   2840                 }
   2841             } break;
   2842 
   2843             case MotionEvent.ACTION_POINTER_UP: {
   2844                 onPointerUp(e);
   2845             } break;
   2846 
   2847             case MotionEvent.ACTION_UP: {
   2848                 mVelocityTracker.addMovement(vtev);
   2849                 eventAddedToVelocityTracker = true;
   2850                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
   2851                 final float xvel = canScrollHorizontally
   2852                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
   2853                 final float yvel = canScrollVertically
   2854                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
   2855                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
   2856                     setScrollState(SCROLL_STATE_IDLE);
   2857                 }
   2858                 resetTouch();
   2859             } break;
   2860 
   2861             case MotionEvent.ACTION_CANCEL: {
   2862                 cancelTouch();
   2863             } break;
   2864         }
   2865 
   2866         if (!eventAddedToVelocityTracker) {
   2867             mVelocityTracker.addMovement(vtev);
   2868         }
   2869         vtev.recycle();
   2870 
   2871         return true;
   2872     }
   2873 
   2874     private void resetTouch() {
   2875         if (mVelocityTracker != null) {
   2876             mVelocityTracker.clear();
   2877         }
   2878         stopNestedScroll();
   2879         releaseGlows();
   2880     }
   2881 
   2882     private void cancelTouch() {
   2883         resetTouch();
   2884         setScrollState(SCROLL_STATE_IDLE);
   2885     }
   2886 
   2887     private void onPointerUp(MotionEvent e) {
   2888         final int actionIndex = e.getActionIndex();
   2889         if (e.getPointerId(actionIndex) == mScrollPointerId) {
   2890             // Pick a new pointer to pick up the slack.
   2891             final int newIndex = actionIndex == 0 ? 1 : 0;
   2892             mScrollPointerId = e.getPointerId(newIndex);
   2893             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
   2894             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
   2895         }
   2896     }
   2897 
   2898     // @Override
   2899     public boolean onGenericMotionEvent(MotionEvent event) {
   2900         if (mLayout == null) {
   2901             return false;
   2902         }
   2903         if (mLayoutFrozen) {
   2904             return false;
   2905         }
   2906         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
   2907             if (event.getAction() == MotionEvent.ACTION_SCROLL) {
   2908                 final float vScroll, hScroll;
   2909                 if (mLayout.canScrollVertically()) {
   2910                     // Inverse the sign of the vertical scroll to align the scroll orientation
   2911                     // with AbsListView.
   2912                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
   2913                 } else {
   2914                     vScroll = 0f;
   2915                 }
   2916                 if (mLayout.canScrollHorizontally()) {
   2917                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
   2918                 } else {
   2919                     hScroll = 0f;
   2920                 }
   2921 
   2922                 if (vScroll != 0 || hScroll != 0) {
   2923                     final float scrollFactor = getScrollFactor();
   2924                     scrollByInternal((int) (hScroll * scrollFactor),
   2925                             (int) (vScroll * scrollFactor), event);
   2926                 }
   2927             }
   2928         }
   2929         return false;
   2930     }
   2931 
   2932     /**
   2933      * Ported from View.getVerticalScrollFactor.
   2934      */
   2935     private float getScrollFactor() {
   2936         if (mScrollFactor == Float.MIN_VALUE) {
   2937             TypedValue outValue = new TypedValue();
   2938             if (getContext().getTheme().resolveAttribute(
   2939                     android.R.attr.listPreferredItemHeight, outValue, true)) {
   2940                 mScrollFactor = outValue.getDimension(
   2941                         getContext().getResources().getDisplayMetrics());
   2942             } else {
   2943                 return 0; //listPreferredItemHeight is not defined, no generic scrolling
   2944             }
   2945         }
   2946         return mScrollFactor;
   2947     }
   2948 
   2949     @Override
   2950     protected void onMeasure(int widthSpec, int heightSpec) {
   2951         if (mLayout == null) {
   2952             defaultOnMeasure(widthSpec, heightSpec);
   2953             return;
   2954         }
   2955         if (mLayout.mAutoMeasure) {
   2956             final int widthMode = MeasureSpec.getMode(widthSpec);
   2957             final int heightMode = MeasureSpec.getMode(heightSpec);
   2958             final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
   2959                     && heightMode == MeasureSpec.EXACTLY;
   2960             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   2961             if (skipMeasure || mAdapter == null) {
   2962                 return;
   2963             }
   2964             if (mState.mLayoutStep == State.STEP_START) {
   2965                 dispatchLayoutStep1();
   2966             }
   2967             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
   2968             // consistency
   2969             mLayout.setMeasureSpecs(widthSpec, heightSpec);
   2970             mState.mIsMeasuring = true;
   2971             dispatchLayoutStep2();
   2972 
   2973             // now we can get the width and height from the children.
   2974             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
   2975 
   2976             // if RecyclerView has non-exact width and height and if there is at least one child
   2977             // which also has non-exact width & height, we have to re-measure.
   2978             if (mLayout.shouldMeasureTwice()) {
   2979                 mLayout.setMeasureSpecs(
   2980                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
   2981                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
   2982                 mState.mIsMeasuring = true;
   2983                 dispatchLayoutStep2();
   2984                 // now we can get the width and height from the children.
   2985                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
   2986             }
   2987         } else {
   2988             if (mHasFixedSize) {
   2989                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   2990                 return;
   2991             }
   2992             // custom onMeasure
   2993             if (mAdapterUpdateDuringMeasure) {
   2994                 eatRequestLayout();
   2995                 onEnterLayoutOrScroll();
   2996                 processAdapterUpdatesAndSetAnimationFlags();
   2997                 onExitLayoutOrScroll();
   2998 
   2999                 if (mState.mRunPredictiveAnimations) {
   3000                     mState.mInPreLayout = true;
   3001                 } else {
   3002                     // consume remaining updates to provide a consistent state with the layout pass.
   3003                     mAdapterHelper.consumeUpdatesInOnePass();
   3004                     mState.mInPreLayout = false;
   3005                 }
   3006                 mAdapterUpdateDuringMeasure = false;
   3007                 resumeRequestLayout(false);
   3008             }
   3009 
   3010             if (mAdapter != null) {
   3011                 mState.mItemCount = mAdapter.getItemCount();
   3012             } else {
   3013                 mState.mItemCount = 0;
   3014             }
   3015             eatRequestLayout();
   3016             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   3017             resumeRequestLayout(false);
   3018             mState.mInPreLayout = false; // clear
   3019         }
   3020     }
   3021 
   3022     /**
   3023      * Used when onMeasure is called before layout manager is set
   3024      */
   3025     void defaultOnMeasure(int widthSpec, int heightSpec) {
   3026         // calling LayoutManager here is not pretty but that API is already public and it is better
   3027         // than creating another method since this is internal.
   3028         final int width = LayoutManager.chooseSize(widthSpec,
   3029                 getPaddingLeft() + getPaddingRight(),
   3030                 getMinimumWidth());
   3031         final int height = LayoutManager.chooseSize(heightSpec,
   3032                 getPaddingTop() + getPaddingBottom(),
   3033                 getMinimumHeight());
   3034 
   3035         setMeasuredDimension(width, height);
   3036     }
   3037 
   3038     @Override
   3039     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   3040         super.onSizeChanged(w, h, oldw, oldh);
   3041         if (w != oldw || h != oldh) {
   3042             invalidateGlows();
   3043             // layout's w/h are updated during measure/layout steps.
   3044         }
   3045     }
   3046 
   3047     /**
   3048      * Sets the {@link ItemAnimator} that will handle animations involving changes
   3049      * to the items in this RecyclerView. By default, RecyclerView instantiates and
   3050      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
   3051      * enabled for the RecyclerView depends on the ItemAnimator and whether
   3052      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
   3053      * supports item animations}.
   3054      *
   3055      * @param animator The ItemAnimator being set. If null, no animations will occur
   3056      * when changes occur to the items in this RecyclerView.
   3057      */
   3058     public void setItemAnimator(ItemAnimator animator) {
   3059         if (mItemAnimator != null) {
   3060             mItemAnimator.endAnimations();
   3061             mItemAnimator.setListener(null);
   3062         }
   3063         mItemAnimator = animator;
   3064         if (mItemAnimator != null) {
   3065             mItemAnimator.setListener(mItemAnimatorListener);
   3066         }
   3067     }
   3068 
   3069     void onEnterLayoutOrScroll() {
   3070         mLayoutOrScrollCounter++;
   3071     }
   3072 
   3073     void onExitLayoutOrScroll() {
   3074         mLayoutOrScrollCounter--;
   3075         if (mLayoutOrScrollCounter < 1) {
   3076             if (DEBUG && mLayoutOrScrollCounter < 0) {
   3077                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
   3078                         + "Some calls are not matching");
   3079             }
   3080             mLayoutOrScrollCounter = 0;
   3081             dispatchContentChangedIfNecessary();
   3082             dispatchPendingImportantForAccessibilityChanges();
   3083         }
   3084     }
   3085 
   3086     boolean isAccessibilityEnabled() {
   3087         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
   3088     }
   3089 
   3090     private void dispatchContentChangedIfNecessary() {
   3091         final int flags = mEatenAccessibilityChangeFlags;
   3092         mEatenAccessibilityChangeFlags = 0;
   3093         if (flags != 0 && isAccessibilityEnabled()) {
   3094             final AccessibilityEvent event = AccessibilityEvent.obtain();
   3095             event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
   3096             event.setContentChangeTypes(flags);
   3097             sendAccessibilityEventUnchecked(event);
   3098         }
   3099     }
   3100 
   3101     /**
   3102      * Returns whether RecyclerView is currently computing a layout.
   3103      * <p>
   3104      * If this method returns true, it means that RecyclerView is in a lockdown state and any
   3105      * attempt to update adapter contents will result in an exception because adapter contents
   3106      * cannot be changed while RecyclerView is trying to compute the layout.
   3107      * <p>
   3108      * It is very unlikely that your code will be running during this state as it is
   3109      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
   3110      * in response to system events (touch, accessibility etc).
   3111      * <p>
   3112      * This case may happen if you have some custom logic to change adapter contents in
   3113      * response to a View callback (e.g. focus change callback) which might be triggered during a
   3114      * layout calculation. In these cases, you should just postpone the change using a Handler or a
   3115      * similar mechanism.
   3116      *
   3117      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
   3118      *         otherwise
   3119      */
   3120     public boolean isComputingLayout() {
   3121         return mLayoutOrScrollCounter > 0;
   3122     }
   3123 
   3124     /**
   3125      * Returns true if an accessibility event should not be dispatched now. This happens when an
   3126      * accessibility request arrives while RecyclerView does not have a stable state which is very
   3127      * hard to handle for a LayoutManager. Instead, this method records necessary information about
   3128      * the event and dispatches a window change event after the critical section is finished.
   3129      *
   3130      * @return True if the accessibility event should be postponed.
   3131      */
   3132     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
   3133         if (isComputingLayout()) {
   3134             int type = 0;
   3135             if (event != null) {
   3136                 type = event.getContentChangeTypes();
   3137             }
   3138             if (type == 0) {
   3139                 type = AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
   3140             }
   3141             mEatenAccessibilityChangeFlags |= type;
   3142             return true;
   3143         }
   3144         return false;
   3145     }
   3146 
   3147     @Override
   3148     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
   3149         if (shouldDeferAccessibilityEvent(event)) {
   3150             return;
   3151         }
   3152         super.sendAccessibilityEventUnchecked(event);
   3153     }
   3154 
   3155     /**
   3156      * Gets the current ItemAnimator for this RecyclerView. A null return value
   3157      * indicates that there is no animator and that item changes will happen without
   3158      * any animations. By default, RecyclerView instantiates and
   3159      * uses an instance of {@link DefaultItemAnimator}.
   3160      *
   3161      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
   3162      * when changes occur to the items in this RecyclerView.
   3163      */
   3164     public ItemAnimator getItemAnimator() {
   3165         return mItemAnimator;
   3166     }
   3167 
   3168     /**
   3169      * Post a runnable to the next frame to run pending item animations. Only the first such
   3170      * request will be posted, governed by the mPostedAnimatorRunner flag.
   3171      */
   3172     void postAnimationRunner() {
   3173         if (!mPostedAnimatorRunner && mIsAttached) {
   3174             postOnAnimation(mItemAnimatorRunner);
   3175             mPostedAnimatorRunner = true;
   3176         }
   3177     }
   3178 
   3179     private boolean predictiveItemAnimationsEnabled() {
   3180         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
   3181     }
   3182 
   3183     /**
   3184      * Consumes adapter updates and calculates which type of animations we want to run.
   3185      * Called in onMeasure and dispatchLayout.
   3186      * <p>
   3187      * This method may process only the pre-layout state of updates or all of them.
   3188      */
   3189     private void processAdapterUpdatesAndSetAnimationFlags() {
   3190         if (mDataSetHasChangedAfterLayout) {
   3191             // Processing these items have no value since data set changed unexpectedly.
   3192             // Instead, we just reset it.
   3193             mAdapterHelper.reset();
   3194             mLayout.onItemsChanged(this);
   3195         }
   3196         // simple animations are a subset of advanced animations (which will cause a
   3197         // pre-layout step)
   3198         // If layout supports predictive animations, pre-process to decide if we want to run them
   3199         if (predictiveItemAnimationsEnabled()) {
   3200             mAdapterHelper.preProcess();
   3201         } else {
   3202             mAdapterHelper.consumeUpdatesInOnePass();
   3203         }
   3204         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
   3205         mState.mRunSimpleAnimations = mFirstLayoutComplete
   3206                 && mItemAnimator != null
   3207                 && (mDataSetHasChangedAfterLayout
   3208                         || animationTypeSupported
   3209                         || mLayout.mRequestedSimpleAnimations)
   3210                 && (!mDataSetHasChangedAfterLayout
   3211                         || mAdapter.hasStableIds());
   3212         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
   3213                 && animationTypeSupported
   3214                 && !mDataSetHasChangedAfterLayout
   3215                 && predictiveItemAnimationsEnabled();
   3216     }
   3217 
   3218     /**
   3219      * Wrapper around layoutChildren() that handles animating changes caused by layout.
   3220      * Animations work on the assumption that there are five different kinds of items
   3221      * in play:
   3222      * PERSISTENT: items are visible before and after layout
   3223      * REMOVED: items were visible before layout and were removed by the app
   3224      * ADDED: items did not exist before layout and were added by the app
   3225      * DISAPPEARING: items exist in the data set before/after, but changed from
   3226      * visible to non-visible in the process of layout (they were moved off
   3227      * screen as a side-effect of other changes)
   3228      * APPEARING: items exist in the data set before/after, but changed from
   3229      * non-visible to visible in the process of layout (they were moved on
   3230      * screen as a side-effect of other changes)
   3231      * The overall approach figures out what items exist before/after layout and
   3232      * infers one of the five above states for each of the items. Then the animations
   3233      * are set up accordingly:
   3234      * PERSISTENT views are animated via
   3235      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3236      * DISAPPEARING views are animated via
   3237      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3238      * APPEARING views are animated via
   3239      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3240      * and changed views are animated via
   3241      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
   3242      */
   3243     void dispatchLayout() {
   3244         if (mAdapter == null) {
   3245             Log.e(TAG, "No adapter attached; skipping layout");
   3246             // leave the state in START
   3247             return;
   3248         }
   3249         if (mLayout == null) {
   3250             Log.e(TAG, "No layout manager attached; skipping layout");
   3251             // leave the state in START
   3252             return;
   3253         }
   3254         mState.mIsMeasuring = false;
   3255         if (mState.mLayoutStep == State.STEP_START) {
   3256             dispatchLayoutStep1();
   3257             mLayout.setExactMeasureSpecsFrom(this);
   3258             dispatchLayoutStep2();
   3259         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
   3260                 || mLayout.getHeight() != getHeight()) {
   3261             // First 2 steps are done in onMeasure but looks like we have to run again due to
   3262             // changed size.
   3263             mLayout.setExactMeasureSpecsFrom(this);
   3264             dispatchLayoutStep2();
   3265         } else {
   3266             // always make sure we sync them (to ensure mode is exact)
   3267             mLayout.setExactMeasureSpecsFrom(this);
   3268         }
   3269         dispatchLayoutStep3();
   3270     }
   3271 
   3272     private void saveFocusInfo() {
   3273         View child = null;
   3274         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
   3275             child = getFocusedChild();
   3276         }
   3277 
   3278         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
   3279         if (focusedVh == null) {
   3280             resetFocusInfo();
   3281         } else {
   3282             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
   3283             // mFocusedItemPosition should hold the current adapter position of the previously
   3284             // focused item. If the item is removed, we store the previous adapter position of the
   3285             // removed item.
   3286             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
   3287                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
   3288                             : focusedVh.getAdapterPosition());
   3289             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
   3290         }
   3291     }
   3292 
   3293     private void resetFocusInfo() {
   3294         mState.mFocusedItemId = NO_ID;
   3295         mState.mFocusedItemPosition = NO_POSITION;
   3296         mState.mFocusedSubChildId = View.NO_ID;
   3297     }
   3298 
   3299     /**
   3300      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
   3301      * previously focused item. It first traverses the adapter forward to find a focusable candidate
   3302      * and if no such candidate is found, it reverses the focus search direction for the items
   3303      * before the mFocusedItemPosition'th index;
   3304      * @return The best candidate to request focus on, or null if no such candidate exists. Null
   3305      * indicates all the existing adapter items are unfocusable.
   3306      */
   3307     @Nullable
   3308     private View findNextViewToFocus() {
   3309         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
   3310                 : 0;
   3311         ViewHolder nextFocus;
   3312         final int itemCount = mState.getItemCount();
   3313         for (int i = startFocusSearchIndex; i < itemCount; i++) {
   3314             nextFocus = findViewHolderForAdapterPosition(i);
   3315             if (nextFocus == null) {
   3316                 break;
   3317             }
   3318             if (nextFocus.itemView.hasFocusable()) {
   3319                 return nextFocus.itemView;
   3320             }
   3321         }
   3322         final int limit = Math.min(itemCount, startFocusSearchIndex);
   3323         for (int i = limit - 1; i >= 0; i--) {
   3324             nextFocus = findViewHolderForAdapterPosition(i);
   3325             if (nextFocus == null) {
   3326                 return null;
   3327             }
   3328             if (nextFocus.itemView.hasFocusable()) {
   3329                 return nextFocus.itemView;
   3330             }
   3331         }
   3332         return null;
   3333     }
   3334 
   3335     private void recoverFocusFromState() {
   3336         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
   3337                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
   3338                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
   3339             // No-op if either of these cases happens:
   3340             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
   3341             // before its children and is focused (i.e. it already stole the focus away from its
   3342             // descendants).
   3343             return;
   3344         }
   3345         // only recover focus if RV itself has the focus or the focused view is hidden
   3346         if (!isFocused()) {
   3347             final View focusedChild = getFocusedChild();
   3348             if (IGNORE_DETACHED_FOCUSED_CHILD
   3349                     && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
   3350                 // Special handling of API 15-. A focused child can be invalid because mFocus is not
   3351                 // cleared when the child is detached (mParent = null),
   3352                 // This happens because clearFocus on API 15- does not invalidate mFocus of its
   3353                 // parent when this child is detached.
   3354                 // For API 16+, this is not an issue because requestFocus takes care of clearing the
   3355                 // prior detached focused child. For API 15- the problem happens in 2 cases because
   3356                 // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
   3357                 // for the current focused item which calls clearChild or 2. when the prior focused
   3358                 // child is removed, removeDetachedView called in layout step 3 which calls
   3359                 // clearChild. We should ignore this invalid focused child in all our calculations
   3360                 // for the next view to receive focus, and apply the focus recovery logic instead.
   3361                 if (mChildHelper.getChildCount() == 0) {
   3362                     // No children left. Request focus on the RV itself since one of its children
   3363                     // was holding focus previously.
   3364                     requestFocus();
   3365                     return;
   3366                 }
   3367             } else if (!mChildHelper.isHidden(focusedChild)) {
   3368                 // If the currently focused child is hidden, apply the focus recovery logic.
   3369                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
   3370                 return;
   3371             }
   3372         }
   3373         ViewHolder focusTarget = null;
   3374         // RV first attempts to locate the previously focused item to request focus on using
   3375         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
   3376         // find the next best candidate to request focus on based on mFocusedItemPosition.
   3377         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
   3378             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
   3379         }
   3380         View viewToFocus = null;
   3381         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
   3382                 || !focusTarget.itemView.hasFocusable()) {
   3383             if (mChildHelper.getChildCount() > 0) {
   3384                 // At this point, RV has focus and either of these conditions are true:
   3385                 // 1. There's no previously focused item either because RV received focused before
   3386                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
   3387                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
   3388                 // focusable. In either of these cases, we make sure that RV still passes down the
   3389                 // focus to one of its focusable children using a best-effort algorithm.
   3390                 viewToFocus = findNextViewToFocus();
   3391             }
   3392         } else {
   3393             // looks like the focused item has been replaced with another view that represents the
   3394             // same item in the adapter. Request focus on that.
   3395             viewToFocus = focusTarget.itemView;
   3396         }
   3397 
   3398         if (viewToFocus != null) {
   3399             if (mState.mFocusedSubChildId != NO_ID) {
   3400                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
   3401                 if (child != null && child.isFocusable()) {
   3402                     viewToFocus = child;
   3403                 }
   3404             }
   3405             viewToFocus.requestFocus();
   3406         }
   3407     }
   3408 
   3409     private int getDeepestFocusedViewWithId(View view) {
   3410         int lastKnownId = view.getId();
   3411         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
   3412             view = ((ViewGroup) view).getFocusedChild();
   3413             final int id = view.getId();
   3414             if (id != View.NO_ID) {
   3415                 lastKnownId = view.getId();
   3416             }
   3417         }
   3418         return lastKnownId;
   3419     }
   3420 
   3421     /**
   3422      * The first step of a layout where we;
   3423      * - process adapter updates
   3424      * - decide which animation should run
   3425      * - save information about current views
   3426      * - If necessary, run predictive layout and save its information
   3427      */
   3428     private void dispatchLayoutStep1() {
   3429         mState.assertLayoutStep(State.STEP_START);
   3430         mState.mIsMeasuring = false;
   3431         eatRequestLayout();
   3432         mViewInfoStore.clear();
   3433         onEnterLayoutOrScroll();
   3434         processAdapterUpdatesAndSetAnimationFlags();
   3435         saveFocusInfo();
   3436         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
   3437         mItemsAddedOrRemoved = mItemsChanged = false;
   3438         mState.mInPreLayout = mState.mRunPredictiveAnimations;
   3439         mState.mItemCount = mAdapter.getItemCount();
   3440         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   3441 
   3442         if (mState.mRunSimpleAnimations) {
   3443             // Step 0: Find out where all non-removed items are, pre-layout
   3444             int count = mChildHelper.getChildCount();
   3445             for (int i = 0; i < count; ++i) {
   3446                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3447                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
   3448                     continue;
   3449                 }
   3450                 final ItemHolderInfo animationInfo = mItemAnimator
   3451                         .recordPreLayoutInformation(mState, holder,
   3452                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
   3453                                 holder.getUnmodifiedPayloads());
   3454                 mViewInfoStore.addToPreLayout(holder, animationInfo);
   3455                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
   3456                         && !holder.shouldIgnore() && !holder.isInvalid()) {
   3457                     long key = getChangedHolderKey(holder);
   3458                     // This is NOT the only place where a ViewHolder is added to old change holders
   3459                     // list. There is another case where:
   3460                     //    * A VH is currently hidden but not deleted
   3461                     //    * The hidden item is changed in the adapter
   3462                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
   3463                     // When this case is detected, RV will un-hide that view and add to the old
   3464                     // change holders list.
   3465                     mViewInfoStore.addToOldChangeHolders(key, holder);
   3466                 }
   3467             }
   3468         }
   3469         if (mState.mRunPredictiveAnimations) {
   3470             // Step 1: run prelayout: This will use the old positions of items. The layout manager
   3471             // is expected to layout everything, even removed items (though not to add removed
   3472             // items back to the container). This gives the pre-layout position of APPEARING views
   3473             // which come into existence as part of the real layout.
   3474 
   3475             // Save old positions so that LayoutManager can run its mapping logic.
   3476             saveOldPositions();
   3477             final boolean didStructureChange = mState.mStructureChanged;
   3478             mState.mStructureChanged = false;
   3479             // temporarily disable flag because we are asking for previous layout
   3480             mLayout.onLayoutChildren(mRecycler, mState);
   3481             mState.mStructureChanged = didStructureChange;
   3482 
   3483             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
   3484                 final View child = mChildHelper.getChildAt(i);
   3485                 final ViewHolder viewHolder = getChildViewHolderInt(child);
   3486                 if (viewHolder.shouldIgnore()) {
   3487                     continue;
   3488                 }
   3489                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
   3490                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
   3491                     boolean wasHidden = viewHolder
   3492                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   3493                     if (!wasHidden) {
   3494                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
   3495                     }
   3496                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
   3497                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
   3498                     if (wasHidden) {
   3499                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
   3500                     } else {
   3501                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
   3502                     }
   3503                 }
   3504             }
   3505             // we don't process disappearing list because they may re-appear in post layout pass.
   3506             clearOldPositions();
   3507         } else {
   3508             clearOldPositions();
   3509         }
   3510         onExitLayoutOrScroll();
   3511         resumeRequestLayout(false);
   3512         mState.mLayoutStep = State.STEP_LAYOUT;
   3513     }
   3514 
   3515     /**
   3516      * The second layout step where we do the actual layout of the views for the final state.
   3517      * This step might be run multiple times if necessary (e.g. measure).
   3518      */
   3519     private void dispatchLayoutStep2() {
   3520         eatRequestLayout();
   3521         onEnterLayoutOrScroll();
   3522         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
   3523         mAdapterHelper.consumeUpdatesInOnePass();
   3524         mState.mItemCount = mAdapter.getItemCount();
   3525         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
   3526 
   3527         // Step 2: Run layout
   3528         mState.mInPreLayout = false;
   3529         mLayout.onLayoutChildren(mRecycler, mState);
   3530 
   3531         mState.mStructureChanged = false;
   3532         mPendingSavedState = null;
   3533 
   3534         // onLayoutChildren may have caused client code to disable item animations; re-check
   3535         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
   3536         mState.mLayoutStep = State.STEP_ANIMATIONS;
   3537         onExitLayoutOrScroll();
   3538         resumeRequestLayout(false);
   3539     }
   3540 
   3541     /**
   3542      * The final step of the layout where we save the information about views for animations,
   3543      * trigger animations and do any necessary cleanup.
   3544      */
   3545     private void dispatchLayoutStep3() {
   3546         mState.assertLayoutStep(State.STEP_ANIMATIONS);
   3547         eatRequestLayout();
   3548         onEnterLayoutOrScroll();
   3549         mState.mLayoutStep = State.STEP_START;
   3550         if (mState.mRunSimpleAnimations) {
   3551             // Step 3: Find out where things are now, and process change animations.
   3552             // traverse list in reverse because we may call animateChange in the loop which may
   3553             // remove the target view holder.
   3554             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
   3555                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3556                 if (holder.shouldIgnore()) {
   3557                     continue;
   3558                 }
   3559                 long key = getChangedHolderKey(holder);
   3560                 final ItemHolderInfo animationInfo = mItemAnimator
   3561                         .recordPostLayoutInformation(mState, holder);
   3562                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
   3563                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
   3564                     // run a change animation
   3565 
   3566                     // If an Item is CHANGED but the updated version is disappearing, it creates
   3567                     // a conflicting case.
   3568                     // Since a view that is marked as disappearing is likely to be going out of
   3569                     // bounds, we run a change animation. Both views will be cleaned automatically
   3570                     // once their animations finish.
   3571                     // On the other hand, if it is the same view holder instance, we run a
   3572                     // disappearing animation instead because we are not going to rebind the updated
   3573                     // VH unless it is enforced by the layout manager.
   3574                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
   3575                             oldChangeViewHolder);
   3576                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
   3577                     if (oldDisappearing && oldChangeViewHolder == holder) {
   3578                         // run disappear animation instead of change
   3579                         mViewInfoStore.addToPostLayout(holder, animationInfo);
   3580                     } else {
   3581                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
   3582                                 oldChangeViewHolder);
   3583                         // we add and remove so that any post info is merged.
   3584                         mViewInfoStore.addToPostLayout(holder, animationInfo);
   3585                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
   3586                         if (preInfo == null) {
   3587                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
   3588                         } else {
   3589                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
   3590                                     oldDisappearing, newDisappearing);
   3591                         }
   3592                     }
   3593                 } else {
   3594                     mViewInfoStore.addToPostLayout(holder, animationInfo);
   3595                 }
   3596             }
   3597 
   3598             // Step 4: Process view info lists and trigger animations
   3599             mViewInfoStore.process(mViewInfoProcessCallback);
   3600         }
   3601 
   3602         mLayout.removeAndRecycleScrapInt(mRecycler);
   3603         mState.mPreviousLayoutItemCount = mState.mItemCount;
   3604         mDataSetHasChangedAfterLayout = false;
   3605         mState.mRunSimpleAnimations = false;
   3606 
   3607         mState.mRunPredictiveAnimations = false;
   3608         mLayout.mRequestedSimpleAnimations = false;
   3609         if (mRecycler.mChangedScrap != null) {
   3610             mRecycler.mChangedScrap.clear();
   3611         }
   3612         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
   3613             // Initial prefetch has expanded cache, so reset until next prefetch.
   3614             // This prevents initial prefetches from expanding the cache permanently.
   3615             mLayout.mPrefetchMaxCountObserved = 0;
   3616             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
   3617             mRecycler.updateViewCacheSize();
   3618         }
   3619 
   3620         mLayout.onLayoutCompleted(mState);
   3621         onExitLayoutOrScroll();
   3622         resumeRequestLayout(false);
   3623         mViewInfoStore.clear();
   3624         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
   3625             dispatchOnScrolled(0, 0);
   3626         }
   3627         recoverFocusFromState();
   3628         resetFocusInfo();
   3629     }
   3630 
   3631     /**
   3632      * This handles the case where there is an unexpected VH missing in the pre-layout map.
   3633      * <p>
   3634      * We might be able to detect the error in the application which will help the developer to
   3635      * resolve the issue.
   3636      * <p>
   3637      * If it is not an expected error, we at least print an error to notify the developer and ignore
   3638      * the animation.
   3639      *
   3640      * https://code.google.com/p/android/issues/detail?id=193958
   3641      *
   3642      * @param key The change key
   3643      * @param holder Current ViewHolder
   3644      * @param oldChangeViewHolder Changed ViewHolder
   3645      */
   3646     private void handleMissingPreInfoForChangeError(long key,
   3647             ViewHolder holder, ViewHolder oldChangeViewHolder) {
   3648         // check if two VH have the same key, if so, print that as an error
   3649         final int childCount = mChildHelper.getChildCount();
   3650         for (int i = 0; i < childCount; i++) {
   3651             View view = mChildHelper.getChildAt(i);
   3652             ViewHolder other = getChildViewHolderInt(view);
   3653             if (other == holder) {
   3654                 continue;
   3655             }
   3656             final long otherKey = getChangedHolderKey(other);
   3657             if (otherKey == key) {
   3658                 if (mAdapter != null && mAdapter.hasStableIds()) {
   3659                     throw new IllegalStateException("Two different ViewHolders have the same stable"
   3660                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
   3661                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
   3662                 } else {
   3663                     throw new IllegalStateException("Two different ViewHolders have the same change"
   3664                             + " ID. This might happen due to inconsistent Adapter update events or"
   3665                             + " if the LayoutManager lays out the same View multiple times."
   3666                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder);
   3667                 }
   3668             }
   3669         }
   3670         // Very unlikely to happen but if it does, notify the developer.
   3671         Log.e(TAG, "Problem while matching changed view holders with the new"
   3672                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
   3673                 + " cannot be found but it is necessary for " + holder);
   3674     }
   3675 
   3676     /**
   3677      * Records the animation information for a view holder that was bounced from hidden list. It
   3678      * also clears the bounce back flag.
   3679      */
   3680     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
   3681             ItemHolderInfo animationInfo) {
   3682         // looks like this view bounced back from hidden list!
   3683         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   3684         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
   3685                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
   3686             long key = getChangedHolderKey(viewHolder);
   3687             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
   3688         }
   3689         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
   3690     }
   3691 
   3692     private void findMinMaxChildLayoutPositions(int[] into) {
   3693         final int count = mChildHelper.getChildCount();
   3694         if (count == 0) {
   3695             into[0] = NO_POSITION;
   3696             into[1] = NO_POSITION;
   3697             return;
   3698         }
   3699         int minPositionPreLayout = Integer.MAX_VALUE;
   3700         int maxPositionPreLayout = Integer.MIN_VALUE;
   3701         for (int i = 0; i < count; ++i) {
   3702             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3703             if (holder.shouldIgnore()) {
   3704                 continue;
   3705             }
   3706             final int pos = holder.getLayoutPosition();
   3707             if (pos < minPositionPreLayout) {
   3708                 minPositionPreLayout = pos;
   3709             }
   3710             if (pos > maxPositionPreLayout) {
   3711                 maxPositionPreLayout = pos;
   3712             }
   3713         }
   3714         into[0] = minPositionPreLayout;
   3715         into[1] = maxPositionPreLayout;
   3716     }
   3717 
   3718     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
   3719         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   3720         return mMinMaxLayoutPositions[0] != minPositionPreLayout
   3721                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
   3722     }
   3723 
   3724     @Override
   3725     protected void removeDetachedView(View child, boolean animate) {
   3726         ViewHolder vh = getChildViewHolderInt(child);
   3727         if (vh != null) {
   3728             if (vh.isTmpDetached()) {
   3729                 vh.clearTmpDetachFlag();
   3730             } else if (!vh.shouldIgnore()) {
   3731                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
   3732                         + " is not flagged as tmp detached." + vh);
   3733             }
   3734         }
   3735         dispatchChildDetached(child);
   3736         super.removeDetachedView(child, animate);
   3737     }
   3738 
   3739     /**
   3740      * Returns a unique key to be used while handling change animations.
   3741      * It might be child's position or stable id depending on the adapter type.
   3742      */
   3743     long getChangedHolderKey(ViewHolder holder) {
   3744         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
   3745     }
   3746 
   3747     void animateAppearance(@NonNull ViewHolder itemHolder,
   3748             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
   3749         itemHolder.setIsRecyclable(false);
   3750         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
   3751             postAnimationRunner();
   3752         }
   3753     }
   3754 
   3755     void animateDisappearance(@NonNull ViewHolder holder,
   3756             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
   3757         addAnimatingView(holder);
   3758         holder.setIsRecyclable(false);
   3759         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
   3760             postAnimationRunner();
   3761         }
   3762     }
   3763 
   3764     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
   3765             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
   3766             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
   3767         oldHolder.setIsRecyclable(false);
   3768         if (oldHolderDisappearing) {
   3769             addAnimatingView(oldHolder);
   3770         }
   3771         if (oldHolder != newHolder) {
   3772             if (newHolderDisappearing) {
   3773                 addAnimatingView(newHolder);
   3774             }
   3775             oldHolder.mShadowedHolder = newHolder;
   3776             // old holder should disappear after animation ends
   3777             addAnimatingView(oldHolder);
   3778             mRecycler.unscrapView(oldHolder);
   3779             newHolder.setIsRecyclable(false);
   3780             newHolder.mShadowingHolder = oldHolder;
   3781         }
   3782         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
   3783             postAnimationRunner();
   3784         }
   3785     }
   3786 
   3787     @Override
   3788     protected void onLayout(boolean changed, int l, int t, int r, int b) {
   3789         Trace.beginSection(TRACE_ON_LAYOUT_TAG);
   3790         dispatchLayout();
   3791         Trace.endSection();
   3792         mFirstLayoutComplete = true;
   3793     }
   3794 
   3795     @Override
   3796     public void requestLayout() {
   3797         if (mEatRequestLayout == 0 && !mLayoutFrozen) {
   3798             super.requestLayout();
   3799         } else {
   3800             mLayoutRequestEaten = true;
   3801         }
   3802     }
   3803 
   3804     void markItemDecorInsetsDirty() {
   3805         final int childCount = mChildHelper.getUnfilteredChildCount();
   3806         for (int i = 0; i < childCount; i++) {
   3807             final View child = mChildHelper.getUnfilteredChildAt(i);
   3808             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   3809         }
   3810         mRecycler.markItemDecorInsetsDirty();
   3811     }
   3812 
   3813     @Override
   3814     public void draw(Canvas c) {
   3815         super.draw(c);
   3816 
   3817         final int count = mItemDecorations.size();
   3818         for (int i = 0; i < count; i++) {
   3819             mItemDecorations.get(i).onDrawOver(c, this, mState);
   3820         }
   3821         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
   3822         // need find children closest to edges. Not sure if it is worth the effort.
   3823         boolean needsInvalidate = false;
   3824         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
   3825             final int restore = c.save();
   3826             final int padding = mClipToPadding ? getPaddingBottom() : 0;
   3827             c.rotate(270);
   3828             c.translate(-getHeight() + padding, 0);
   3829             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
   3830             c.restoreToCount(restore);
   3831         }
   3832         if (mTopGlow != null && !mTopGlow.isFinished()) {
   3833             final int restore = c.save();
   3834             if (mClipToPadding) {
   3835                 c.translate(getPaddingLeft(), getPaddingTop());
   3836             }
   3837             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
   3838             c.restoreToCount(restore);
   3839         }
   3840         if (mRightGlow != null && !mRightGlow.isFinished()) {
   3841             final int restore = c.save();
   3842             final int width = getWidth();
   3843             final int padding = mClipToPadding ? getPaddingTop() : 0;
   3844             c.rotate(90);
   3845             c.translate(-padding, -width);
   3846             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
   3847             c.restoreToCount(restore);
   3848         }
   3849         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
   3850             final int restore = c.save();
   3851             c.rotate(180);
   3852             if (mClipToPadding) {
   3853                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
   3854             } else {
   3855                 c.translate(-getWidth(), -getHeight());
   3856             }
   3857             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
   3858             c.restoreToCount(restore);
   3859         }
   3860 
   3861         // If some views are animating, ItemDecorators are likely to move/change with them.
   3862         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
   3863         // display lists are not invalidated.
   3864         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
   3865                 && mItemAnimator.isRunning()) {
   3866             needsInvalidate = true;
   3867         }
   3868 
   3869         if (needsInvalidate) {
   3870             postInvalidateOnAnimation();
   3871         }
   3872     }
   3873 
   3874     @Override
   3875     public void onDraw(Canvas c) {
   3876         super.onDraw(c);
   3877 
   3878         final int count = mItemDecorations.size();
   3879         for (int i = 0; i < count; i++) {
   3880             mItemDecorations.get(i).onDraw(c, this, mState);
   3881         }
   3882     }
   3883 
   3884     @Override
   3885     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
   3886         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
   3887     }
   3888 
   3889     @Override
   3890     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
   3891         if (mLayout == null) {
   3892             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3893         }
   3894         return mLayout.generateDefaultLayoutParams();
   3895     }
   3896 
   3897     @Override
   3898     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
   3899         if (mLayout == null) {
   3900             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3901         }
   3902         return mLayout.generateLayoutParams(getContext(), attrs);
   3903     }
   3904 
   3905     @Override
   3906     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
   3907         if (mLayout == null) {
   3908             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3909         }
   3910         return mLayout.generateLayoutParams(p);
   3911     }
   3912 
   3913     /**
   3914      * Returns true if RecyclerView is currently running some animations.
   3915      * <p>
   3916      * If you want to be notified when animations are finished, use
   3917      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
   3918      *
   3919      * @return True if there are some item animations currently running or waiting to be started.
   3920      */
   3921     public boolean isAnimating() {
   3922         return mItemAnimator != null && mItemAnimator.isRunning();
   3923     }
   3924 
   3925     void saveOldPositions() {
   3926         final int childCount = mChildHelper.getUnfilteredChildCount();
   3927         for (int i = 0; i < childCount; i++) {
   3928             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3929             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
   3930                 throw new IllegalStateException("view holder cannot have position -1 unless it"
   3931                         + " is removed");
   3932             }
   3933             if (!holder.shouldIgnore()) {
   3934                 holder.saveOldPosition();
   3935             }
   3936         }
   3937     }
   3938 
   3939     void clearOldPositions() {
   3940         final int childCount = mChildHelper.getUnfilteredChildCount();
   3941         for (int i = 0; i < childCount; i++) {
   3942             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3943             if (!holder.shouldIgnore()) {
   3944                 holder.clearOldPosition();
   3945             }
   3946         }
   3947         mRecycler.clearOldPositions();
   3948     }
   3949 
   3950     void offsetPositionRecordsForMove(int from, int to) {
   3951         final int childCount = mChildHelper.getUnfilteredChildCount();
   3952         final int start, end, inBetweenOffset;
   3953         if (from < to) {
   3954             start = from;
   3955             end = to;
   3956             inBetweenOffset = -1;
   3957         } else {
   3958             start = to;
   3959             end = from;
   3960             inBetweenOffset = 1;
   3961         }
   3962 
   3963         for (int i = 0; i < childCount; i++) {
   3964             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3965             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   3966                 continue;
   3967             }
   3968             if (DEBUG) {
   3969                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
   3970                         + holder);
   3971             }
   3972             if (holder.mPosition == from) {
   3973                 holder.offsetPosition(to - from, false);
   3974             } else {
   3975                 holder.offsetPosition(inBetweenOffset, false);
   3976             }
   3977 
   3978             mState.mStructureChanged = true;
   3979         }
   3980         mRecycler.offsetPositionRecordsForMove(from, to);
   3981         requestLayout();
   3982     }
   3983 
   3984     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
   3985         final int childCount = mChildHelper.getUnfilteredChildCount();
   3986         for (int i = 0; i < childCount; i++) {
   3987             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3988             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
   3989                 if (DEBUG) {
   3990                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
   3991                             + holder + " now at position " + (holder.mPosition + itemCount));
   3992                 }
   3993                 holder.offsetPosition(itemCount, false);
   3994                 mState.mStructureChanged = true;
   3995             }
   3996         }
   3997         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
   3998         requestLayout();
   3999     }
   4000 
   4001     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
   4002             boolean applyToPreLayout) {
   4003         final int positionEnd = positionStart + itemCount;
   4004         final int childCount = mChildHelper.getUnfilteredChildCount();
   4005         for (int i = 0; i < childCount; i++) {
   4006             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4007             if (holder != null && !holder.shouldIgnore()) {
   4008                 if (holder.mPosition >= positionEnd) {
   4009                     if (DEBUG) {
   4010                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
   4011                                 + " holder " + holder + " now at position "
   4012                                 + (holder.mPosition - itemCount));
   4013                     }
   4014                     holder.offsetPosition(-itemCount, applyToPreLayout);
   4015                     mState.mStructureChanged = true;
   4016                 } else if (holder.mPosition >= positionStart) {
   4017                     if (DEBUG) {
   4018                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
   4019                                 + " holder " + holder + " now REMOVED");
   4020                     }
   4021                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
   4022                             applyToPreLayout);
   4023                     mState.mStructureChanged = true;
   4024                 }
   4025             }
   4026         }
   4027         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
   4028         requestLayout();
   4029     }
   4030 
   4031     /**
   4032      * Rebind existing views for the given range, or create as needed.
   4033      *
   4034      * @param positionStart Adapter position to start at
   4035      * @param itemCount Number of views that must explicitly be rebound
   4036      */
   4037     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
   4038         final int childCount = mChildHelper.getUnfilteredChildCount();
   4039         final int positionEnd = positionStart + itemCount;
   4040 
   4041         for (int i = 0; i < childCount; i++) {
   4042             final View child = mChildHelper.getUnfilteredChildAt(i);
   4043             final ViewHolder holder = getChildViewHolderInt(child);
   4044             if (holder == null || holder.shouldIgnore()) {
   4045                 continue;
   4046             }
   4047             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
   4048                 // We re-bind these view holders after pre-processing is complete so that
   4049                 // ViewHolders have their final positions assigned.
   4050                 holder.addFlags(ViewHolder.FLAG_UPDATE);
   4051                 holder.addChangePayload(payload);
   4052                 // lp cannot be null since we get ViewHolder from it.
   4053                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   4054             }
   4055         }
   4056         mRecycler.viewRangeUpdate(positionStart, itemCount);
   4057     }
   4058 
   4059     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
   4060         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
   4061                 viewHolder.getUnmodifiedPayloads());
   4062     }
   4063 
   4064 
   4065     /**
   4066      * Call this method to signal that *all* adapter content has changed (generally, because of
   4067      * swapAdapter, or notifyDataSetChanged), and that once layout occurs, all attached items should
   4068      * be discarded or animated. Note that this work is deferred because RecyclerView requires a
   4069      * layout to resolve non-incremental changes to the data set.
   4070      *
   4071      * Attached items are labeled as position unknown, and may no longer be cached.
   4072      *
   4073      * It is still possible for items to be prefetched while mDataSetHasChangedAfterLayout == true,
   4074      * so calling this method *must* be associated with marking the cache invalid, so that the
   4075      * only valid items that remain in the cache, once layout occurs, are prefetched items.
   4076      */
   4077     void setDataSetChangedAfterLayout() {
   4078         if (mDataSetHasChangedAfterLayout) {
   4079             return;
   4080         }
   4081         mDataSetHasChangedAfterLayout = true;
   4082         final int childCount = mChildHelper.getUnfilteredChildCount();
   4083         for (int i = 0; i < childCount; i++) {
   4084             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4085             if (holder != null && !holder.shouldIgnore()) {
   4086                 holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   4087             }
   4088         }
   4089         mRecycler.setAdapterPositionsAsUnknown();
   4090 
   4091         // immediately mark all views as invalid, so prefetched views can be
   4092         // differentiated from views bound to previous data set - both in children, and cache
   4093         markKnownViewsInvalid();
   4094     }
   4095 
   4096     /**
   4097      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
   4098      * data change event.
   4099      */
   4100     void markKnownViewsInvalid() {
   4101         final int childCount = mChildHelper.getUnfilteredChildCount();
   4102         for (int i = 0; i < childCount; i++) {
   4103             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4104             if (holder != null && !holder.shouldIgnore()) {
   4105                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   4106             }
   4107         }
   4108         markItemDecorInsetsDirty();
   4109         mRecycler.markKnownViewsInvalid();
   4110     }
   4111 
   4112     /**
   4113      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
   4114      * will trigger a {@link #requestLayout()} call.
   4115      */
   4116     public void invalidateItemDecorations() {
   4117         if (mItemDecorations.size() == 0) {
   4118             return;
   4119         }
   4120         if (mLayout != null) {
   4121             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
   4122                     + " or layout");
   4123         }
   4124         markItemDecorInsetsDirty();
   4125         requestLayout();
   4126     }
   4127 
   4128     /**
   4129      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
   4130      * focus even if the View representing the Item is replaced during a layout calculation.
   4131      * <p>
   4132      * By default, this value is {@code true}.
   4133      *
   4134      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
   4135      * focus.
   4136      *
   4137      * @see #setPreserveFocusAfterLayout(boolean)
   4138      */
   4139     public boolean getPreserveFocusAfterLayout() {
   4140         return mPreserveFocusAfterLayout;
   4141     }
   4142 
   4143     /**
   4144      * Set whether the RecyclerView should try to keep the same Item focused after a layout
   4145      * calculation or not.
   4146      * <p>
   4147      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
   4148      * views may lose focus during a layout calculation as their state changes or they are replaced
   4149      * with another view due to type change or animation. In these cases, RecyclerView can request
   4150      * focus on the new view automatically.
   4151      *
   4152      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
   4153      *                                 layout calculations. Defaults to true.
   4154      *
   4155      * @see #getPreserveFocusAfterLayout()
   4156      */
   4157     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
   4158         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
   4159     }
   4160 
   4161     /**
   4162      * Retrieve the {@link ViewHolder} for the given child view.
   4163      *
   4164      * @param child Child of this RecyclerView to query for its ViewHolder
   4165      * @return The child view's ViewHolder
   4166      */
   4167     public ViewHolder getChildViewHolder(View child) {
   4168         final ViewParent parent = child.getParent();
   4169         if (parent != null && parent != this) {
   4170             throw new IllegalArgumentException("View " + child + " is not a direct child of "
   4171                     + this);
   4172         }
   4173         return getChildViewHolderInt(child);
   4174     }
   4175 
   4176     /**
   4177      * Traverses the ancestors of the given view and returns the item view that contains it and
   4178      * also a direct child of the RecyclerView. This returned view can be used to get the
   4179      * ViewHolder by calling {@link #getChildViewHolder(View)}.
   4180      *
   4181      * @param view The view that is a descendant of the RecyclerView.
   4182      *
   4183      * @return The direct child of the RecyclerView which contains the given view or null if the
   4184      * provided view is not a descendant of this RecyclerView.
   4185      *
   4186      * @see #getChildViewHolder(View)
   4187      * @see #findContainingViewHolder(View)
   4188      */
   4189     @Nullable
   4190     public View findContainingItemView(View view) {
   4191         ViewParent parent = view.getParent();
   4192         while (parent != null && parent != this && parent instanceof View) {
   4193             view = (View) parent;
   4194             parent = view.getParent();
   4195         }
   4196         return parent == this ? view : null;
   4197     }
   4198 
   4199     /**
   4200      * Returns the ViewHolder that contains the given view.
   4201      *
   4202      * @param view The view that is a descendant of the RecyclerView.
   4203      *
   4204      * @return The ViewHolder that contains the given view or null if the provided view is not a
   4205      * descendant of this RecyclerView.
   4206      */
   4207     @Nullable
   4208     public ViewHolder findContainingViewHolder(View view) {
   4209         View itemView = findContainingItemView(view);
   4210         return itemView == null ? null : getChildViewHolder(itemView);
   4211     }
   4212 
   4213 
   4214     static ViewHolder getChildViewHolderInt(View child) {
   4215         if (child == null) {
   4216             return null;
   4217         }
   4218         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
   4219     }
   4220 
   4221     /**
   4222      * @deprecated use {@link #getChildAdapterPosition(View)} or
   4223      * {@link #getChildLayoutPosition(View)}.
   4224      */
   4225     @Deprecated
   4226     public int getChildPosition(View child) {
   4227         return getChildAdapterPosition(child);
   4228     }
   4229 
   4230     /**
   4231      * Return the adapter position that the given child view corresponds to.
   4232      *
   4233      * @param child Child View to query
   4234      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
   4235      */
   4236     public int getChildAdapterPosition(View child) {
   4237         final ViewHolder holder = getChildViewHolderInt(child);
   4238         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
   4239     }
   4240 
   4241     /**
   4242      * Return the adapter position of the given child view as of the latest completed layout pass.
   4243      * <p>
   4244      * This position may not be equal to Item's adapter position if there are pending changes
   4245      * in the adapter which have not been reflected to the layout yet.
   4246      *
   4247      * @param child Child View to query
   4248      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
   4249      * the View is representing a removed item.
   4250      */
   4251     public int getChildLayoutPosition(View child) {
   4252         final ViewHolder holder = getChildViewHolderInt(child);
   4253         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
   4254     }
   4255 
   4256     /**
   4257      * Return the stable item id that the given child view corresponds to.
   4258      *
   4259      * @param child Child View to query
   4260      * @return Item id corresponding to the given view or {@link #NO_ID}
   4261      */
   4262     public long getChildItemId(View child) {
   4263         if (mAdapter == null || !mAdapter.hasStableIds()) {
   4264             return NO_ID;
   4265         }
   4266         final ViewHolder holder = getChildViewHolderInt(child);
   4267         return holder != null ? holder.getItemId() : NO_ID;
   4268     }
   4269 
   4270     /**
   4271      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
   4272      * {@link #findViewHolderForAdapterPosition(int)}
   4273      */
   4274     @Deprecated
   4275     public ViewHolder findViewHolderForPosition(int position) {
   4276         return findViewHolderForPosition(position, false);
   4277     }
   4278 
   4279     /**
   4280      * Return the ViewHolder for the item in the given position of the data set as of the latest
   4281      * layout pass.
   4282      * <p>
   4283      * This method checks only the children of RecyclerView. If the item at the given
   4284      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   4285      * <p>
   4286      * Note that when Adapter contents change, ViewHolder positions are not updated until the
   4287      * next layout calculation. If there are pending adapter updates, the return value of this
   4288      * method may not match your adapter contents. You can use
   4289      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
   4290      * <p>
   4291      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
   4292      * with the same layout position representing the same Item. In this case, the updated
   4293      * ViewHolder will be returned.
   4294      *
   4295      * @param position The position of the item in the data set of the adapter
   4296      * @return The ViewHolder at <code>position</code> or null if there is no such item
   4297      */
   4298     public ViewHolder findViewHolderForLayoutPosition(int position) {
   4299         return findViewHolderForPosition(position, false);
   4300     }
   4301 
   4302     /**
   4303      * Return the ViewHolder for the item in the given position of the data set. Unlike
   4304      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
   4305      * adapter changes that may not be reflected to the layout yet. On the other hand, if
   4306      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
   4307      * calculated yet, this method will return <code>null</code> since the new positions of views
   4308      * are unknown until the layout is calculated.
   4309      * <p>
   4310      * This method checks only the children of RecyclerView. If the item at the given
   4311      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   4312      * <p>
   4313      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
   4314      * representing the same Item. In this case, the updated ViewHolder will be returned.
   4315      *
   4316      * @param position The position of the item in the data set of the adapter
   4317      * @return The ViewHolder at <code>position</code> or null if there is no such item
   4318      */
   4319     public ViewHolder findViewHolderForAdapterPosition(int position) {
   4320         if (mDataSetHasChangedAfterLayout) {
   4321             return null;
   4322         }
   4323         final int childCount = mChildHelper.getUnfilteredChildCount();
   4324         // hidden VHs are not preferred but if that is the only one we find, we rather return it
   4325         ViewHolder hidden = null;
   4326         for (int i = 0; i < childCount; i++) {
   4327             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4328             if (holder != null && !holder.isRemoved()
   4329                     && getAdapterPositionFor(holder) == position) {
   4330                 if (mChildHelper.isHidden(holder.itemView)) {
   4331                     hidden = holder;
   4332                 } else {
   4333                     return holder;
   4334                 }
   4335             }
   4336         }
   4337         return hidden;
   4338     }
   4339 
   4340     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
   4341         final int childCount = mChildHelper.getUnfilteredChildCount();
   4342         ViewHolder hidden = null;
   4343         for (int i = 0; i < childCount; i++) {
   4344             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4345             if (holder != null && !holder.isRemoved()) {
   4346                 if (checkNewPosition) {
   4347                     if (holder.mPosition != position) {
   4348                         continue;
   4349                     }
   4350                 } else if (holder.getLayoutPosition() != position) {
   4351                     continue;
   4352                 }
   4353                 if (mChildHelper.isHidden(holder.itemView)) {
   4354                     hidden = holder;
   4355                 } else {
   4356                     return holder;
   4357                 }
   4358             }
   4359         }
   4360         // This method should not query cached views. It creates a problem during adapter updates
   4361         // when we are dealing with already laid out views. Also, for the public method, it is more
   4362         // reasonable to return null if position is not laid out.
   4363         return hidden;
   4364     }
   4365 
   4366     /**
   4367      * Return the ViewHolder for the item with the given id. The RecyclerView must
   4368      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
   4369      * return a non-null value.
   4370      * <p>
   4371      * This method checks only the children of RecyclerView. If the item with the given
   4372      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
   4373      *
   4374      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
   4375      * same id. In this case, the updated ViewHolder will be returned.
   4376      *
   4377      * @param id The id for the requested item
   4378      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
   4379      */
   4380     public ViewHolder findViewHolderForItemId(long id) {
   4381         if (mAdapter == null || !mAdapter.hasStableIds()) {
   4382             return null;
   4383         }
   4384         final int childCount = mChildHelper.getUnfilteredChildCount();
   4385         ViewHolder hidden = null;
   4386         for (int i = 0; i < childCount; i++) {
   4387             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4388             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
   4389                 if (mChildHelper.isHidden(holder.itemView)) {
   4390                     hidden = holder;
   4391                 } else {
   4392                     return holder;
   4393                 }
   4394             }
   4395         }
   4396         return hidden;
   4397     }
   4398 
   4399     /**
   4400      * Find the topmost view under the given point.
   4401      *
   4402      * @param x Horizontal position in pixels to search
   4403      * @param y Vertical position in pixels to search
   4404      * @return The child view under (x, y) or null if no matching child is found
   4405      */
   4406     public View findChildViewUnder(float x, float y) {
   4407         final int count = mChildHelper.getChildCount();
   4408         for (int i = count - 1; i >= 0; i--) {
   4409             final View child = mChildHelper.getChildAt(i);
   4410             final float translationX = child.getTranslationX();
   4411             final float translationY = child.getTranslationY();
   4412             if (x >= child.getLeft() + translationX
   4413                     && x <= child.getRight() + translationX
   4414                     && y >= child.getTop() + translationY
   4415                     && y <= child.getBottom() + translationY) {
   4416                 return child;
   4417             }
   4418         }
   4419         return null;
   4420     }
   4421 
   4422     @Override
   4423     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
   4424         return super.drawChild(canvas, child, drawingTime);
   4425     }
   4426 
   4427     /**
   4428      * Offset the bounds of all child views by <code>dy</code> pixels.
   4429      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   4430      *
   4431      * @param dy Vertical pixel offset to apply to the bounds of all child views
   4432      */
   4433     public void offsetChildrenVertical(int dy) {
   4434         final int childCount = mChildHelper.getChildCount();
   4435         for (int i = 0; i < childCount; i++) {
   4436             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
   4437         }
   4438     }
   4439 
   4440     /**
   4441      * Called when an item view is attached to this RecyclerView.
   4442      *
   4443      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   4444      * of child views as they become attached. This will be called before a
   4445      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
   4446      * changes.</p>
   4447      *
   4448      * @param child Child view that is now attached to this RecyclerView and its associated window
   4449      */
   4450     public void onChildAttachedToWindow(View child) {
   4451     }
   4452 
   4453     /**
   4454      * Called when an item view is detached from this RecyclerView.
   4455      *
   4456      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   4457      * of child views as they become detached. This will be called as a
   4458      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
   4459      *
   4460      * @param child Child view that is now detached from this RecyclerView and its associated window
   4461      */
   4462     public void onChildDetachedFromWindow(View child) {
   4463     }
   4464 
   4465     /**
   4466      * Offset the bounds of all child views by <code>dx</code> pixels.
   4467      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   4468      *
   4469      * @param dx Horizontal pixel offset to apply to the bounds of all child views
   4470      */
   4471     public void offsetChildrenHorizontal(int dx) {
   4472         final int childCount = mChildHelper.getChildCount();
   4473         for (int i = 0; i < childCount; i++) {
   4474             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
   4475         }
   4476     }
   4477 
   4478     /**
   4479      * Returns the bounds of the view including its decoration and margins.
   4480      *
   4481      * @param view The view element to check
   4482      * @param outBounds A rect that will receive the bounds of the element including its
   4483      *                  decoration and margins.
   4484      */
   4485     public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
   4486         getDecoratedBoundsWithMarginsInt(view, outBounds);
   4487     }
   4488 
   4489     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
   4490         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
   4491         final Rect insets = lp.mDecorInsets;
   4492         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
   4493                 view.getTop() - insets.top - lp.topMargin,
   4494                 view.getRight() + insets.right + lp.rightMargin,
   4495                 view.getBottom() + insets.bottom + lp.bottomMargin);
   4496     }
   4497 
   4498     Rect getItemDecorInsetsForChild(View child) {
   4499         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   4500         if (!lp.mInsetsDirty) {
   4501             return lp.mDecorInsets;
   4502         }
   4503 
   4504         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
   4505             // changed/invalid items should not be updated until they are rebound.
   4506             return lp.mDecorInsets;
   4507         }
   4508         final Rect insets = lp.mDecorInsets;
   4509         insets.set(0, 0, 0, 0);
   4510         final int decorCount = mItemDecorations.size();
   4511         for (int i = 0; i < decorCount; i++) {
   4512             mTempRect.set(0, 0, 0, 0);
   4513             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
   4514             insets.left += mTempRect.left;
   4515             insets.top += mTempRect.top;
   4516             insets.right += mTempRect.right;
   4517             insets.bottom += mTempRect.bottom;
   4518         }
   4519         lp.mInsetsDirty = false;
   4520         return insets;
   4521     }
   4522 
   4523     /**
   4524      * Called when the scroll position of this RecyclerView changes. Subclasses should use
   4525      * this method to respond to scrolling within the adapter's data set instead of an explicit
   4526      * listener.
   4527      *
   4528      * <p>This method will always be invoked before listeners. If a subclass needs to perform
   4529      * any additional upkeep or bookkeeping after scrolling but before listeners run,
   4530      * this is a good place to do so.</p>
   4531      *
   4532      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
   4533      * the distance scrolled in either direction within the adapter's data set instead of absolute
   4534      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
   4535      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
   4536      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
   4537      * do not correspond to the data set scroll position. However, some subclasses may choose
   4538      * to use these fields as special offsets.</p>
   4539      *
   4540      * @param dx horizontal distance scrolled in pixels
   4541      * @param dy vertical distance scrolled in pixels
   4542      */
   4543     public void onScrolled(int dx, int dy) {
   4544         // Do nothing
   4545     }
   4546 
   4547     void dispatchOnScrolled(int hresult, int vresult) {
   4548         mDispatchScrollCounter++;
   4549         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
   4550         // but some general-purpose code may choose to respond to changes this way.
   4551         final int scrollX = getScrollX();
   4552         final int scrollY = getScrollY();
   4553         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
   4554 
   4555         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
   4556         onScrolled(hresult, vresult);
   4557 
   4558         // Invoke listeners last. Subclassed view methods always handle the event first.
   4559         // All internal state is consistent by the time listeners are invoked.
   4560         if (mScrollListener != null) {
   4561             mScrollListener.onScrolled(this, hresult, vresult);
   4562         }
   4563         if (mScrollListeners != null) {
   4564             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   4565                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
   4566             }
   4567         }
   4568         mDispatchScrollCounter--;
   4569     }
   4570 
   4571     /**
   4572      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
   4573      * method to respond to state changes instead of an explicit listener.
   4574      *
   4575      * <p>This method will always be invoked before listeners, but after the LayoutManager
   4576      * responds to the scroll state change.</p>
   4577      *
   4578      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
   4579      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
   4580      */
   4581     public void onScrollStateChanged(int state) {
   4582         // Do nothing
   4583     }
   4584 
   4585     void dispatchOnScrollStateChanged(int state) {
   4586         // Let the LayoutManager go first; this allows it to bring any properties into
   4587         // a consistent state before the RecyclerView subclass responds.
   4588         if (mLayout != null) {
   4589             mLayout.onScrollStateChanged(state);
   4590         }
   4591 
   4592         // Let the RecyclerView subclass handle this event next; any LayoutManager property
   4593         // changes will be reflected by this time.
   4594         onScrollStateChanged(state);
   4595 
   4596         // Listeners go last. All other internal state is consistent by this point.
   4597         if (mScrollListener != null) {
   4598             mScrollListener.onScrollStateChanged(this, state);
   4599         }
   4600         if (mScrollListeners != null) {
   4601             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   4602                 mScrollListeners.get(i).onScrollStateChanged(this, state);
   4603             }
   4604         }
   4605     }
   4606 
   4607     /**
   4608      * Returns whether there are pending adapter updates which are not yet applied to the layout.
   4609      * <p>
   4610      * If this method returns <code>true</code>, it means that what user is currently seeing may not
   4611      * reflect them adapter contents (depending on what has changed).
   4612      * You may use this information to defer or cancel some operations.
   4613      * <p>
   4614      * This method returns true if RecyclerView has not yet calculated the first layout after it is
   4615      * attached to the Window or the Adapter has been replaced.
   4616      *
   4617      * @return True if there are some adapter updates which are not yet reflected to layout or false
   4618      * if layout is up to date.
   4619      */
   4620     public boolean hasPendingAdapterUpdates() {
   4621         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
   4622                 || mAdapterHelper.hasPendingUpdates();
   4623     }
   4624 
   4625     class ViewFlinger implements Runnable {
   4626         private int mLastFlingX;
   4627         private int mLastFlingY;
   4628         private OverScroller mScroller;
   4629         Interpolator mInterpolator = sQuinticInterpolator;
   4630 
   4631 
   4632         // When set to true, postOnAnimation callbacks are delayed until the run method completes
   4633         private boolean mEatRunOnAnimationRequest = false;
   4634 
   4635         // Tracks if postAnimationCallback should be re-attached when it is done
   4636         private boolean mReSchedulePostAnimationCallback = false;
   4637 
   4638         ViewFlinger() {
   4639             mScroller = new OverScroller(getContext(), sQuinticInterpolator);
   4640         }
   4641 
   4642         @Override
   4643         public void run() {
   4644             if (mLayout == null) {
   4645                 stop();
   4646                 return; // no layout, cannot scroll.
   4647             }
   4648             disableRunOnAnimationRequests();
   4649             consumePendingUpdateOperations();
   4650             // keep a local reference so that if it is changed during onAnimation method, it won't
   4651             // cause unexpected behaviors
   4652             final OverScroller scroller = mScroller;
   4653             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
   4654             if (scroller.computeScrollOffset()) {
   4655                 final int x = scroller.getCurrX();
   4656                 final int y = scroller.getCurrY();
   4657                 final int dx = x - mLastFlingX;
   4658                 final int dy = y - mLastFlingY;
   4659                 int hresult = 0;
   4660                 int vresult = 0;
   4661                 mLastFlingX = x;
   4662                 mLastFlingY = y;
   4663                 int overscrollX = 0, overscrollY = 0;
   4664                 if (mAdapter != null) {
   4665                     eatRequestLayout();
   4666                     onEnterLayoutOrScroll();
   4667                     Trace.beginSection(TRACE_SCROLL_TAG);
   4668                     if (dx != 0) {
   4669                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
   4670                         overscrollX = dx - hresult;
   4671                     }
   4672                     if (dy != 0) {
   4673                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
   4674                         overscrollY = dy - vresult;
   4675                     }
   4676                     Trace.endSection();
   4677                     repositionShadowingViews();
   4678 
   4679                     onExitLayoutOrScroll();
   4680                     resumeRequestLayout(false);
   4681 
   4682                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
   4683                             && smoothScroller.isRunning()) {
   4684                         final int adapterSize = mState.getItemCount();
   4685                         if (adapterSize == 0) {
   4686                             smoothScroller.stop();
   4687                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
   4688                             smoothScroller.setTargetPosition(adapterSize - 1);
   4689                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   4690                         } else {
   4691                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   4692                         }
   4693                     }
   4694                 }
   4695                 if (!mItemDecorations.isEmpty()) {
   4696                     invalidate();
   4697                 }
   4698                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   4699                     considerReleasingGlowsOnScroll(dx, dy);
   4700                 }
   4701                 if (overscrollX != 0 || overscrollY != 0) {
   4702                     final int vel = (int) scroller.getCurrVelocity();
   4703 
   4704                     int velX = 0;
   4705                     if (overscrollX != x) {
   4706                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
   4707                     }
   4708 
   4709                     int velY = 0;
   4710                     if (overscrollY != y) {
   4711                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
   4712                     }
   4713 
   4714                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   4715                         absorbGlows(velX, velY);
   4716                     }
   4717                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
   4718                             && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
   4719                         scroller.abortAnimation();
   4720                     }
   4721                 }
   4722                 if (hresult != 0 || vresult != 0) {
   4723                     dispatchOnScrolled(hresult, vresult);
   4724                 }
   4725 
   4726                 if (!awakenScrollBars()) {
   4727                     invalidate();
   4728                 }
   4729 
   4730                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
   4731                         && vresult == dy;
   4732                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
   4733                         && hresult == dx;
   4734                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
   4735                         || fullyConsumedVertical;
   4736 
   4737                 if (scroller.isFinished() || !fullyConsumedAny) {
   4738                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
   4739                     if (ALLOW_THREAD_GAP_WORK) {
   4740                         mPrefetchRegistry.clearPrefetchPositions();
   4741                     }
   4742                 } else {
   4743                     postOnAnimation();
   4744                     if (mGapWorker != null) {
   4745                         mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
   4746                     }
   4747                 }
   4748             }
   4749             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
   4750             if (smoothScroller != null) {
   4751                 if (smoothScroller.isPendingInitialRun()) {
   4752                     smoothScroller.onAnimation(0, 0);
   4753                 }
   4754                 if (!mReSchedulePostAnimationCallback) {
   4755                     smoothScroller.stop(); //stop if it does not trigger any scroll
   4756                 }
   4757             }
   4758             enableRunOnAnimationRequests();
   4759         }
   4760 
   4761         private void disableRunOnAnimationRequests() {
   4762             mReSchedulePostAnimationCallback = false;
   4763             mEatRunOnAnimationRequest = true;
   4764         }
   4765 
   4766         private void enableRunOnAnimationRequests() {
   4767             mEatRunOnAnimationRequest = false;
   4768             if (mReSchedulePostAnimationCallback) {
   4769                 postOnAnimation();
   4770             }
   4771         }
   4772 
   4773         void postOnAnimation() {
   4774             if (mEatRunOnAnimationRequest) {
   4775                 mReSchedulePostAnimationCallback = true;
   4776             } else {
   4777                 removeCallbacks(this);
   4778                 RecyclerView.this.postOnAnimation(this);
   4779             }
   4780         }
   4781 
   4782         public void fling(int velocityX, int velocityY) {
   4783             setScrollState(SCROLL_STATE_SETTLING);
   4784             mLastFlingX = mLastFlingY = 0;
   4785             mScroller.fling(0, 0, velocityX, velocityY,
   4786                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
   4787             postOnAnimation();
   4788         }
   4789 
   4790         public void smoothScrollBy(int dx, int dy) {
   4791             smoothScrollBy(dx, dy, 0, 0);
   4792         }
   4793 
   4794         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
   4795             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
   4796         }
   4797 
   4798         private float distanceInfluenceForSnapDuration(float f) {
   4799             f -= 0.5f; // center the values about 0.
   4800             f *= 0.3f * Math.PI / 2.0f;
   4801             return (float) Math.sin(f);
   4802         }
   4803 
   4804         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
   4805             final int absDx = Math.abs(dx);
   4806             final int absDy = Math.abs(dy);
   4807             final boolean horizontal = absDx > absDy;
   4808             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
   4809             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
   4810             final int containerSize = horizontal ? getWidth() : getHeight();
   4811             final int halfContainerSize = containerSize / 2;
   4812             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
   4813             final float distance = halfContainerSize + halfContainerSize
   4814                     * distanceInfluenceForSnapDuration(distanceRatio);
   4815 
   4816             final int duration;
   4817             if (velocity > 0) {
   4818                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
   4819             } else {
   4820                 float absDelta = (float) (horizontal ? absDx : absDy);
   4821                 duration = (int) (((absDelta / containerSize) + 1) * 300);
   4822             }
   4823             return Math.min(duration, MAX_SCROLL_DURATION);
   4824         }
   4825 
   4826         public void smoothScrollBy(int dx, int dy, int duration) {
   4827             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
   4828         }
   4829 
   4830         public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
   4831             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
   4832                     interpolator == null ? sQuinticInterpolator : interpolator);
   4833         }
   4834 
   4835         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
   4836             if (mInterpolator != interpolator) {
   4837                 mInterpolator = interpolator;
   4838                 mScroller = new OverScroller(getContext(), interpolator);
   4839             }
   4840             setScrollState(SCROLL_STATE_SETTLING);
   4841             mLastFlingX = mLastFlingY = 0;
   4842             mScroller.startScroll(0, 0, dx, dy, duration);
   4843             postOnAnimation();
   4844         }
   4845 
   4846         public void stop() {
   4847             removeCallbacks(this);
   4848             mScroller.abortAnimation();
   4849         }
   4850 
   4851     }
   4852 
   4853     void repositionShadowingViews() {
   4854         // Fix up shadow views used by change animations
   4855         int count = mChildHelper.getChildCount();
   4856         for (int i = 0; i < count; i++) {
   4857             View view = mChildHelper.getChildAt(i);
   4858             ViewHolder holder = getChildViewHolder(view);
   4859             if (holder != null && holder.mShadowingHolder != null) {
   4860                 View shadowingView = holder.mShadowingHolder.itemView;
   4861                 int left = view.getLeft();
   4862                 int top = view.getTop();
   4863                 if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
   4864                     shadowingView.layout(left, top,
   4865                             left + shadowingView.getWidth(),
   4866                             top + shadowingView.getHeight());
   4867                 }
   4868             }
   4869         }
   4870     }
   4871 
   4872     private class RecyclerViewDataObserver extends AdapterDataObserver {
   4873         RecyclerViewDataObserver() {
   4874         }
   4875 
   4876         @Override
   4877         public void onChanged() {
   4878             assertNotInLayoutOrScroll(null);
   4879             mState.mStructureChanged = true;
   4880 
   4881             setDataSetChangedAfterLayout();
   4882             if (!mAdapterHelper.hasPendingUpdates()) {
   4883                 requestLayout();
   4884             }
   4885         }
   4886 
   4887         @Override
   4888         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   4889             assertNotInLayoutOrScroll(null);
   4890             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
   4891                 triggerUpdateProcessor();
   4892             }
   4893         }
   4894 
   4895         @Override
   4896         public void onItemRangeInserted(int positionStart, int itemCount) {
   4897             assertNotInLayoutOrScroll(null);
   4898             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
   4899                 triggerUpdateProcessor();
   4900             }
   4901         }
   4902 
   4903         @Override
   4904         public void onItemRangeRemoved(int positionStart, int itemCount) {
   4905             assertNotInLayoutOrScroll(null);
   4906             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
   4907                 triggerUpdateProcessor();
   4908             }
   4909         }
   4910 
   4911         @Override
   4912         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   4913             assertNotInLayoutOrScroll(null);
   4914             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
   4915                 triggerUpdateProcessor();
   4916             }
   4917         }
   4918 
   4919         void triggerUpdateProcessor() {
   4920             if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
   4921                 RecyclerView.this.postOnAnimation(mUpdateChildViewsRunnable);
   4922             } else {
   4923                 mAdapterUpdateDuringMeasure = true;
   4924                 requestLayout();
   4925             }
   4926         }
   4927     }
   4928 
   4929     /**
   4930      * RecycledViewPool lets you share Views between multiple RecyclerViews.
   4931      * <p>
   4932      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
   4933      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
   4934      * <p>
   4935      * RecyclerView automatically creates a pool for itself if you don't provide one.
   4936      *
   4937      */
   4938     public static class RecycledViewPool {
   4939         private static final int DEFAULT_MAX_SCRAP = 5;
   4940 
   4941         /**
   4942          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
   4943          *
   4944          * Note that this tracks running averages of create/bind time across all RecyclerViews
   4945          * (and, indirectly, Adapters) that use this pool.
   4946          *
   4947          * 1) This enables us to track average create and bind times across multiple adapters. Even
   4948          * though create (and especially bind) may behave differently for different Adapter
   4949          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
   4950          *
   4951          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
   4952          * false for all other views of its type for the same deadline. This prevents items
   4953          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
   4954          */
   4955         static class ScrapData {
   4956             ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
   4957             int mMaxScrap = DEFAULT_MAX_SCRAP;
   4958             long mCreateRunningAverageNs = 0;
   4959             long mBindRunningAverageNs = 0;
   4960         }
   4961         SparseArray<ScrapData> mScrap = new SparseArray<>();
   4962 
   4963         private int mAttachCount = 0;
   4964 
   4965         public void clear() {
   4966             for (int i = 0; i < mScrap.size(); i++) {
   4967                 ScrapData data = mScrap.valueAt(i);
   4968                 data.mScrapHeap.clear();
   4969             }
   4970         }
   4971 
   4972         public void setMaxRecycledViews(int viewType, int max) {
   4973             ScrapData scrapData = getScrapDataForType(viewType);
   4974             scrapData.mMaxScrap = max;
   4975             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
   4976             if (scrapHeap != null) {
   4977                 while (scrapHeap.size() > max) {
   4978                     scrapHeap.remove(scrapHeap.size() - 1);
   4979                 }
   4980             }
   4981         }
   4982 
   4983         /**
   4984          * Returns the current number of Views held by the RecycledViewPool of the given view type.
   4985          */
   4986         public int getRecycledViewCount(int viewType) {
   4987             return getScrapDataForType(viewType).mScrapHeap.size();
   4988         }
   4989 
   4990         public ViewHolder getRecycledView(int viewType) {
   4991             final ScrapData scrapData = mScrap.get(viewType);
   4992             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
   4993                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
   4994                 return scrapHeap.remove(scrapHeap.size() - 1);
   4995             }
   4996             return null;
   4997         }
   4998 
   4999         int size() {
   5000             int count = 0;
   5001             for (int i = 0; i < mScrap.size(); i++) {
   5002                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
   5003                 if (viewHolders != null) {
   5004                     count += viewHolders.size();
   5005                 }
   5006             }
   5007             return count;
   5008         }
   5009 
   5010         public void putRecycledView(ViewHolder scrap) {
   5011             final int viewType = scrap.getItemViewType();
   5012             final ArrayList scrapHeap = getScrapDataForType(viewType).mScrapHeap;
   5013             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
   5014                 return;
   5015             }
   5016             if (DEBUG && scrapHeap.contains(scrap)) {
   5017                 throw new IllegalArgumentException("this scrap item already exists");
   5018             }
   5019             scrap.resetInternal();
   5020             scrapHeap.add(scrap);
   5021         }
   5022 
   5023         long runningAverage(long oldAverage, long newValue) {
   5024             if (oldAverage == 0) {
   5025                 return newValue;
   5026             }
   5027             return (oldAverage / 4 * 3) + (newValue / 4);
   5028         }
   5029 
   5030         void factorInCreateTime(int viewType, long createTimeNs) {
   5031             ScrapData scrapData = getScrapDataForType(viewType);
   5032             scrapData.mCreateRunningAverageNs = runningAverage(
   5033                     scrapData.mCreateRunningAverageNs, createTimeNs);
   5034         }
   5035 
   5036         void factorInBindTime(int viewType, long bindTimeNs) {
   5037             ScrapData scrapData = getScrapDataForType(viewType);
   5038             scrapData.mBindRunningAverageNs = runningAverage(
   5039                     scrapData.mBindRunningAverageNs, bindTimeNs);
   5040         }
   5041 
   5042         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
   5043             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
   5044             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
   5045         }
   5046 
   5047         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
   5048             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
   5049             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
   5050         }
   5051 
   5052         void attach(Adapter adapter) {
   5053             mAttachCount++;
   5054         }
   5055 
   5056         void detach() {
   5057             mAttachCount--;
   5058         }
   5059 
   5060 
   5061         /**
   5062          * Detaches the old adapter and attaches the new one.
   5063          * <p>
   5064          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
   5065          * adapter uses a different ViewHolder than the oldAdapter.
   5066          *
   5067          * @param oldAdapter The previous adapter instance. Will be detached.
   5068          * @param newAdapter The new adapter instance. Will be attached.
   5069          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
   5070          *                               ViewHolder and view types.
   5071          */
   5072         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   5073                 boolean compatibleWithPrevious) {
   5074             if (oldAdapter != null) {
   5075                 detach();
   5076             }
   5077             if (!compatibleWithPrevious && mAttachCount == 0) {
   5078                 clear();
   5079             }
   5080             if (newAdapter != null) {
   5081                 attach(newAdapter);
   5082             }
   5083         }
   5084 
   5085         private ScrapData getScrapDataForType(int viewType) {
   5086             ScrapData scrapData = mScrap.get(viewType);
   5087             if (scrapData == null) {
   5088                 scrapData = new ScrapData();
   5089                 mScrap.put(viewType, scrapData);
   5090             }
   5091             return scrapData;
   5092         }
   5093     }
   5094 
   5095     /**
   5096      * Utility method for finding an internal RecyclerView, if present
   5097      */
   5098     @Nullable
   5099     static RecyclerView findNestedRecyclerView(@NonNull View view) {
   5100         if (!(view instanceof ViewGroup)) {
   5101             return null;
   5102         }
   5103         if (view instanceof RecyclerView) {
   5104             return (RecyclerView) view;
   5105         }
   5106         final ViewGroup parent = (ViewGroup) view;
   5107         final int count = parent.getChildCount();
   5108         for (int i = 0; i < count; i++) {
   5109             final View child = parent.getChildAt(i);
   5110             final RecyclerView descendant = findNestedRecyclerView(child);
   5111             if (descendant != null) {
   5112                 return descendant;
   5113             }
   5114         }
   5115         return null;
   5116     }
   5117 
   5118     /**
   5119      * Utility method for clearing holder's internal RecyclerView, if present
   5120      */
   5121     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
   5122         if (holder.mNestedRecyclerView != null) {
   5123             View item = holder.mNestedRecyclerView.get();
   5124             while (item != null) {
   5125                 if (item == holder.itemView) {
   5126                     return; // match found, don't need to clear
   5127                 }
   5128 
   5129                 ViewParent parent = item.getParent();
   5130                 if (parent instanceof View) {
   5131                     item = (View) parent;
   5132                 } else {
   5133                     item = null;
   5134                 }
   5135             }
   5136             holder.mNestedRecyclerView = null; // not nested
   5137         }
   5138     }
   5139 
   5140     /**
   5141      * Time base for deadline-aware work scheduling. Overridable for testing.
   5142      *
   5143      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
   5144      * isn't relevant.
   5145      */
   5146     long getNanoTime() {
   5147         if (ALLOW_THREAD_GAP_WORK) {
   5148             return System.nanoTime();
   5149         } else {
   5150             return 0;
   5151         }
   5152     }
   5153 
   5154     /**
   5155      * A Recycler is responsible for managing scrapped or detached item views for reuse.
   5156      *
   5157      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
   5158      * that has been marked for removal or reuse.</p>
   5159      *
   5160      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
   5161      * an adapter's data set representing the data at a given position or item ID.
   5162      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
   5163      * If not, the view can be quickly reused by the LayoutManager with no further work.
   5164      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
   5165      * may be repositioned by a LayoutManager without remeasurement.</p>
   5166      */
   5167     public final class Recycler {
   5168         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
   5169         ArrayList<ViewHolder> mChangedScrap = null;
   5170 
   5171         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
   5172 
   5173         private final List<ViewHolder>
   5174                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
   5175 
   5176         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
   5177         int mViewCacheMax = DEFAULT_CACHE_SIZE;
   5178 
   5179         RecycledViewPool mRecyclerPool;
   5180 
   5181         private ViewCacheExtension mViewCacheExtension;
   5182 
   5183         static final int DEFAULT_CACHE_SIZE = 2;
   5184 
   5185         /**
   5186          * Clear scrap views out of this recycler. Detached views contained within a
   5187          * recycled view pool will remain.
   5188          */
   5189         public void clear() {
   5190             mAttachedScrap.clear();
   5191             recycleAndClearCachedViews();
   5192         }
   5193 
   5194         /**
   5195          * Set the maximum number of detached, valid views we should retain for later use.
   5196          *
   5197          * @param viewCount Number of views to keep before sending views to the shared pool
   5198          */
   5199         public void setViewCacheSize(int viewCount) {
   5200             mRequestedCacheMax = viewCount;
   5201             updateViewCacheSize();
   5202         }
   5203 
   5204         void updateViewCacheSize() {
   5205             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
   5206             mViewCacheMax = mRequestedCacheMax + extraCache;
   5207 
   5208             // first, try the views that can be recycled
   5209             for (int i = mCachedViews.size() - 1;
   5210                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
   5211                 recycleCachedViewAt(i);
   5212             }
   5213         }
   5214 
   5215         /**
   5216          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
   5217          *
   5218          * @return List of ViewHolders in the scrap list.
   5219          */
   5220         public List<ViewHolder> getScrapList() {
   5221             return mUnmodifiableAttachedScrap;
   5222         }
   5223 
   5224         /**
   5225          * Helper method for getViewForPosition.
   5226          * <p>
   5227          * Checks whether a given view holder can be used for the provided position.
   5228          *
   5229          * @param holder ViewHolder
   5230          * @return true if ViewHolder matches the provided position, false otherwise
   5231          */
   5232         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
   5233             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
   5234             // if it is not removed, verify the type and id.
   5235             if (holder.isRemoved()) {
   5236                 if (DEBUG && !mState.isPreLayout()) {
   5237                     throw new IllegalStateException("should not receive a removed view unless it"
   5238                             + " is pre layout");
   5239                 }
   5240                 return mState.isPreLayout();
   5241             }
   5242             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
   5243                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
   5244                         + "adapter position" + holder);
   5245             }
   5246             if (!mState.isPreLayout()) {
   5247                 // don't check type if it is pre-layout.
   5248                 final int type = mAdapter.getItemViewType(holder.mPosition);
   5249                 if (type != holder.getItemViewType()) {
   5250                     return false;
   5251                 }
   5252             }
   5253             if (mAdapter.hasStableIds()) {
   5254                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
   5255             }
   5256             return true;
   5257         }
   5258 
   5259         /**
   5260          * Attempts to bind view, and account for relevant timing information. If
   5261          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
   5262          *
   5263          * @param holder Holder to be bound.
   5264          * @param offsetPosition Position of item to be bound.
   5265          * @param position Pre-layout position of item to be bound.
   5266          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
   5267          *                   complete. If FOREVER_NS is passed, this method will not fail to
   5268          *                   bind the holder.
   5269          * @return
   5270          */
   5271         private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
   5272                 int position, long deadlineNs) {
   5273             holder.mOwnerRecyclerView = RecyclerView.this;
   5274             final int viewType = holder.getItemViewType();
   5275             long startBindNs = getNanoTime();
   5276             if (deadlineNs != FOREVER_NS
   5277                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
   5278                 // abort - we have a deadline we can't meet
   5279                 return false;
   5280             }
   5281             mAdapter.bindViewHolder(holder, offsetPosition);
   5282             long endBindNs = getNanoTime();
   5283             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
   5284             attachAccessibilityDelegate(holder.itemView);
   5285             if (mState.isPreLayout()) {
   5286                 holder.mPreLayoutPosition = position;
   5287             }
   5288             return true;
   5289         }
   5290 
   5291         /**
   5292          * Binds the given View to the position. The View can be a View previously retrieved via
   5293          * {@link #getViewForPosition(int)} or created by
   5294          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
   5295          * <p>
   5296          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
   5297          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
   5298          * wants to handle its own recycling logic.
   5299          * <p>
   5300          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
   5301          * you don't need to call this method unless you want to bind this View to another position.
   5302          *
   5303          * @param view The view to update.
   5304          * @param position The position of the item to bind to this View.
   5305          */
   5306         public void bindViewToPosition(View view, int position) {
   5307             ViewHolder holder = getChildViewHolderInt(view);
   5308             if (holder == null) {
   5309                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
   5310                         + " pass arbitrary views to this method, they should be created by the "
   5311                         + "Adapter");
   5312             }
   5313             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5314             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   5315                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   5316                         + "position " + position + "(offset:" + offsetPosition + ")."
   5317                         + "state:" + mState.getItemCount());
   5318             }
   5319             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
   5320 
   5321             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   5322             final LayoutParams rvLayoutParams;
   5323             if (lp == null) {
   5324                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   5325                 holder.itemView.setLayoutParams(rvLayoutParams);
   5326             } else if (!checkLayoutParams(lp)) {
   5327                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   5328                 holder.itemView.setLayoutParams(rvLayoutParams);
   5329             } else {
   5330                 rvLayoutParams = (LayoutParams) lp;
   5331             }
   5332 
   5333             rvLayoutParams.mInsetsDirty = true;
   5334             rvLayoutParams.mViewHolder = holder;
   5335             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
   5336         }
   5337 
   5338         /**
   5339          * RecyclerView provides artificial position range (item count) in pre-layout state and
   5340          * automatically maps these positions to {@link Adapter} positions when
   5341          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
   5342          * <p>
   5343          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
   5344          * LayoutManager may need to call some custom component with item positions in which
   5345          * case you need the actual adapter position instead of the pre layout position. You
   5346          * can use this method to convert a pre-layout position to adapter (post layout) position.
   5347          * <p>
   5348          * Note that if the provided position belongs to a deleted ViewHolder, this method will
   5349          * return -1.
   5350          * <p>
   5351          * Calling this method in post-layout state returns the same value back.
   5352          *
   5353          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
   5354          *                 less than {@link State#getItemCount()}.
   5355          */
   5356         public int convertPreLayoutPositionToPostLayout(int position) {
   5357             if (position < 0 || position >= mState.getItemCount()) {
   5358                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
   5359                         + "item count is " + mState.getItemCount());
   5360             }
   5361             if (!mState.isPreLayout()) {
   5362                 return position;
   5363             }
   5364             return mAdapterHelper.findPositionOffset(position);
   5365         }
   5366 
   5367         /**
   5368          * Obtain a view initialized for the given position.
   5369          *
   5370          * This method should be used by {@link LayoutManager} implementations to obtain
   5371          * views to represent data from an {@link Adapter}.
   5372          * <p>
   5373          * The Recycler may reuse a scrap or detached view from a shared pool if one is
   5374          * available for the correct view type. If the adapter has not indicated that the
   5375          * data at the given position has changed, the Recycler will attempt to hand back
   5376          * a scrap view that was previously initialized for that data without rebinding.
   5377          *
   5378          * @param position Position to obtain a view for
   5379          * @return A view representing the data at <code>position</code> from <code>adapter</code>
   5380          */
   5381         public View getViewForPosition(int position) {
   5382             return getViewForPosition(position, false);
   5383         }
   5384 
   5385         View getViewForPosition(int position, boolean dryRun) {
   5386             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
   5387         }
   5388 
   5389         /**
   5390          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
   5391          * cache, the RecycledViewPool, or creating it directly.
   5392          * <p>
   5393          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
   5394          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
   5395          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
   5396          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
   5397          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
   5398          *
   5399          * @param position Position of ViewHolder to be returned.
   5400          * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
   5401          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
   5402          *                   complete. If FOREVER_NS is passed, this method will not fail to
   5403          *                   create/bind the holder if needed.
   5404          *
   5405          * @return ViewHolder for requested position
   5406          */
   5407         @Nullable
   5408         ViewHolder tryGetViewHolderForPositionByDeadline(int position,
   5409                 boolean dryRun, long deadlineNs) {
   5410             if (position < 0 || position >= mState.getItemCount()) {
   5411                 throw new IndexOutOfBoundsException("Invalid item position " + position
   5412                         + "(" + position + "). Item count:" + mState.getItemCount());
   5413             }
   5414             boolean fromScrapOrHiddenOrCache = false;
   5415             ViewHolder holder = null;
   5416             // 0) If there is a changed scrap, try to find from there
   5417             if (mState.isPreLayout()) {
   5418                 holder = getChangedScrapViewForPosition(position);
   5419                 fromScrapOrHiddenOrCache = holder != null;
   5420             }
   5421             // 1) Find by position from scrap/hidden list/cache
   5422             if (holder == null) {
   5423                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
   5424                 if (holder != null) {
   5425                     if (!validateViewHolderForOffsetPosition(holder)) {
   5426                         // recycle holder (and unscrap if relevant) since it can't be used
   5427                         if (!dryRun) {
   5428                             // we would like to recycle this but need to make sure it is not used by
   5429                             // animation logic etc.
   5430                             holder.addFlags(ViewHolder.FLAG_INVALID);
   5431                             if (holder.isScrap()) {
   5432                                 removeDetachedView(holder.itemView, false);
   5433                                 holder.unScrap();
   5434                             } else if (holder.wasReturnedFromScrap()) {
   5435                                 holder.clearReturnedFromScrapFlag();
   5436                             }
   5437                             recycleViewHolderInternal(holder);
   5438                         }
   5439                         holder = null;
   5440                     } else {
   5441                         fromScrapOrHiddenOrCache = true;
   5442                     }
   5443                 }
   5444             }
   5445             if (holder == null) {
   5446                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5447                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   5448                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   5449                             + "position " + position + "(offset:" + offsetPosition + ")."
   5450                             + "state:" + mState.getItemCount());
   5451                 }
   5452 
   5453                 final int type = mAdapter.getItemViewType(offsetPosition);
   5454                 // 2) Find from scrap/cache via stable ids, if exists
   5455                 if (mAdapter.hasStableIds()) {
   5456                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
   5457                             type, dryRun);
   5458                     if (holder != null) {
   5459                         // update position
   5460                         holder.mPosition = offsetPosition;
   5461                         fromScrapOrHiddenOrCache = true;
   5462                     }
   5463                 }
   5464                 if (holder == null && mViewCacheExtension != null) {
   5465                     // We are NOT sending the offsetPosition because LayoutManager does not
   5466                     // know it.
   5467                     final View view = mViewCacheExtension
   5468                             .getViewForPositionAndType(this, position, type);
   5469                     if (view != null) {
   5470                         holder = getChildViewHolder(view);
   5471                         if (holder == null) {
   5472                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   5473                                     + " a view which does not have a ViewHolder");
   5474                         } else if (holder.shouldIgnore()) {
   5475                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   5476                                     + " a view that is ignored. You must call stopIgnoring before"
   5477                                     + " returning this view.");
   5478                         }
   5479                     }
   5480                 }
   5481                 if (holder == null) { // fallback to pool
   5482                     if (DEBUG) {
   5483                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
   5484                                 + position + ") fetching from shared pool");
   5485                     }
   5486                     holder = getRecycledViewPool().getRecycledView(type);
   5487                     if (holder != null) {
   5488                         holder.resetInternal();
   5489                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
   5490                             invalidateDisplayListInt(holder);
   5491                         }
   5492                     }
   5493                 }
   5494                 if (holder == null) {
   5495                     long start = getNanoTime();
   5496                     if (deadlineNs != FOREVER_NS
   5497                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
   5498                         // abort - we have a deadline we can't meet
   5499                         return null;
   5500                     }
   5501                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
   5502                     if (ALLOW_THREAD_GAP_WORK) {
   5503                         // only bother finding nested RV if prefetching
   5504                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
   5505                         if (innerView != null) {
   5506                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
   5507                         }
   5508                     }
   5509 
   5510                     long end = getNanoTime();
   5511                     mRecyclerPool.factorInCreateTime(type, end - start);
   5512                     if (DEBUG) {
   5513                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
   5514                     }
   5515                 }
   5516             }
   5517 
   5518             // This is very ugly but the only place we can grab this information
   5519             // before the View is rebound and returned to the LayoutManager for post layout ops.
   5520             // We don't need this in pre-layout since the VH is not updated by the LM.
   5521             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
   5522                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
   5523                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   5524                 if (mState.mRunSimpleAnimations) {
   5525                     int changeFlags = ItemAnimator
   5526                             .buildAdapterChangeFlagsForAnimations(holder);
   5527                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
   5528                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
   5529                             holder, changeFlags, holder.getUnmodifiedPayloads());
   5530                     recordAnimationInfoIfBouncedHiddenView(holder, info);
   5531                 }
   5532             }
   5533 
   5534             boolean bound = false;
   5535             if (mState.isPreLayout() && holder.isBound()) {
   5536                 // do not update unless we absolutely have to.
   5537                 holder.mPreLayoutPosition = position;
   5538             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
   5539                 if (DEBUG && holder.isRemoved()) {
   5540                     throw new IllegalStateException("Removed holder should be bound and it should"
   5541                             + " come here only in pre-layout. Holder: " + holder);
   5542                 }
   5543                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5544                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
   5545             }
   5546 
   5547             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   5548             final LayoutParams rvLayoutParams;
   5549             if (lp == null) {
   5550                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   5551                 holder.itemView.setLayoutParams(rvLayoutParams);
   5552             } else if (!checkLayoutParams(lp)) {
   5553                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   5554                 holder.itemView.setLayoutParams(rvLayoutParams);
   5555             } else {
   5556                 rvLayoutParams = (LayoutParams) lp;
   5557             }
   5558             rvLayoutParams.mViewHolder = holder;
   5559             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
   5560             return holder;
   5561         }
   5562 
   5563         private void attachAccessibilityDelegate(View itemView) {
   5564             if (isAccessibilityEnabled()) {
   5565                 if (itemView.getImportantForAccessibility()
   5566                         == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
   5567                     itemView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
   5568                 }
   5569 
   5570                 if (itemView.getAccessibilityDelegate() == null) {
   5571                     itemView.setAccessibilityDelegate(mAccessibilityDelegate.getItemDelegate());
   5572                 }
   5573             }
   5574         }
   5575 
   5576         private void invalidateDisplayListInt(ViewHolder holder) {
   5577             if (holder.itemView instanceof ViewGroup) {
   5578                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
   5579             }
   5580         }
   5581 
   5582         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
   5583             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
   5584                 final View view = viewGroup.getChildAt(i);
   5585                 if (view instanceof ViewGroup) {
   5586                     invalidateDisplayListInt((ViewGroup) view, true);
   5587                 }
   5588             }
   5589             if (!invalidateThis) {
   5590                 return;
   5591             }
   5592             // we need to force it to become invisible
   5593             if (viewGroup.getVisibility() == View.INVISIBLE) {
   5594                 viewGroup.setVisibility(View.VISIBLE);
   5595                 viewGroup.setVisibility(View.INVISIBLE);
   5596             } else {
   5597                 final int visibility = viewGroup.getVisibility();
   5598                 viewGroup.setVisibility(View.INVISIBLE);
   5599                 viewGroup.setVisibility(visibility);
   5600             }
   5601         }
   5602 
   5603         /**
   5604          * Recycle a detached view. The specified view will be added to a pool of views
   5605          * for later rebinding and reuse.
   5606          *
   5607          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
   5608          * View is scrapped, it will be removed from scrap list.</p>
   5609          *
   5610          * @param view Removed view for recycling
   5611          * @see LayoutManager#removeAndRecycleView(View, Recycler)
   5612          */
   5613         public void recycleView(View view) {
   5614             // This public recycle method tries to make view recycle-able since layout manager
   5615             // intended to recycle this view (e.g. even if it is in scrap or change cache)
   5616             ViewHolder holder = getChildViewHolderInt(view);
   5617             if (holder.isTmpDetached()) {
   5618                 removeDetachedView(view, false);
   5619             }
   5620             if (holder.isScrap()) {
   5621                 holder.unScrap();
   5622             } else if (holder.wasReturnedFromScrap()) {
   5623                 holder.clearReturnedFromScrapFlag();
   5624             }
   5625             recycleViewHolderInternal(holder);
   5626         }
   5627 
   5628         /**
   5629          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
   5630          * catch potential bugs.
   5631          * @param view
   5632          */
   5633         void recycleViewInternal(View view) {
   5634             recycleViewHolderInternal(getChildViewHolderInt(view));
   5635         }
   5636 
   5637         void recycleAndClearCachedViews() {
   5638             final int count = mCachedViews.size();
   5639             for (int i = count - 1; i >= 0; i--) {
   5640                 recycleCachedViewAt(i);
   5641             }
   5642             mCachedViews.clear();
   5643             if (ALLOW_THREAD_GAP_WORK) {
   5644                 mPrefetchRegistry.clearPrefetchPositions();
   5645             }
   5646         }
   5647 
   5648         /**
   5649          * Recycles a cached view and removes the view from the list. Views are added to cache
   5650          * if and only if they are recyclable, so this method does not check it again.
   5651          * <p>
   5652          * A small exception to this rule is when the view does not have an animator reference
   5653          * but transient state is true (due to animations created outside ItemAnimator). In that
   5654          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
   5655          * still recyclable since Adapter wants to do so.
   5656          *
   5657          * @param cachedViewIndex The index of the view in cached views list
   5658          */
   5659         void recycleCachedViewAt(int cachedViewIndex) {
   5660             if (DEBUG) {
   5661                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
   5662             }
   5663             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
   5664             if (DEBUG) {
   5665                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
   5666             }
   5667             addViewHolderToRecycledViewPool(viewHolder, true);
   5668             mCachedViews.remove(cachedViewIndex);
   5669         }
   5670 
   5671         /**
   5672          * internal implementation checks if view is scrapped or attached and throws an exception
   5673          * if so.
   5674          * Public version un-scraps before calling recycle.
   5675          */
   5676         void recycleViewHolderInternal(ViewHolder holder) {
   5677             if (holder.isScrap() || holder.itemView.getParent() != null) {
   5678                 throw new IllegalArgumentException(
   5679                         "Scrapped or attached views may not be recycled. isScrap:"
   5680                                 + holder.isScrap() + " isAttached:"
   5681                                 + (holder.itemView.getParent() != null));
   5682             }
   5683 
   5684             if (holder.isTmpDetached()) {
   5685                 throw new IllegalArgumentException("Tmp detached view should be removed "
   5686                         + "from RecyclerView before it can be recycled: " + holder);
   5687             }
   5688 
   5689             if (holder.shouldIgnore()) {
   5690                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
   5691                         + " should first call stopIgnoringView(view) before calling recycle.");
   5692             }
   5693             //noinspection unchecked
   5694             final boolean transientStatePreventsRecycling = holder
   5695                     .doesTransientStatePreventRecycling();
   5696             final boolean forceRecycle = mAdapter != null
   5697                     && transientStatePreventsRecycling
   5698                     && mAdapter.onFailedToRecycleView(holder);
   5699             boolean cached = false;
   5700             boolean recycled = false;
   5701             if (DEBUG && mCachedViews.contains(holder)) {
   5702                 throw new IllegalArgumentException("cached view received recycle internal? "
   5703                         + holder);
   5704             }
   5705             if (forceRecycle || holder.isRecyclable()) {
   5706                 if (mViewCacheMax > 0
   5707                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
   5708                                 | ViewHolder.FLAG_REMOVED
   5709                                 | ViewHolder.FLAG_UPDATE
   5710                                 | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
   5711                     // Retire oldest cached view
   5712                     int cachedViewSize = mCachedViews.size();
   5713                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
   5714                         recycleCachedViewAt(0);
   5715                         cachedViewSize--;
   5716                     }
   5717 
   5718                     int targetCacheIndex = cachedViewSize;
   5719                     if (ALLOW_THREAD_GAP_WORK
   5720                             && cachedViewSize > 0
   5721                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
   5722                         // when adding the view, skip past most recently prefetched views
   5723                         int cacheIndex = cachedViewSize - 1;
   5724                         while (cacheIndex >= 0) {
   5725                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
   5726                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
   5727                                 break;
   5728                             }
   5729                             cacheIndex--;
   5730                         }
   5731                         targetCacheIndex = cacheIndex + 1;
   5732                     }
   5733                     mCachedViews.add(targetCacheIndex, holder);
   5734                     cached = true;
   5735                 }
   5736                 if (!cached) {
   5737                     addViewHolderToRecycledViewPool(holder, true);
   5738                     recycled = true;
   5739                 }
   5740             } else {
   5741                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
   5742                 // runs. In this case, the item is eventually recycled by
   5743                 // ItemAnimatorRestoreListener#onAnimationFinished.
   5744 
   5745                 // TODO: consider cancelling an animation when an item is removed scrollBy,
   5746                 // to return it to the pool faster
   5747                 if (DEBUG) {
   5748                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
   5749                             + "re-visit here. We are still removing it from animation lists");
   5750                 }
   5751             }
   5752             // even if the holder is not removed, we still call this method so that it is removed
   5753             // from view holder lists.
   5754             mViewInfoStore.removeViewHolder(holder);
   5755             if (!cached && !recycled && transientStatePreventsRecycling) {
   5756                 holder.mOwnerRecyclerView = null;
   5757             }
   5758         }
   5759 
   5760         /**
   5761          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
   5762          *
   5763          * Pass false to dispatchRecycled for views that have not been bound.
   5764          *
   5765          * @param holder Holder to be added to the pool.
   5766          * @param dispatchRecycled True to dispatch View recycled callbacks.
   5767          */
   5768         void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
   5769             clearNestedRecyclerViewIfNotNested(holder);
   5770             holder.itemView.setAccessibilityDelegate(null);
   5771             if (dispatchRecycled) {
   5772                 dispatchViewRecycled(holder);
   5773             }
   5774             holder.mOwnerRecyclerView = null;
   5775             getRecycledViewPool().putRecycledView(holder);
   5776         }
   5777 
   5778         /**
   5779          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
   5780          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
   5781          * internal bookkeeping.
   5782          */
   5783         void quickRecycleScrapView(View view) {
   5784             final ViewHolder holder = getChildViewHolderInt(view);
   5785             holder.mScrapContainer = null;
   5786             holder.mInChangeScrap = false;
   5787             holder.clearReturnedFromScrapFlag();
   5788             recycleViewHolderInternal(holder);
   5789         }
   5790 
   5791         /**
   5792          * Mark an attached view as scrap.
   5793          *
   5794          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
   5795          * for rebinding and reuse. Requests for a view for a given position may return a
   5796          * reused or rebound scrap view instance.</p>
   5797          *
   5798          * @param view View to scrap
   5799          */
   5800         void scrapView(View view) {
   5801             final ViewHolder holder = getChildViewHolderInt(view);
   5802             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
   5803                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
   5804                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
   5805                     throw new IllegalArgumentException("Called scrap view with an invalid view."
   5806                             + " Invalid views cannot be reused from scrap, they should rebound from"
   5807                             + " recycler pool.");
   5808                 }
   5809                 holder.setScrapContainer(this, false);
   5810                 mAttachedScrap.add(holder);
   5811             } else {
   5812                 if (mChangedScrap == null) {
   5813                     mChangedScrap = new ArrayList<ViewHolder>();
   5814                 }
   5815                 holder.setScrapContainer(this, true);
   5816                 mChangedScrap.add(holder);
   5817             }
   5818         }
   5819 
   5820         /**
   5821          * Remove a previously scrapped view from the pool of eligible scrap.
   5822          *
   5823          * <p>This view will no longer be eligible for reuse until re-scrapped or
   5824          * until it is explicitly removed and recycled.</p>
   5825          */
   5826         void unscrapView(ViewHolder holder) {
   5827             if (holder.mInChangeScrap) {
   5828                 mChangedScrap.remove(holder);
   5829             } else {
   5830                 mAttachedScrap.remove(holder);
   5831             }
   5832             holder.mScrapContainer = null;
   5833             holder.mInChangeScrap = false;
   5834             holder.clearReturnedFromScrapFlag();
   5835         }
   5836 
   5837         int getScrapCount() {
   5838             return mAttachedScrap.size();
   5839         }
   5840 
   5841         View getScrapViewAt(int index) {
   5842             return mAttachedScrap.get(index).itemView;
   5843         }
   5844 
   5845         void clearScrap() {
   5846             mAttachedScrap.clear();
   5847             if (mChangedScrap != null) {
   5848                 mChangedScrap.clear();
   5849             }
   5850         }
   5851 
   5852         ViewHolder getChangedScrapViewForPosition(int position) {
   5853             // If pre-layout, check the changed scrap for an exact match.
   5854             final int changedScrapSize;
   5855             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
   5856                 return null;
   5857             }
   5858             // find by position
   5859             for (int i = 0; i < changedScrapSize; i++) {
   5860                 final ViewHolder holder = mChangedScrap.get(i);
   5861                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
   5862                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5863                     return holder;
   5864                 }
   5865             }
   5866             // find by id
   5867             if (mAdapter.hasStableIds()) {
   5868                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5869                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
   5870                     final long id = mAdapter.getItemId(offsetPosition);
   5871                     for (int i = 0; i < changedScrapSize; i++) {
   5872                         final ViewHolder holder = mChangedScrap.get(i);
   5873                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
   5874                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5875                             return holder;
   5876                         }
   5877                     }
   5878                 }
   5879             }
   5880             return null;
   5881         }
   5882 
   5883         /**
   5884          * Returns a view for the position either from attach scrap, hidden children, or cache.
   5885          *
   5886          * @param position Item position
   5887          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
   5888          * @return a ViewHolder that can be re-used for this position.
   5889          */
   5890         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
   5891             final int scrapCount = mAttachedScrap.size();
   5892 
   5893             // Try first for an exact, non-invalid match from scrap.
   5894             for (int i = 0; i < scrapCount; i++) {
   5895                 final ViewHolder holder = mAttachedScrap.get(i);
   5896                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
   5897                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
   5898                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5899                     return holder;
   5900                 }
   5901             }
   5902 
   5903             if (!dryRun) {
   5904                 View view = mChildHelper.findHiddenNonRemovedView(position);
   5905                 if (view != null) {
   5906                     // This View is good to be used. We just need to unhide, detach and move to the
   5907                     // scrap list.
   5908                     final ViewHolder vh = getChildViewHolderInt(view);
   5909                     mChildHelper.unhide(view);
   5910                     int layoutIndex = mChildHelper.indexOfChild(view);
   5911                     if (layoutIndex == RecyclerView.NO_POSITION) {
   5912                         throw new IllegalStateException("layout index should not be -1 after "
   5913                                 + "unhiding a view:" + vh);
   5914                     }
   5915                     mChildHelper.detachViewFromParent(layoutIndex);
   5916                     scrapView(view);
   5917                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
   5918                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   5919                     return vh;
   5920                 }
   5921             }
   5922 
   5923             // Search in our first-level recycled view cache.
   5924             final int cacheSize = mCachedViews.size();
   5925             for (int i = 0; i < cacheSize; i++) {
   5926                 final ViewHolder holder = mCachedViews.get(i);
   5927                 // invalid view holders may be in cache if adapter has stable ids as they can be
   5928                 // retrieved via getScrapOrCachedViewForId
   5929                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
   5930                     if (!dryRun) {
   5931                         mCachedViews.remove(i);
   5932                     }
   5933                     if (DEBUG) {
   5934                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
   5935                                 + ") found match in cache: " + holder);
   5936                     }
   5937                     return holder;
   5938                 }
   5939             }
   5940             return null;
   5941         }
   5942 
   5943         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
   5944             // Look in our attached views first
   5945             final int count = mAttachedScrap.size();
   5946             for (int i = count - 1; i >= 0; i--) {
   5947                 final ViewHolder holder = mAttachedScrap.get(i);
   5948                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
   5949                     if (type == holder.getItemViewType()) {
   5950                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   5951                         if (holder.isRemoved()) {
   5952                             // this might be valid in two cases:
   5953                             // > item is removed but we are in pre-layout pass
   5954                             // >> do nothing. return as is. make sure we don't rebind
   5955                             // > item is removed then added to another position and we are in
   5956                             // post layout.
   5957                             // >> remove removed and invalid flags, add update flag to rebind
   5958                             // because item was invisible to us and we don't know what happened in
   5959                             // between.
   5960                             if (!mState.isPreLayout()) {
   5961                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
   5962                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
   5963                             }
   5964                         }
   5965                         return holder;
   5966                     } else if (!dryRun) {
   5967                         // if we are running animations, it is actually better to keep it in scrap
   5968                         // but this would force layout manager to lay it out which would be bad.
   5969                         // Recycle this scrap. Type mismatch.
   5970                         mAttachedScrap.remove(i);
   5971                         removeDetachedView(holder.itemView, false);
   5972                         quickRecycleScrapView(holder.itemView);
   5973                     }
   5974                 }
   5975             }
   5976 
   5977             // Search the first-level cache
   5978             final int cacheSize = mCachedViews.size();
   5979             for (int i = cacheSize - 1; i >= 0; i--) {
   5980                 final ViewHolder holder = mCachedViews.get(i);
   5981                 if (holder.getItemId() == id) {
   5982                     if (type == holder.getItemViewType()) {
   5983                         if (!dryRun) {
   5984                             mCachedViews.remove(i);
   5985                         }
   5986                         return holder;
   5987                     } else if (!dryRun) {
   5988                         recycleCachedViewAt(i);
   5989                         return null;
   5990                     }
   5991                 }
   5992             }
   5993             return null;
   5994         }
   5995 
   5996         void dispatchViewRecycled(ViewHolder holder) {
   5997             if (mRecyclerListener != null) {
   5998                 mRecyclerListener.onViewRecycled(holder);
   5999             }
   6000             if (mAdapter != null) {
   6001                 mAdapter.onViewRecycled(holder);
   6002             }
   6003             if (mState != null) {
   6004                 mViewInfoStore.removeViewHolder(holder);
   6005             }
   6006             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
   6007         }
   6008 
   6009         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   6010                 boolean compatibleWithPrevious) {
   6011             clear();
   6012             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
   6013         }
   6014 
   6015         void offsetPositionRecordsForMove(int from, int to) {
   6016             final int start, end, inBetweenOffset;
   6017             if (from < to) {
   6018                 start = from;
   6019                 end = to;
   6020                 inBetweenOffset = -1;
   6021             } else {
   6022                 start = to;
   6023                 end = from;
   6024                 inBetweenOffset = 1;
   6025             }
   6026             final int cachedCount = mCachedViews.size();
   6027             for (int i = 0; i < cachedCount; i++) {
   6028                 final ViewHolder holder = mCachedViews.get(i);
   6029                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   6030                     continue;
   6031                 }
   6032                 if (holder.mPosition == from) {
   6033                     holder.offsetPosition(to - from, false);
   6034                 } else {
   6035                     holder.offsetPosition(inBetweenOffset, false);
   6036                 }
   6037                 if (DEBUG) {
   6038                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
   6039                             + holder);
   6040                 }
   6041             }
   6042         }
   6043 
   6044         void offsetPositionRecordsForInsert(int insertedAt, int count) {
   6045             final int cachedCount = mCachedViews.size();
   6046             for (int i = 0; i < cachedCount; i++) {
   6047                 final ViewHolder holder = mCachedViews.get(i);
   6048                 if (holder != null && holder.mPosition >= insertedAt) {
   6049                     if (DEBUG) {
   6050                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
   6051                                 + holder + " now at position " + (holder.mPosition + count));
   6052                     }
   6053                     holder.offsetPosition(count, true);
   6054                 }
   6055             }
   6056         }
   6057 
   6058         /**
   6059          * @param removedFrom Remove start index
   6060          * @param count Remove count
   6061          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
   6062          *                         false, they'll be applied before the second layout pass
   6063          */
   6064         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
   6065             final int removedEnd = removedFrom + count;
   6066             final int cachedCount = mCachedViews.size();
   6067             for (int i = cachedCount - 1; i >= 0; i--) {
   6068                 final ViewHolder holder = mCachedViews.get(i);
   6069                 if (holder != null) {
   6070                     if (holder.mPosition >= removedEnd) {
   6071                         if (DEBUG) {
   6072                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
   6073                                     + " holder " + holder + " now at position "
   6074                                     + (holder.mPosition - count));
   6075                         }
   6076                         holder.offsetPosition(-count, applyToPreLayout);
   6077                     } else if (holder.mPosition >= removedFrom) {
   6078                         // Item for this view was removed. Dump it from the cache.
   6079                         holder.addFlags(ViewHolder.FLAG_REMOVED);
   6080                         recycleCachedViewAt(i);
   6081                     }
   6082                 }
   6083             }
   6084         }
   6085 
   6086         void setViewCacheExtension(ViewCacheExtension extension) {
   6087             mViewCacheExtension = extension;
   6088         }
   6089 
   6090         void setRecycledViewPool(RecycledViewPool pool) {
   6091             if (mRecyclerPool != null) {
   6092                 mRecyclerPool.detach();
   6093             }
   6094             mRecyclerPool = pool;
   6095             if (pool != null) {
   6096                 mRecyclerPool.attach(getAdapter());
   6097             }
   6098         }
   6099 
   6100         RecycledViewPool getRecycledViewPool() {
   6101             if (mRecyclerPool == null) {
   6102                 mRecyclerPool = new RecycledViewPool();
   6103             }
   6104             return mRecyclerPool;
   6105         }
   6106 
   6107         void viewRangeUpdate(int positionStart, int itemCount) {
   6108             final int positionEnd = positionStart + itemCount;
   6109             final int cachedCount = mCachedViews.size();
   6110             for (int i = cachedCount - 1; i >= 0; i--) {
   6111                 final ViewHolder holder = mCachedViews.get(i);
   6112                 if (holder == null) {
   6113                     continue;
   6114                 }
   6115 
   6116                 final int pos = holder.getLayoutPosition();
   6117                 if (pos >= positionStart && pos < positionEnd) {
   6118                     holder.addFlags(ViewHolder.FLAG_UPDATE);
   6119                     recycleCachedViewAt(i);
   6120                     // cached views should not be flagged as changed because this will cause them
   6121                     // to animate when they are returned from cache.
   6122                 }
   6123             }
   6124         }
   6125 
   6126         void setAdapterPositionsAsUnknown() {
   6127             final int cachedCount = mCachedViews.size();
   6128             for (int i = 0; i < cachedCount; i++) {
   6129                 final ViewHolder holder = mCachedViews.get(i);
   6130                 if (holder != null) {
   6131                     holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   6132                 }
   6133             }
   6134         }
   6135 
   6136         void markKnownViewsInvalid() {
   6137             if (mAdapter != null && mAdapter.hasStableIds()) {
   6138                 final int cachedCount = mCachedViews.size();
   6139                 for (int i = 0; i < cachedCount; i++) {
   6140                     final ViewHolder holder = mCachedViews.get(i);
   6141                     if (holder != null) {
   6142                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   6143                         holder.addChangePayload(null);
   6144                     }
   6145                 }
   6146             } else {
   6147                 // we cannot re-use cached views in this case. Recycle them all
   6148                 recycleAndClearCachedViews();
   6149             }
   6150         }
   6151 
   6152         void clearOldPositions() {
   6153             final int cachedCount = mCachedViews.size();
   6154             for (int i = 0; i < cachedCount; i++) {
   6155                 final ViewHolder holder = mCachedViews.get(i);
   6156                 holder.clearOldPosition();
   6157             }
   6158             final int scrapCount = mAttachedScrap.size();
   6159             for (int i = 0; i < scrapCount; i++) {
   6160                 mAttachedScrap.get(i).clearOldPosition();
   6161             }
   6162             if (mChangedScrap != null) {
   6163                 final int changedScrapCount = mChangedScrap.size();
   6164                 for (int i = 0; i < changedScrapCount; i++) {
   6165                     mChangedScrap.get(i).clearOldPosition();
   6166                 }
   6167             }
   6168         }
   6169 
   6170         void markItemDecorInsetsDirty() {
   6171             final int cachedCount = mCachedViews.size();
   6172             for (int i = 0; i < cachedCount; i++) {
   6173                 final ViewHolder holder = mCachedViews.get(i);
   6174                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
   6175                 if (layoutParams != null) {
   6176                     layoutParams.mInsetsDirty = true;
   6177                 }
   6178             }
   6179         }
   6180     }
   6181 
   6182     /**
   6183      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
   6184      * be controlled by the developer.
   6185      * <p>
   6186      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
   6187      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
   6188      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
   6189      * {@link RecycledViewPool}.
   6190      * <p>
   6191      * Note that, Recycler never sends Views to this method to be cached. It is developers
   6192      * responsibility to decide whether they want to keep their Views in this custom cache or let
   6193      * the default recycling policy handle it.
   6194      */
   6195     public abstract static class ViewCacheExtension {
   6196 
   6197         /**
   6198          * Returns a View that can be binded to the given Adapter position.
   6199          * <p>
   6200          * This method should <b>not</b> create a new View. Instead, it is expected to return
   6201          * an already created View that can be re-used for the given type and position.
   6202          * If the View is marked as ignored, it should first call
   6203          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
   6204          * <p>
   6205          * RecyclerView will re-bind the returned View to the position if necessary.
   6206          *
   6207          * @param recycler The Recycler that can be used to bind the View
   6208          * @param position The adapter position
   6209          * @param type     The type of the View, defined by adapter
   6210          * @return A View that is bound to the given position or NULL if there is no View to re-use
   6211          * @see LayoutManager#ignoreView(View)
   6212          */
   6213         public abstract View getViewForPositionAndType(Recycler recycler, int position, int type);
   6214     }
   6215 
   6216     /**
   6217      * Base class for an Adapter
   6218      *
   6219      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
   6220      * within a {@link RecyclerView}.</p>
   6221      *
   6222      * @param  A class that extends ViewHolder that will be used by the adapter.
   6223      */
   6224     public abstract static class Adapter<VH extends ViewHolder> {
   6225         private final AdapterDataObservable mObservable = new AdapterDataObservable();
   6226         private boolean mHasStableIds = false;
   6227 
   6228         /**
   6229          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
   6230          * an item.
   6231          * <p>
   6232          * This new ViewHolder should be constructed with a new View that can represent the items
   6233          * of the given type. You can either create a new View manually or inflate it from an XML
   6234          * layout file.
   6235          * <p>
   6236          * The new ViewHolder will be used to display items of the adapter using
   6237          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
   6238          * different items in the data set, it is a good idea to cache references to sub views of
   6239          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
   6240          *
   6241          * @param parent The ViewGroup into which the new View will be added after it is bound to
   6242          *               an adapter position.
   6243          * @param viewType The view type of the new View.
   6244          *
   6245          * @return A new ViewHolder that holds a View of the given view type.
   6246          * @see #getItemViewType(int)
   6247          * @see #onBindViewHolder(ViewHolder, int)
   6248          */
   6249         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
   6250 
   6251         /**
   6252          * Called by RecyclerView to display the data at the specified position. This method should
   6253          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
   6254          * position.
   6255          * <p>
   6256          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   6257          * again if the position of the item changes in the data set unless the item itself is
   6258          * invalidated or the new position cannot be determined. For this reason, you should only
   6259          * use the <code>position</code> parameter while acquiring the related data item inside
   6260          * this method and should not keep a copy of it. If you need the position of an item later
   6261          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   6262          * have the updated adapter position.
   6263          *
   6264          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
   6265          * handle efficient partial bind.
   6266          *
   6267          * @param holder The ViewHolder which should be updated to represent the contents of the
   6268          *        item at the given position in the data set.
   6269          * @param position The position of the item within the adapter's data set.
   6270          */
   6271         public abstract void onBindViewHolder(VH holder, int position);
   6272 
   6273         /**
   6274          * Called by RecyclerView to display the data at the specified position. This method
   6275          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
   6276          * the given position.
   6277          * <p>
   6278          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   6279          * again if the position of the item changes in the data set unless the item itself is
   6280          * invalidated or the new position cannot be determined. For this reason, you should only
   6281          * use the <code>position</code> parameter while acquiring the related data item inside
   6282          * this method and should not keep a copy of it. If you need the position of an item later
   6283          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   6284          * have the updated adapter position.
   6285          * <p>
   6286          * Partial bind vs full bind:
   6287          * <p>
   6288          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
   6289          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
   6290          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
   6291          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
   6292          * Adapter should not assume that the payload passed in notify methods will be received by
   6293          * onBindViewHolder().  For example when the view is not attached to the screen, the
   6294          * payload in notifyItemChange() will be simply dropped.
   6295          *
   6296          * @param holder The ViewHolder which should be updated to represent the contents of the
   6297          *               item at the given position in the data set.
   6298          * @param position The position of the item within the adapter's data set.
   6299          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
   6300          *                 update.
   6301          */
   6302         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
   6303             onBindViewHolder(holder, position);
   6304         }
   6305 
   6306         /**
   6307          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
   6308          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
   6309          *
   6310          * @see #onCreateViewHolder(ViewGroup, int)
   6311          */
   6312         public final VH createViewHolder(ViewGroup parent, int viewType) {
   6313             Trace.beginSection(TRACE_CREATE_VIEW_TAG);
   6314             final VH holder = onCreateViewHolder(parent, viewType);
   6315             holder.mItemViewType = viewType;
   6316             Trace.endSection();
   6317             return holder;
   6318         }
   6319 
   6320         /**
   6321          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
   6322          * {@link ViewHolder} contents with the item at the given position and also sets up some
   6323          * private fields to be used by RecyclerView.
   6324          *
   6325          * @see #onBindViewHolder(ViewHolder, int)
   6326          */
   6327         public final void bindViewHolder(VH holder, int position) {
   6328             holder.mPosition = position;
   6329             if (hasStableIds()) {
   6330                 holder.mItemId = getItemId(position);
   6331             }
   6332             holder.setFlags(ViewHolder.FLAG_BOUND,
   6333                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
   6334                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   6335             Trace.beginSection(TRACE_BIND_VIEW_TAG);
   6336             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
   6337             holder.clearPayload();
   6338             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
   6339             if (layoutParams instanceof RecyclerView.LayoutParams) {
   6340                 ((LayoutParams) layoutParams).mInsetsDirty = true;
   6341             }
   6342             Trace.endSection();
   6343         }
   6344 
   6345         /**
   6346          * Return the view type of the item at <code>position</code> for the purposes
   6347          * of view recycling.
   6348          *
   6349          * <p>The default implementation of this method returns 0, making the assumption of
   6350          * a single view type for the adapter. Unlike ListView adapters, types need not
   6351          * be contiguous. Consider using id resources to uniquely identify item view types.
   6352          *
   6353          * @param position position to query
   6354          * @return integer value identifying the type of the view needed to represent the item at
   6355          *                 <code>position</code>. Type codes need not be contiguous.
   6356          */
   6357         public int getItemViewType(int position) {
   6358             return 0;
   6359         }
   6360 
   6361         /**
   6362          * Indicates whether each item in the data set can be represented with a unique identifier
   6363          * of type {@link java.lang.Long}.
   6364          *
   6365          * @param hasStableIds Whether items in data set have unique identifiers or not.
   6366          * @see #hasStableIds()
   6367          * @see #getItemId(int)
   6368          */
   6369         public void setHasStableIds(boolean hasStableIds) {
   6370             if (hasObservers()) {
   6371                 throw new IllegalStateException("Cannot change whether this adapter has "
   6372                         + "stable IDs while the adapter has registered observers.");
   6373             }
   6374             mHasStableIds = hasStableIds;
   6375         }
   6376 
   6377         /**
   6378          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
   6379          * would return false this method should return {@link #NO_ID}. The default implementation
   6380          * of this method returns {@link #NO_ID}.
   6381          *
   6382          * @param position Adapter position to query
   6383          * @return the stable ID of the item at position
   6384          */
   6385         public long getItemId(int position) {
   6386             return NO_ID;
   6387         }
   6388 
   6389         /**
   6390          * Returns the total number of items in the data set held by the adapter.
   6391          *
   6392          * @return The total number of items in this adapter.
   6393          */
   6394         public abstract int getItemCount();
   6395 
   6396         /**
   6397          * Returns true if this adapter publishes a unique <code>long</code> value that can
   6398          * act as a key for the item at a given position in the data set. If that item is relocated
   6399          * in the data set, the ID returned for that item should be the same.
   6400          *
   6401          * @return true if this adapter's items have stable IDs
   6402          */
   6403         public final boolean hasStableIds() {
   6404             return mHasStableIds;
   6405         }
   6406 
   6407         /**
   6408          * Called when a view created by this adapter has been recycled.
   6409          *
   6410          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
   6411          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
   6412          * fallen out of visibility or a set of cached views represented by views still
   6413          * attached to the parent RecyclerView. If an item view has large or expensive data
   6414          * bound to it such as large bitmaps, this may be a good place to release those
   6415          * resources.</p>
   6416          * <p>
   6417          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   6418          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   6419          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   6420          * its adapter position.
   6421          *
   6422          * @param holder The ViewHolder for the view being recycled
   6423          */
   6424         public void onViewRecycled(VH holder) {
   6425         }
   6426 
   6427         /**
   6428          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
   6429          * due to its transient state. Upon receiving this callback, Adapter can clear the
   6430          * animation(s) that effect the View's transient state and return <code>true</code> so that
   6431          * the View can be recycled. Keep in mind that the View in question is already removed from
   6432          * the RecyclerView.
   6433          * <p>
   6434          * In some cases, it is acceptable to recycle a View although it has transient state. Most
   6435          * of the time, this is a case where the transient state will be cleared in
   6436          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
   6437          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
   6438          * value of this method to decide whether the View should be recycled or not.
   6439          * <p>
   6440          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
   6441          * should never receive this callback because RecyclerView keeps those Views as children
   6442          * until their animations are complete. This callback is useful when children of the item
   6443          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
   6444          * <p>
   6445          * You should <em>never</em> fix this issue by calling
   6446          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
   6447          * <code>holder.itemView.setHasTransientState(true);</code>. Each
   6448          * <code>View.setHasTransientState(true)</code> call must be matched by a
   6449          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
   6450          * may become inconsistent. You should always prefer to end or cancel animations that are
   6451          * triggering the transient state instead of handling it manually.
   6452          *
   6453          * @param holder The ViewHolder containing the View that could not be recycled due to its
   6454          *               transient state.
   6455          * @return True if the View should be recycled, false otherwise. Note that if this method
   6456          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
   6457          * the View and recycle it regardless. If this method returns <code>false</code>,
   6458          * RecyclerView will check the View's transient state again before giving a final decision.
   6459          * Default implementation returns false.
   6460          */
   6461         public boolean onFailedToRecycleView(VH holder) {
   6462             return false;
   6463         }
   6464 
   6465         /**
   6466          * Called when a view created by this adapter has been attached to a window.
   6467          *
   6468          * <p>This can be used as a reasonable signal that the view is about to be seen
   6469          * by the user. If the adapter previously freed any resources in
   6470          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
   6471          * those resources should be restored here.</p>
   6472          *
   6473          * @param holder Holder of the view being attached
   6474          */
   6475         public void onViewAttachedToWindow(VH holder) {
   6476         }
   6477 
   6478         /**
   6479          * Called when a view created by this adapter has been detached from its window.
   6480          *
   6481          * <p>Becoming detached from the window is not necessarily a permanent condition;
   6482          * the consumer of an Adapter's views may choose to cache views offscreen while they
   6483          * are not visible, attaching and detaching them as appropriate.</p>
   6484          *
   6485          * @param holder Holder of the view being detached
   6486          */
   6487         public void onViewDetachedFromWindow(VH holder) {
   6488         }
   6489 
   6490         /**
   6491          * Returns true if one or more observers are attached to this adapter.
   6492          *
   6493          * @return true if this adapter has observers
   6494          */
   6495         public final boolean hasObservers() {
   6496             return mObservable.hasObservers();
   6497         }
   6498 
   6499         /**
   6500          * Register a new observer to listen for data changes.
   6501          *
   6502          * <p>The adapter may publish a variety of events describing specific changes.
   6503          * Not all adapters may support all change types and some may fall back to a generic
   6504          * {@link com.android.internal.widget.RecyclerView.AdapterDataObserver#onChanged()
   6505          * "something changed"} event if more specific data is not available.</p>
   6506          *
   6507          * <p>Components registering observers with an adapter are responsible for
   6508          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6509          * unregistering} those observers when finished.</p>
   6510          *
   6511          * @param observer Observer to register
   6512          *
   6513          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6514          */
   6515         public void registerAdapterDataObserver(AdapterDataObserver observer) {
   6516             mObservable.registerObserver(observer);
   6517         }
   6518 
   6519         /**
   6520          * Unregister an observer currently listening for data changes.
   6521          *
   6522          * <p>The unregistered observer will no longer receive events about changes
   6523          * to the adapter.</p>
   6524          *
   6525          * @param observer Observer to unregister
   6526          *
   6527          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6528          */
   6529         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
   6530             mObservable.unregisterObserver(observer);
   6531         }
   6532 
   6533         /**
   6534          * Called by RecyclerView when it starts observing this Adapter.
   6535          * <p>
   6536          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
   6537          *
   6538          * @param recyclerView The RecyclerView instance which started observing this adapter.
   6539          * @see #onDetachedFromRecyclerView(RecyclerView)
   6540          */
   6541         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
   6542         }
   6543 
   6544         /**
   6545          * Called by RecyclerView when it stops observing this Adapter.
   6546          *
   6547          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
   6548          * @see #onAttachedToRecyclerView(RecyclerView)
   6549          */
   6550         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
   6551         }
   6552 
   6553         /**
   6554          * Notify any registered observers that the data set has changed.
   6555          *
   6556          * <p>There are two different classes of data change events, item changes and structural
   6557          * changes. Item changes are when a single item has its data updated but no positional
   6558          * changes have occurred. Structural changes are when items are inserted, removed or moved
   6559          * within the data set.</p>
   6560          *
   6561          * <p>This event does not specify what about the data set has changed, forcing
   6562          * any observers to assume that all existing items and structure may no longer be valid.
   6563          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
   6564          *
   6565          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
   6566          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
   6567          * this method is used. This can help for the purposes of animation and visual
   6568          * object persistence but individual item views will still need to be rebound
   6569          * and relaid out.</p>
   6570          *
   6571          * <p>If you are writing an adapter it will always be more efficient to use the more
   6572          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
   6573          * as a last resort.</p>
   6574          *
   6575          * @see #notifyItemChanged(int)
   6576          * @see #notifyItemInserted(int)
   6577          * @see #notifyItemRemoved(int)
   6578          * @see #notifyItemRangeChanged(int, int)
   6579          * @see #notifyItemRangeInserted(int, int)
   6580          * @see #notifyItemRangeRemoved(int, int)
   6581          */
   6582         public final void notifyDataSetChanged() {
   6583             mObservable.notifyChanged();
   6584         }
   6585 
   6586         /**
   6587          * Notify any registered observers that the item at <code>position</code> has changed.
   6588          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
   6589          *
   6590          * <p>This is an item change event, not a structural change event. It indicates that any
   6591          * reflection of the data at <code>position</code> is out of date and should be updated.
   6592          * The item at <code>position</code> retains the same identity.</p>
   6593          *
   6594          * @param position Position of the item that has changed
   6595          *
   6596          * @see #notifyItemRangeChanged(int, int)
   6597          */
   6598         public final void notifyItemChanged(int position) {
   6599             mObservable.notifyItemRangeChanged(position, 1);
   6600         }
   6601 
   6602         /**
   6603          * Notify any registered observers that the item at <code>position</code> has changed with
   6604          * an optional payload object.
   6605          *
   6606          * <p>This is an item change event, not a structural change event. It indicates that any
   6607          * reflection of the data at <code>position</code> is out of date and should be updated.
   6608          * The item at <code>position</code> retains the same identity.
   6609          * </p>
   6610          *
   6611          * <p>
   6612          * Client can optionally pass a payload for partial change. These payloads will be merged
   6613          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   6614          * item is already represented by a ViewHolder and it will be rebound to the same
   6615          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   6616          * payloads on that item and prevent future payload until
   6617          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   6618          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   6619          * attached, the payload will be simply dropped.
   6620          *
   6621          * @param position Position of the item that has changed
   6622          * @param payload Optional parameter, use null to identify a "full" update
   6623          *
   6624          * @see #notifyItemRangeChanged(int, int)
   6625          */
   6626         public final void notifyItemChanged(int position, Object payload) {
   6627             mObservable.notifyItemRangeChanged(position, 1, payload);
   6628         }
   6629 
   6630         /**
   6631          * Notify any registered observers that the <code>itemCount</code> items starting at
   6632          * position <code>positionStart</code> have changed.
   6633          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
   6634          *
   6635          * <p>This is an item change event, not a structural change event. It indicates that
   6636          * any reflection of the data in the given position range is out of date and should
   6637          * be updated. The items in the given range retain the same identity.</p>
   6638          *
   6639          * @param positionStart Position of the first item that has changed
   6640          * @param itemCount Number of items that have changed
   6641          *
   6642          * @see #notifyItemChanged(int)
   6643          */
   6644         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
   6645             mObservable.notifyItemRangeChanged(positionStart, itemCount);
   6646         }
   6647 
   6648         /**
   6649          * Notify any registered observers that the <code>itemCount</code> items starting at
   6650          * position <code>positionStart</code> have changed. An optional payload can be
   6651          * passed to each changed item.
   6652          *
   6653          * <p>This is an item change event, not a structural change event. It indicates that any
   6654          * reflection of the data in the given position range is out of date and should be updated.
   6655          * The items in the given range retain the same identity.
   6656          * </p>
   6657          *
   6658          * <p>
   6659          * Client can optionally pass a payload for partial change. These payloads will be merged
   6660          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   6661          * item is already represented by a ViewHolder and it will be rebound to the same
   6662          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   6663          * payloads on that item and prevent future payload until
   6664          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   6665          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   6666          * attached, the payload will be simply dropped.
   6667          *
   6668          * @param positionStart Position of the first item that has changed
   6669          * @param itemCount Number of items that have changed
   6670          * @param payload  Optional parameter, use null to identify a "full" update
   6671          *
   6672          * @see #notifyItemChanged(int)
   6673          */
   6674         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
   6675             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
   6676         }
   6677 
   6678         /**
   6679          * Notify any registered observers that the item reflected at <code>position</code>
   6680          * has been newly inserted. The item previously at <code>position</code> is now at
   6681          * position <code>position + 1</code>.
   6682          *
   6683          * <p>This is a structural change event. Representations of other existing items in the
   6684          * data set are still considered up to date and will not be rebound, though their
   6685          * positions may be altered.</p>
   6686          *
   6687          * @param position Position of the newly inserted item in the data set
   6688          *
   6689          * @see #notifyItemRangeInserted(int, int)
   6690          */
   6691         public final void notifyItemInserted(int position) {
   6692             mObservable.notifyItemRangeInserted(position, 1);
   6693         }
   6694 
   6695         /**
   6696          * Notify any registered observers that the item reflected at <code>fromPosition</code>
   6697          * has been moved to <code>toPosition</code>.
   6698          *
   6699          * <p>This is a structural change event. Representations of other existing items in the
   6700          * data set are still considered up to date and will not be rebound, though their
   6701          * positions may be altered.</p>
   6702          *
   6703          * @param fromPosition Previous position of the item.
   6704          * @param toPosition New position of the item.
   6705          */
   6706         public final void notifyItemMoved(int fromPosition, int toPosition) {
   6707             mObservable.notifyItemMoved(fromPosition, toPosition);
   6708         }
   6709 
   6710         /**
   6711          * Notify any registered observers that the currently reflected <code>itemCount</code>
   6712          * items starting at <code>positionStart</code> have been newly inserted. The items
   6713          * previously located at <code>positionStart</code> and beyond can now be found starting
   6714          * at position <code>positionStart + itemCount</code>.
   6715          *
   6716          * <p>This is a structural change event. Representations of other existing items in the
   6717          * data set are still considered up to date and will not be rebound, though their positions
   6718          * may be altered.</p>
   6719          *
   6720          * @param positionStart Position of the first item that was inserted
   6721          * @param itemCount Number of items inserted
   6722          *
   6723          * @see #notifyItemInserted(int)
   6724          */
   6725         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
   6726             mObservable.notifyItemRangeInserted(positionStart, itemCount);
   6727         }
   6728 
   6729         /**
   6730          * Notify any registered observers that the item previously located at <code>position</code>
   6731          * has been removed from the data set. The items previously located at and after
   6732          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
   6733          *
   6734          * <p>This is a structural change event. Representations of other existing items in the
   6735          * data set are still considered up to date and will not be rebound, though their positions
   6736          * may be altered.</p>
   6737          *
   6738          * @param position Position of the item that has now been removed
   6739          *
   6740          * @see #notifyItemRangeRemoved(int, int)
   6741          */
   6742         public final void notifyItemRemoved(int position) {
   6743             mObservable.notifyItemRangeRemoved(position, 1);
   6744         }
   6745 
   6746         /**
   6747          * Notify any registered observers that the <code>itemCount</code> items previously
   6748          * located at <code>positionStart</code> have been removed from the data set. The items
   6749          * previously located at and after <code>positionStart + itemCount</code> may now be found
   6750          * at <code>oldPosition - itemCount</code>.
   6751          *
   6752          * <p>This is a structural change event. Representations of other existing items in the data
   6753          * set are still considered up to date and will not be rebound, though their positions
   6754          * may be altered.</p>
   6755          *
   6756          * @param positionStart Previous position of the first item that was removed
   6757          * @param itemCount Number of items removed from the data set
   6758          */
   6759         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
   6760             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
   6761         }
   6762     }
   6763 
   6764     void dispatchChildDetached(View child) {
   6765         final ViewHolder viewHolder = getChildViewHolderInt(child);
   6766         onChildDetachedFromWindow(child);
   6767         if (mAdapter != null && viewHolder != null) {
   6768             mAdapter.onViewDetachedFromWindow(viewHolder);
   6769         }
   6770         if (mOnChildAttachStateListeners != null) {
   6771             final int cnt = mOnChildAttachStateListeners.size();
   6772             for (int i = cnt - 1; i >= 0; i--) {
   6773                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
   6774             }
   6775         }
   6776     }
   6777 
   6778     void dispatchChildAttached(View child) {
   6779         final ViewHolder viewHolder = getChildViewHolderInt(child);
   6780         onChildAttachedToWindow(child);
   6781         if (mAdapter != null && viewHolder != null) {
   6782             mAdapter.onViewAttachedToWindow(viewHolder);
   6783         }
   6784         if (mOnChildAttachStateListeners != null) {
   6785             final int cnt = mOnChildAttachStateListeners.size();
   6786             for (int i = cnt - 1; i >= 0; i--) {
   6787                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
   6788             }
   6789         }
   6790     }
   6791 
   6792     /**
   6793      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
   6794      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
   6795      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
   6796      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
   6797      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
   6798      * layout managers are provided for general use.
   6799      * <p/>
   6800      * If the LayoutManager specifies a default constructor or one with the signature
   6801      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
   6802      * instantiate and set the LayoutManager when being inflated. Most used properties can
   6803      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
   6804      * a LayoutManager specifies both constructors, the non-default constructor will take
   6805      * precedence.
   6806      *
   6807      */
   6808     public abstract static class LayoutManager {
   6809         ChildHelper mChildHelper;
   6810         RecyclerView mRecyclerView;
   6811 
   6812         @Nullable
   6813         SmoothScroller mSmoothScroller;
   6814 
   6815         boolean mRequestedSimpleAnimations = false;
   6816 
   6817         boolean mIsAttachedToWindow = false;
   6818 
   6819         boolean mAutoMeasure = false;
   6820 
   6821         /**
   6822          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
   6823          * if the space that will be given to it is already larger than what it has measured before.
   6824          */
   6825         private boolean mMeasurementCacheEnabled = true;
   6826 
   6827         private boolean mItemPrefetchEnabled = true;
   6828 
   6829         /**
   6830          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
   6831          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
   6832          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
   6833          *
   6834          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
   6835          * will be reset upon layout to prevent initial prefetches (often large, since they're
   6836          * proportional to expected child count) from expanding cache permanently.
   6837          */
   6838         int mPrefetchMaxCountObserved;
   6839 
   6840         /**
   6841          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
   6842          */
   6843         boolean mPrefetchMaxObservedInInitialPrefetch;
   6844 
   6845         /**
   6846          * These measure specs might be the measure specs that were passed into RecyclerView's
   6847          * onMeasure method OR fake measure specs created by the RecyclerView.
   6848          * For example, when a layout is run, RecyclerView always sets these specs to be
   6849          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
   6850          * <p>
   6851          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
   6852          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
   6853          * corrupt values. Older platforms have no responsibility to provide a size if they set
   6854          * mode to unspecified.
   6855          */
   6856         private int mWidthMode, mHeightMode;
   6857         private int mWidth, mHeight;
   6858 
   6859 
   6860         /**
   6861          * Interface for LayoutManagers to request items to be prefetched, based on position, with
   6862          * specified distance from viewport, which indicates priority.
   6863          *
   6864          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
   6865          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
   6866          */
   6867         public interface LayoutPrefetchRegistry {
   6868             /**
   6869              * Requests an an item to be prefetched, based on position, with a specified distance,
   6870              * indicating priority.
   6871              *
   6872              * @param layoutPosition Position of the item to prefetch.
   6873              * @param pixelDistance Distance from the current viewport to the bounds of the item,
   6874              *                      must be non-negative.
   6875              */
   6876             void addPosition(int layoutPosition, int pixelDistance);
   6877         }
   6878 
   6879         void setRecyclerView(RecyclerView recyclerView) {
   6880             if (recyclerView == null) {
   6881                 mRecyclerView = null;
   6882                 mChildHelper = null;
   6883                 mWidth = 0;
   6884                 mHeight = 0;
   6885             } else {
   6886                 mRecyclerView = recyclerView;
   6887                 mChildHelper = recyclerView.mChildHelper;
   6888                 mWidth = recyclerView.getWidth();
   6889                 mHeight = recyclerView.getHeight();
   6890             }
   6891             mWidthMode = MeasureSpec.EXACTLY;
   6892             mHeightMode = MeasureSpec.EXACTLY;
   6893         }
   6894 
   6895         void setMeasureSpecs(int wSpec, int hSpec) {
   6896             mWidth = MeasureSpec.getSize(wSpec);
   6897             mWidthMode = MeasureSpec.getMode(wSpec);
   6898             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
   6899                 mWidth = 0;
   6900             }
   6901 
   6902             mHeight = MeasureSpec.getSize(hSpec);
   6903             mHeightMode = MeasureSpec.getMode(hSpec);
   6904             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
   6905                 mHeight = 0;
   6906             }
   6907         }
   6908 
   6909         /**
   6910          * Called after a layout is calculated during a measure pass when using auto-measure.
   6911          * <p>
   6912          * It simply traverses all children to calculate a bounding box then calls
   6913          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
   6914          * if they need to handle the bounding box differently.
   6915          * <p>
   6916          * For example, GridLayoutManager override that method to ensure that even if a column is
   6917          * empty, the GridLayoutManager still measures wide enough to include it.
   6918          *
   6919          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
   6920          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
   6921          */
   6922         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
   6923             final int count = getChildCount();
   6924             if (count == 0) {
   6925                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
   6926                 return;
   6927             }
   6928             int minX = Integer.MAX_VALUE;
   6929             int minY = Integer.MAX_VALUE;
   6930             int maxX = Integer.MIN_VALUE;
   6931             int maxY = Integer.MIN_VALUE;
   6932 
   6933             for (int i = 0; i < count; i++) {
   6934                 View child = getChildAt(i);
   6935                 final Rect bounds = mRecyclerView.mTempRect;
   6936                 getDecoratedBoundsWithMargins(child, bounds);
   6937                 if (bounds.left < minX) {
   6938                     minX = bounds.left;
   6939                 }
   6940                 if (bounds.right > maxX) {
   6941                     maxX = bounds.right;
   6942                 }
   6943                 if (bounds.top < minY) {
   6944                     minY = bounds.top;
   6945                 }
   6946                 if (bounds.bottom > maxY) {
   6947                     maxY = bounds.bottom;
   6948                 }
   6949             }
   6950             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
   6951             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
   6952         }
   6953 
   6954         /**
   6955          * Sets the measured dimensions from the given bounding box of the children and the
   6956          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
   6957          * called after the RecyclerView calls
   6958          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a measurement pass.
   6959          * <p>
   6960          * This method should call {@link #setMeasuredDimension(int, int)}.
   6961          * <p>
   6962          * The default implementation adds the RecyclerView's padding to the given bounding box
   6963          * then caps the value to be within the given measurement specs.
   6964          * <p>
   6965          * This method is only called if the LayoutManager opted into the auto measurement API.
   6966          *
   6967          * @param childrenBounds The bounding box of all children
   6968          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
   6969          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
   6970          *
   6971          * @see #setAutoMeasureEnabled(boolean)
   6972          */
   6973         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
   6974             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
   6975             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
   6976             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
   6977             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
   6978             setMeasuredDimension(width, height);
   6979         }
   6980 
   6981         /**
   6982          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
   6983          */
   6984         public void requestLayout() {
   6985             if (mRecyclerView != null) {
   6986                 mRecyclerView.requestLayout();
   6987             }
   6988         }
   6989 
   6990         /**
   6991          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   6992          * {@link IllegalStateException} if it <b>is not</b>.
   6993          *
   6994          * @param message The message for the exception. Can be null.
   6995          * @see #assertNotInLayoutOrScroll(String)
   6996          */
   6997         public void assertInLayoutOrScroll(String message) {
   6998             if (mRecyclerView != null) {
   6999                 mRecyclerView.assertInLayoutOrScroll(message);
   7000             }
   7001         }
   7002 
   7003         /**
   7004          * Chooses a size from the given specs and parameters that is closest to the desired size
   7005          * and also complies with the spec.
   7006          *
   7007          * @param spec The measureSpec
   7008          * @param desired The preferred measurement
   7009          * @param min The minimum value
   7010          *
   7011          * @return A size that fits to the given specs
   7012          */
   7013         public static int chooseSize(int spec, int desired, int min) {
   7014             final int mode = View.MeasureSpec.getMode(spec);
   7015             final int size = View.MeasureSpec.getSize(spec);
   7016             switch (mode) {
   7017                 case View.MeasureSpec.EXACTLY:
   7018                     return size;
   7019                 case View.MeasureSpec.AT_MOST:
   7020                     return Math.min(size, Math.max(desired, min));
   7021                 case View.MeasureSpec.UNSPECIFIED:
   7022                 default:
   7023                     return Math.max(desired, min);
   7024             }
   7025         }
   7026 
   7027         /**
   7028          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   7029          * {@link IllegalStateException} if it <b>is</b>.
   7030          *
   7031          * @param message The message for the exception. Can be null.
   7032          * @see #assertInLayoutOrScroll(String)
   7033          */
   7034         public void assertNotInLayoutOrScroll(String message) {
   7035             if (mRecyclerView != null) {
   7036                 mRecyclerView.assertNotInLayoutOrScroll(message);
   7037             }
   7038         }
   7039 
   7040         /**
   7041          * Defines whether the layout should be measured by the RecyclerView or the LayoutManager
   7042          * wants to handle the layout measurements itself.
   7043          * <p>
   7044          * This method is usually called by the LayoutManager with value {@code true} if it wants
   7045          * to support WRAP_CONTENT. If you are using a public LayoutManager but want to customize
   7046          * the measurement logic, you can call this method with {@code false} and override
   7047          * {@link LayoutManager#onMeasure(int, int)} to implement your custom measurement logic.
   7048          * <p>
   7049          * AutoMeasure is a convenience mechanism for LayoutManagers to easily wrap their content or
   7050          * handle various specs provided by the RecyclerView's parent.
   7051          * It works by calling {@link LayoutManager#onLayoutChildren(Recycler, State)} during an
   7052          * {@link RecyclerView#onMeasure(int, int)} call, then calculating desired dimensions based
   7053          * on children's positions. It does this while supporting all existing animation
   7054          * capabilities of the RecyclerView.
   7055          * <p>
   7056          * AutoMeasure works as follows:
   7057          * <ol>
   7058          * <li>LayoutManager should call {@code setAutoMeasureEnabled(true)} to enable it. All of
   7059          * the framework LayoutManagers use {@code auto-measure}.</li>
   7060          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided specs are
   7061          * exact, RecyclerView will only call LayoutManager's {@code onMeasure} and return without
   7062          * doing any layout calculation.</li>
   7063          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
   7064          * layout process in {@code onMeasure} call. It will process all pending Adapter updates and
   7065          * decide whether to run a predictive layout or not. If it decides to do so, it will first
   7066          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
   7067          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
   7068          * return the width and height of the RecyclerView as of the last layout calculation.
   7069          * <p>
   7070          * After handling the predictive case, RecyclerView will call
   7071          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
   7072          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
   7073          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
   7074          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
   7075          * <li>After the layout calculation, RecyclerView sets the measured width & height by
   7076          * calculating the bounding box for the children (+ RecyclerView's padding). The
   7077          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
   7078          * different values. For instance, GridLayoutManager overrides this value to handle the case
   7079          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
   7080          * width to fit 3 items, not 2.</li>
   7081          * <li>Any following on measure call to the RecyclerView will run
   7082          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
   7083          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
   7084          * take care of which views are actually added / removed / moved / changed for animations so
   7085          * that the LayoutManager should not worry about them and handle each
   7086          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.
   7087          * </li>
   7088          * <li>When measure is complete and RecyclerView's
   7089          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
   7090          * whether it already did layout calculations during the measure pass and if so, it re-uses
   7091          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
   7092          * if the last measure spec was different from the final dimensions or adapter contents
   7093          * have changed between the measure call and the layout call.</li>
   7094          * <li>Finally, animations are calculated and run as usual.</li>
   7095          * </ol>
   7096          *
   7097          * @param enabled <code>True</code> if the Layout should be measured by the
   7098          *                             RecyclerView, <code>false</code> if the LayoutManager wants
   7099          *                             to measure itself.
   7100          *
   7101          * @see #setMeasuredDimension(Rect, int, int)
   7102          * @see #isAutoMeasureEnabled()
   7103          */
   7104         public void setAutoMeasureEnabled(boolean enabled) {
   7105             mAutoMeasure = enabled;
   7106         }
   7107 
   7108         /**
   7109          * Returns whether the LayoutManager uses the automatic measurement API or not.
   7110          *
   7111          * @return <code>True</code> if the LayoutManager is measured by the RecyclerView or
   7112          * <code>false</code> if it measures itself.
   7113          *
   7114          * @see #setAutoMeasureEnabled(boolean)
   7115          */
   7116         public boolean isAutoMeasureEnabled() {
   7117             return mAutoMeasure;
   7118         }
   7119 
   7120         /**
   7121          * Returns whether this LayoutManager supports automatic item animations.
   7122          * A LayoutManager wishing to support item animations should obey certain
   7123          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
   7124          * The default return value is <code>false</code>, so subclasses of LayoutManager
   7125          * will not get predictive item animations by default.
   7126          *
   7127          * <p>Whether item animations are enabled in a RecyclerView is determined both
   7128          * by the return value from this method and the
   7129          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
   7130          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
   7131          * method returns false, then simple item animations will be enabled, in which
   7132          * views that are moving onto or off of the screen are simply faded in/out. If
   7133          * the RecyclerView has a non-null ItemAnimator and this method returns true,
   7134          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
   7135          * setup up the information needed to more intelligently predict where appearing
   7136          * and disappearing views should be animated from/to.</p>
   7137          *
   7138          * @return true if predictive item animations should be enabled, false otherwise
   7139          */
   7140         public boolean supportsPredictiveItemAnimations() {
   7141             return false;
   7142         }
   7143 
   7144         /**
   7145          * Sets whether the LayoutManager should be queried for views outside of
   7146          * its viewport while the UI thread is idle between frames.
   7147          *
   7148          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
   7149          * view system traversals on devices running API 21 or greater. Default value is true.</p>
   7150          *
   7151          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
   7152          * to RenderThread and the starting up its next frame at the next VSync pulse. By
   7153          * prefetching out of window views in this time period, delays from inflation and view
   7154          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
   7155          *
   7156          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
   7157          * size of the View cache to hold prefetched views.</p>
   7158          *
   7159          * @param enabled <code>True</code> if items should be prefetched in between traversals.
   7160          *
   7161          * @see #isItemPrefetchEnabled()
   7162          */
   7163         public final void setItemPrefetchEnabled(boolean enabled) {
   7164             if (enabled != mItemPrefetchEnabled) {
   7165                 mItemPrefetchEnabled = enabled;
   7166                 mPrefetchMaxCountObserved = 0;
   7167                 if (mRecyclerView != null) {
   7168                     mRecyclerView.mRecycler.updateViewCacheSize();
   7169                 }
   7170             }
   7171         }
   7172 
   7173         /**
   7174          * Sets whether the LayoutManager should be queried for views outside of
   7175          * its viewport while the UI thread is idle between frames.
   7176          *
   7177          * @see #setItemPrefetchEnabled(boolean)
   7178          *
   7179          * @return true if item prefetch is enabled, false otherwise
   7180          */
   7181         public final boolean isItemPrefetchEnabled() {
   7182             return mItemPrefetchEnabled;
   7183         }
   7184 
   7185         /**
   7186          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
   7187          *
   7188          * <p>If item prefetch is enabled, this method is called in between traversals to gather
   7189          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
   7190          * traversals.</p>
   7191          *
   7192          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
   7193          * each item to be prepared, and these positions will have their ViewHolders created and
   7194          * bound, if there is sufficient time available, in advance of being needed by a
   7195          * scroll or layout.</p>
   7196          *
   7197          * @param dx X movement component.
   7198          * @param dy Y movement component.
   7199          * @param state State of RecyclerView
   7200          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
   7201          *
   7202          * @see #isItemPrefetchEnabled()
   7203          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
   7204          */
   7205         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
   7206                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
   7207 
   7208         /**
   7209          * Gather all positions from the LayoutManager to be prefetched in preperation for its
   7210          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
   7211          *
   7212          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
   7213          *
   7214          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
   7215          * LayoutManager, this method is called in between draw traversals to gather
   7216          * which positions this LayoutManager will first need, once it appears on the screen.</p>
   7217          *
   7218          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
   7219          * vertically scrolling LayoutManager, this method would be called when the horizontal list
   7220          * is about to come onscreen.</p>
   7221          *
   7222          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
   7223          * each item to be prepared, and these positions will have their ViewHolders created and
   7224          * bound, if there is sufficient time available, in advance of being needed by a
   7225          * scroll or layout.</p>
   7226          *
   7227          * @param adapterItemCount number of items in the associated adapter.
   7228          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
   7229          *
   7230          * @see #isItemPrefetchEnabled()
   7231          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
   7232          */
   7233         public void collectInitialPrefetchPositions(int adapterItemCount,
   7234                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
   7235 
   7236         void dispatchAttachedToWindow(RecyclerView view) {
   7237             mIsAttachedToWindow = true;
   7238             onAttachedToWindow(view);
   7239         }
   7240 
   7241         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
   7242             mIsAttachedToWindow = false;
   7243             onDetachedFromWindow(view, recycler);
   7244         }
   7245 
   7246         /**
   7247          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
   7248          * to a window.
   7249          *
   7250          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
   7251          * is attached to window.
   7252          */
   7253         public boolean isAttachedToWindow() {
   7254             return mIsAttachedToWindow;
   7255         }
   7256 
   7257         /**
   7258          * Causes the Runnable to execute on the next animation time step.
   7259          * The runnable will be run on the user interface thread.
   7260          * <p>
   7261          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   7262          *
   7263          * @param action The Runnable that will be executed.
   7264          *
   7265          * @see #removeCallbacks
   7266          */
   7267         public void postOnAnimation(Runnable action) {
   7268             if (mRecyclerView != null) {
   7269                 mRecyclerView.postOnAnimation(action);
   7270             }
   7271         }
   7272 
   7273         /**
   7274          * Removes the specified Runnable from the message queue.
   7275          * <p>
   7276          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   7277          *
   7278          * @param action The Runnable to remove from the message handling queue
   7279          *
   7280          * @return true if RecyclerView could ask the Handler to remove the Runnable,
   7281          *         false otherwise. When the returned value is true, the Runnable
   7282          *         may or may not have been actually removed from the message queue
   7283          *         (for instance, if the Runnable was not in the queue already.)
   7284          *
   7285          * @see #postOnAnimation
   7286          */
   7287         public boolean removeCallbacks(Runnable action) {
   7288             if (mRecyclerView != null) {
   7289                 return mRecyclerView.removeCallbacks(action);
   7290             }
   7291             return false;
   7292         }
   7293         /**
   7294          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
   7295          * is attached to a window.
   7296          * <p>
   7297          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
   7298          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
   7299          * not requested on the RecyclerView while it was detached.
   7300          * <p>
   7301          * Subclass implementations should always call through to the superclass implementation.
   7302          *
   7303          * @param view The RecyclerView this LayoutManager is bound to
   7304          *
   7305          * @see #onDetachedFromWindow(RecyclerView, Recycler)
   7306          */
   7307         @CallSuper
   7308         public void onAttachedToWindow(RecyclerView view) {
   7309         }
   7310 
   7311         /**
   7312          * @deprecated
   7313          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
   7314          */
   7315         @Deprecated
   7316         public void onDetachedFromWindow(RecyclerView view) {
   7317 
   7318         }
   7319 
   7320         /**
   7321          * Called when this LayoutManager is detached from its parent RecyclerView or when
   7322          * its parent RecyclerView is detached from its window.
   7323          * <p>
   7324          * LayoutManager should clear all of its View references as another LayoutManager might be
   7325          * assigned to the RecyclerView.
   7326          * <p>
   7327          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
   7328          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
   7329          * not requested on the RecyclerView while it was detached.
   7330          * <p>
   7331          * If your LayoutManager has View references that it cleans in on-detach, it should also
   7332          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
   7333          * RecyclerView is re-attached.
   7334          * <p>
   7335          * Subclass implementations should always call through to the superclass implementation.
   7336          *
   7337          * @param view The RecyclerView this LayoutManager is bound to
   7338          * @param recycler The recycler to use if you prefer to recycle your children instead of
   7339          *                 keeping them around.
   7340          *
   7341          * @see #onAttachedToWindow(RecyclerView)
   7342          */
   7343         @CallSuper
   7344         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
   7345             onDetachedFromWindow(view);
   7346         }
   7347 
   7348         /**
   7349          * Check if the RecyclerView is configured to clip child views to its padding.
   7350          *
   7351          * @return true if this RecyclerView clips children to its padding, false otherwise
   7352          */
   7353         public boolean getClipToPadding() {
   7354             return mRecyclerView != null && mRecyclerView.mClipToPadding;
   7355         }
   7356 
   7357         /**
   7358          * Lay out all relevant child views from the given adapter.
   7359          *
   7360          * The LayoutManager is in charge of the behavior of item animations. By default,
   7361          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
   7362          * item animations are enabled. This means that add/remove operations on the
   7363          * adapter will result in animations to add new or appearing items, removed or
   7364          * disappearing items, and moved items. If a LayoutManager returns false from
   7365          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
   7366          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
   7367          * RecyclerView will have enough information to run those animations in a simple
   7368          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
   7369          * simply fade views in and out, whether they are actually added/removed or whether
   7370          * they are moved on or off the screen due to other add/remove operations.
   7371          *
   7372          * <p>A LayoutManager wanting a better item animation experience, where items can be
   7373          * animated onto and off of the screen according to where the items exist when they
   7374          * are not on screen, then the LayoutManager should return true from
   7375          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
   7376          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
   7377          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
   7378          * once as a "pre" layout step to determine where items would have been prior to
   7379          * a real layout, and again to do the "real" layout. In the pre-layout phase,
   7380          * items will remember their pre-layout positions to allow them to be laid out
   7381          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
   7382          * be returned from the scrap to help determine correct placement of other items.
   7383          * These removed items should not be added to the child list, but should be used
   7384          * to help calculate correct positioning of other views, including views that
   7385          * were not previously onscreen (referred to as APPEARING views), but whose
   7386          * pre-layout offscreen position can be determined given the extra
   7387          * information about the pre-layout removed views.</p>
   7388          *
   7389          * <p>The second layout pass is the real layout in which only non-removed views
   7390          * will be used. The only additional requirement during this pass is, if
   7391          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
   7392          * views exist in the child list prior to layout and which are not there after
   7393          * layout (referred to as DISAPPEARING views), and to position/layout those views
   7394          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
   7395          * the animation system to know the location to which to animate these disappearing
   7396          * views.</p>
   7397          *
   7398          * <p>The default LayoutManager implementations for RecyclerView handle all of these
   7399          * requirements for animations already. Clients of RecyclerView can either use one
   7400          * of these layout managers directly or look at their implementations of
   7401          * onLayoutChildren() to see how they account for the APPEARING and
   7402          * DISAPPEARING views.</p>
   7403          *
   7404          * @param recycler         Recycler to use for fetching potentially cached views for a
   7405          *                         position
   7406          * @param state            Transient state of RecyclerView
   7407          */
   7408         public void onLayoutChildren(Recycler recycler, State state) {
   7409             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
   7410         }
   7411 
   7412         /**
   7413          * Called after a full layout calculation is finished. The layout calculation may include
   7414          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
   7415          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
   7416          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
   7417          * <p>
   7418          * This is a good place for the LayoutManager to do some cleanup like pending scroll
   7419          * position, saved state etc.
   7420          *
   7421          * @param state Transient state of RecyclerView
   7422          */
   7423         public void onLayoutCompleted(State state) {
   7424         }
   7425 
   7426         /**
   7427          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
   7428          *
   7429          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
   7430          * to store extra information specific to the layout. Client code should subclass
   7431          * {@link RecyclerView.LayoutParams} for this purpose.</p>
   7432          *
   7433          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   7434          * you must also override
   7435          * {@link #checkLayoutParams(LayoutParams)},
   7436          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   7437          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   7438          *
   7439          * @return A new LayoutParams for a child view
   7440          */
   7441         public abstract LayoutParams generateDefaultLayoutParams();
   7442 
   7443         /**
   7444          * Determines the validity of the supplied LayoutParams object.
   7445          *
   7446          * <p>This should check to make sure that the object is of the correct type
   7447          * and all values are within acceptable ranges. The default implementation
   7448          * returns <code>true</code> for non-null params.</p>
   7449          *
   7450          * @param lp LayoutParams object to check
   7451          * @return true if this LayoutParams object is valid, false otherwise
   7452          */
   7453         public boolean checkLayoutParams(LayoutParams lp) {
   7454             return lp != null;
   7455         }
   7456 
   7457         /**
   7458          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
   7459          * values from the supplied LayoutParams object if possible.
   7460          *
   7461          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   7462          * you must also override
   7463          * {@link #checkLayoutParams(LayoutParams)},
   7464          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   7465          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   7466          *
   7467          * @param lp Source LayoutParams object to copy values from
   7468          * @return a new LayoutParams object
   7469          */
   7470         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
   7471             if (lp instanceof LayoutParams) {
   7472                 return new LayoutParams((LayoutParams) lp);
   7473             } else if (lp instanceof MarginLayoutParams) {
   7474                 return new LayoutParams((MarginLayoutParams) lp);
   7475             } else {
   7476                 return new LayoutParams(lp);
   7477             }
   7478         }
   7479 
   7480         /**
   7481          * Create a LayoutParams object suitable for this LayoutManager from
   7482          * an inflated layout resource.
   7483          *
   7484          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   7485          * you must also override
   7486          * {@link #checkLayoutParams(LayoutParams)},
   7487          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   7488          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   7489          *
   7490          * @param c Context for obtaining styled attributes
   7491          * @param attrs AttributeSet describing the supplied arguments
   7492          * @return a new LayoutParams object
   7493          */
   7494         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
   7495             return new LayoutParams(c, attrs);
   7496         }
   7497 
   7498         /**
   7499          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
   7500          * The default implementation does nothing and returns 0.
   7501          *
   7502          * @param dx            distance to scroll by in pixels. X increases as scroll position
   7503          *                      approaches the right.
   7504          * @param recycler      Recycler to use for fetching potentially cached views for a
   7505          *                      position
   7506          * @param state         Transient state of RecyclerView
   7507          * @return The actual distance scrolled. The return value will be negative if dx was
   7508          * negative and scrolling proceeeded in that direction.
   7509          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
   7510          */
   7511         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
   7512             return 0;
   7513         }
   7514 
   7515         /**
   7516          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
   7517          * The default implementation does nothing and returns 0.
   7518          *
   7519          * @param dy            distance to scroll in pixels. Y increases as scroll position
   7520          *                      approaches the bottom.
   7521          * @param recycler      Recycler to use for fetching potentially cached views for a
   7522          *                      position
   7523          * @param state         Transient state of RecyclerView
   7524          * @return The actual distance scrolled. The return value will be negative if dy was
   7525          * negative and scrolling proceeeded in that direction.
   7526          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
   7527          */
   7528         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
   7529             return 0;
   7530         }
   7531 
   7532         /**
   7533          * Query if horizontal scrolling is currently supported. The default implementation
   7534          * returns false.
   7535          *
   7536          * @return True if this LayoutManager can scroll the current contents horizontally
   7537          */
   7538         public boolean canScrollHorizontally() {
   7539             return false;
   7540         }
   7541 
   7542         /**
   7543          * Query if vertical scrolling is currently supported. The default implementation
   7544          * returns false.
   7545          *
   7546          * @return True if this LayoutManager can scroll the current contents vertically
   7547          */
   7548         public boolean canScrollVertically() {
   7549             return false;
   7550         }
   7551 
   7552         /**
   7553          * Scroll to the specified adapter position.
   7554          *
   7555          * Actual position of the item on the screen depends on the LayoutManager implementation.
   7556          * @param position Scroll to this adapter position.
   7557          */
   7558         public void scrollToPosition(int position) {
   7559             if (DEBUG) {
   7560                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
   7561             }
   7562         }
   7563 
   7564         /**
   7565          * <p>Smooth scroll to the specified adapter position.</p>
   7566          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
   7567          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
   7568          * </p>
   7569          * @param recyclerView The RecyclerView to which this layout manager is attached
   7570          * @param state    Current State of RecyclerView
   7571          * @param position Scroll to this adapter position.
   7572          */
   7573         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
   7574                 int position) {
   7575             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
   7576         }
   7577 
   7578         /**
   7579          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
   7580          * <p>Calling this method will cancel any previous smooth scroll request.</p>
   7581          * @param smoothScroller Instance which defines how smooth scroll should be animated
   7582          */
   7583         public void startSmoothScroll(SmoothScroller smoothScroller) {
   7584             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
   7585                     && mSmoothScroller.isRunning()) {
   7586                 mSmoothScroller.stop();
   7587             }
   7588             mSmoothScroller = smoothScroller;
   7589             mSmoothScroller.start(mRecyclerView, this);
   7590         }
   7591 
   7592         /**
   7593          * @return true if RecycylerView is currently in the state of smooth scrolling.
   7594          */
   7595         public boolean isSmoothScrolling() {
   7596             return mSmoothScroller != null && mSmoothScroller.isRunning();
   7597         }
   7598 
   7599 
   7600         /**
   7601          * Returns the resolved layout direction for this RecyclerView.
   7602          *
   7603          * @return {@link android.view.View#LAYOUT_DIRECTION_RTL} if the layout
   7604          * direction is RTL or returns
   7605          * {@link android.view.View#LAYOUT_DIRECTION_LTR} if the layout direction
   7606          * is not RTL.
   7607          */
   7608         public int getLayoutDirection() {
   7609             return mRecyclerView.getLayoutDirection();
   7610         }
   7611 
   7612         /**
   7613          * Ends all animations on the view created by the {@link ItemAnimator}.
   7614          *
   7615          * @param view The View for which the animations should be ended.
   7616          * @see RecyclerView.ItemAnimator#endAnimations()
   7617          */
   7618         public void endAnimation(View view) {
   7619             if (mRecyclerView.mItemAnimator != null) {
   7620                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
   7621             }
   7622         }
   7623 
   7624         /**
   7625          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   7626          * to the layout that is known to be going away, either because it has been
   7627          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   7628          * visible portion of the container but is being laid out in order to inform RecyclerView
   7629          * in how to animate the item out of view.
   7630          * <p>
   7631          * Views added via this method are going to be invisible to LayoutManager after the
   7632          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   7633          * or won't be included in {@link #getChildCount()} method.
   7634          *
   7635          * @param child View to add and then remove with animation.
   7636          */
   7637         public void addDisappearingView(View child) {
   7638             addDisappearingView(child, -1);
   7639         }
   7640 
   7641         /**
   7642          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   7643          * to the layout that is known to be going away, either because it has been
   7644          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   7645          * visible portion of the container but is being laid out in order to inform RecyclerView
   7646          * in how to animate the item out of view.
   7647          * <p>
   7648          * Views added via this method are going to be invisible to LayoutManager after the
   7649          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   7650          * or won't be included in {@link #getChildCount()} method.
   7651          *
   7652          * @param child View to add and then remove with animation.
   7653          * @param index Index of the view.
   7654          */
   7655         public void addDisappearingView(View child, int index) {
   7656             addViewInt(child, index, true);
   7657         }
   7658 
   7659         /**
   7660          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   7661          * use this method to add views obtained from a {@link Recycler} using
   7662          * {@link Recycler#getViewForPosition(int)}.
   7663          *
   7664          * @param child View to add
   7665          */
   7666         public void addView(View child) {
   7667             addView(child, -1);
   7668         }
   7669 
   7670         /**
   7671          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   7672          * use this method to add views obtained from a {@link Recycler} using
   7673          * {@link Recycler#getViewForPosition(int)}.
   7674          *
   7675          * @param child View to add
   7676          * @param index Index to add child at
   7677          */
   7678         public void addView(View child, int index) {
   7679             addViewInt(child, index, false);
   7680         }
   7681 
   7682         private void addViewInt(View child, int index, boolean disappearing) {
   7683             final ViewHolder holder = getChildViewHolderInt(child);
   7684             if (disappearing || holder.isRemoved()) {
   7685                 // these views will be hidden at the end of the layout pass.
   7686                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
   7687             } else {
   7688                 // This may look like unnecessary but may happen if layout manager supports
   7689                 // predictive layouts and adapter removed then re-added the same item.
   7690                 // In this case, added version will be visible in the post layout (because add is
   7691                 // deferred) but RV will still bind it to the same View.
   7692                 // So if a View re-appears in post layout pass, remove it from disappearing list.
   7693                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
   7694             }
   7695             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   7696             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
   7697                 if (holder.isScrap()) {
   7698                     holder.unScrap();
   7699                 } else {
   7700                     holder.clearReturnedFromScrapFlag();
   7701                 }
   7702                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
   7703                 if (DISPATCH_TEMP_DETACH) {
   7704                     child.dispatchFinishTemporaryDetach();
   7705                 }
   7706             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
   7707                 // ensure in correct position
   7708                 int currentIndex = mChildHelper.indexOfChild(child);
   7709                 if (index == -1) {
   7710                     index = mChildHelper.getChildCount();
   7711                 }
   7712                 if (currentIndex == -1) {
   7713                     throw new IllegalStateException("Added View has RecyclerView as parent but"
   7714                             + " view is not a real child. Unfiltered index:"
   7715                             + mRecyclerView.indexOfChild(child));
   7716                 }
   7717                 if (currentIndex != index) {
   7718                     mRecyclerView.mLayout.moveView(currentIndex, index);
   7719                 }
   7720             } else {
   7721                 mChildHelper.addView(child, index, false);
   7722                 lp.mInsetsDirty = true;
   7723                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
   7724                     mSmoothScroller.onChildAttachedToWindow(child);
   7725                 }
   7726             }
   7727             if (lp.mPendingInvalidate) {
   7728                 if (DEBUG) {
   7729                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
   7730                 }
   7731                 holder.itemView.invalidate();
   7732                 lp.mPendingInvalidate = false;
   7733             }
   7734         }
   7735 
   7736         /**
   7737          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   7738          * use this method to completely remove a child view that is no longer needed.
   7739          * LayoutManagers should strongly consider recycling removed views using
   7740          * {@link Recycler#recycleView(android.view.View)}.
   7741          *
   7742          * @param child View to remove
   7743          */
   7744         public void removeView(View child) {
   7745             mChildHelper.removeView(child);
   7746         }
   7747 
   7748         /**
   7749          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   7750          * use this method to completely remove a child view that is no longer needed.
   7751          * LayoutManagers should strongly consider recycling removed views using
   7752          * {@link Recycler#recycleView(android.view.View)}.
   7753          *
   7754          * @param index Index of the child view to remove
   7755          */
   7756         public void removeViewAt(int index) {
   7757             final View child = getChildAt(index);
   7758             if (child != null) {
   7759                 mChildHelper.removeViewAt(index);
   7760             }
   7761         }
   7762 
   7763         /**
   7764          * Remove all views from the currently attached RecyclerView. This will not recycle
   7765          * any of the affected views; the LayoutManager is responsible for doing so if desired.
   7766          */
   7767         public void removeAllViews() {
   7768             // Only remove non-animating views
   7769             final int childCount = getChildCount();
   7770             for (int i = childCount - 1; i >= 0; i--) {
   7771                 mChildHelper.removeViewAt(i);
   7772             }
   7773         }
   7774 
   7775         /**
   7776          * Returns offset of the RecyclerView's text baseline from the its top boundary.
   7777          *
   7778          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
   7779          * there is no baseline.
   7780          */
   7781         public int getBaseline() {
   7782             return -1;
   7783         }
   7784 
   7785         /**
   7786          * Returns the adapter position of the item represented by the given View. This does not
   7787          * contain any adapter changes that might have happened after the last layout.
   7788          *
   7789          * @param view The view to query
   7790          * @return The adapter position of the item which is rendered by this View.
   7791          */
   7792         public int getPosition(View view) {
   7793             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
   7794         }
   7795 
   7796         /**
   7797          * Returns the View type defined by the adapter.
   7798          *
   7799          * @param view The view to query
   7800          * @return The type of the view assigned by the adapter.
   7801          */
   7802         public int getItemViewType(View view) {
   7803             return getChildViewHolderInt(view).getItemViewType();
   7804         }
   7805 
   7806         /**
   7807          * Traverses the ancestors of the given view and returns the item view that contains it
   7808          * and also a direct child of the LayoutManager.
   7809          * <p>
   7810          * Note that this method may return null if the view is a child of the RecyclerView but
   7811          * not a child of the LayoutManager (e.g. running a disappear animation).
   7812          *
   7813          * @param view The view that is a descendant of the LayoutManager.
   7814          *
   7815          * @return The direct child of the LayoutManager which contains the given view or null if
   7816          * the provided view is not a descendant of this LayoutManager.
   7817          *
   7818          * @see RecyclerView#getChildViewHolder(View)
   7819          * @see RecyclerView#findContainingViewHolder(View)
   7820          */
   7821         @Nullable
   7822         public View findContainingItemView(View view) {
   7823             if (mRecyclerView == null) {
   7824                 return null;
   7825             }
   7826             View found = mRecyclerView.findContainingItemView(view);
   7827             if (found == null) {
   7828                 return null;
   7829             }
   7830             if (mChildHelper.isHidden(found)) {
   7831                 return null;
   7832             }
   7833             return found;
   7834         }
   7835 
   7836         /**
   7837          * Finds the view which represents the given adapter position.
   7838          * <p>
   7839          * This method traverses each child since it has no information about child order.
   7840          * Override this method to improve performance if your LayoutManager keeps data about
   7841          * child views.
   7842          * <p>
   7843          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
   7844          *
   7845          * @param position Position of the item in adapter
   7846          * @return The child view that represents the given position or null if the position is not
   7847          * laid out
   7848          */
   7849         public View findViewByPosition(int position) {
   7850             final int childCount = getChildCount();
   7851             for (int i = 0; i < childCount; i++) {
   7852                 View child = getChildAt(i);
   7853                 ViewHolder vh = getChildViewHolderInt(child);
   7854                 if (vh == null) {
   7855                     continue;
   7856                 }
   7857                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
   7858                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
   7859                     return child;
   7860                 }
   7861             }
   7862             return null;
   7863         }
   7864 
   7865         /**
   7866          * Temporarily detach a child view.
   7867          *
   7868          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   7869          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   7870          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   7871          * so that the detached view may be rebound and reused.</p>
   7872          *
   7873          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   7874          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   7875          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   7876          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   7877          *
   7878          * @param child Child to detach
   7879          */
   7880         public void detachView(View child) {
   7881             final int ind = mChildHelper.indexOfChild(child);
   7882             if (ind >= 0) {
   7883                 detachViewInternal(ind, child);
   7884             }
   7885         }
   7886 
   7887         /**
   7888          * Temporarily detach a child view.
   7889          *
   7890          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   7891          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   7892          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   7893          * so that the detached view may be rebound and reused.</p>
   7894          *
   7895          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   7896          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   7897          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   7898          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   7899          *
   7900          * @param index Index of the child to detach
   7901          */
   7902         public void detachViewAt(int index) {
   7903             detachViewInternal(index, getChildAt(index));
   7904         }
   7905 
   7906         private void detachViewInternal(int index, View view) {
   7907             if (DISPATCH_TEMP_DETACH) {
   7908                 view.dispatchStartTemporaryDetach();
   7909             }
   7910             mChildHelper.detachViewFromParent(index);
   7911         }
   7912 
   7913         /**
   7914          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   7915          * This method should not be used to reattach views that were previously
   7916          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   7917          *
   7918          * @param child Child to reattach
   7919          * @param index Intended child index for child
   7920          * @param lp LayoutParams for child
   7921          */
   7922         public void attachView(View child, int index, LayoutParams lp) {
   7923             ViewHolder vh = getChildViewHolderInt(child);
   7924             if (vh.isRemoved()) {
   7925                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
   7926             } else {
   7927                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
   7928             }
   7929             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
   7930             if (DISPATCH_TEMP_DETACH)  {
   7931                 child.dispatchFinishTemporaryDetach();
   7932             }
   7933         }
   7934 
   7935         /**
   7936          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   7937          * This method should not be used to reattach views that were previously
   7938          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   7939          *
   7940          * @param child Child to reattach
   7941          * @param index Intended child index for child
   7942          */
   7943         public void attachView(View child, int index) {
   7944             attachView(child, index, (LayoutParams) child.getLayoutParams());
   7945         }
   7946 
   7947         /**
   7948          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   7949          * This method should not be used to reattach views that were previously
   7950          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   7951          *
   7952          * @param child Child to reattach
   7953          */
   7954         public void attachView(View child) {
   7955             attachView(child, -1);
   7956         }
   7957 
   7958         /**
   7959          * Finish removing a view that was previously temporarily
   7960          * {@link #detachView(android.view.View) detached}.
   7961          *
   7962          * @param child Detached child to remove
   7963          */
   7964         public void removeDetachedView(View child) {
   7965             mRecyclerView.removeDetachedView(child, false);
   7966         }
   7967 
   7968         /**
   7969          * Moves a View from one position to another.
   7970          *
   7971          * @param fromIndex The View's initial index
   7972          * @param toIndex The View's target index
   7973          */
   7974         public void moveView(int fromIndex, int toIndex) {
   7975             View view = getChildAt(fromIndex);
   7976             if (view == null) {
   7977                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
   7978                         + fromIndex);
   7979             }
   7980             detachViewAt(fromIndex);
   7981             attachView(view, toIndex);
   7982         }
   7983 
   7984         /**
   7985          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
   7986          *
   7987          * <p>Scrapping a view allows it to be rebound and reused to show updated or
   7988          * different data.</p>
   7989          *
   7990          * @param child Child to detach and scrap
   7991          * @param recycler Recycler to deposit the new scrap view into
   7992          */
   7993         public void detachAndScrapView(View child, Recycler recycler) {
   7994             int index = mChildHelper.indexOfChild(child);
   7995             scrapOrRecycleView(recycler, index, child);
   7996         }
   7997 
   7998         /**
   7999          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
   8000          *
   8001          * <p>Scrapping a view allows it to be rebound and reused to show updated or
   8002          * different data.</p>
   8003          *
   8004          * @param index Index of child to detach and scrap
   8005          * @param recycler Recycler to deposit the new scrap view into
   8006          */
   8007         public void detachAndScrapViewAt(int index, Recycler recycler) {
   8008             final View child = getChildAt(index);
   8009             scrapOrRecycleView(recycler, index, child);
   8010         }
   8011 
   8012         /**
   8013          * Remove a child view and recycle it using the given Recycler.
   8014          *
   8015          * @param child Child to remove and recycle
   8016          * @param recycler Recycler to use to recycle child
   8017          */
   8018         public void removeAndRecycleView(View child, Recycler recycler) {
   8019             removeView(child);
   8020             recycler.recycleView(child);
   8021         }
   8022 
   8023         /**
   8024          * Remove a child view and recycle it using the given Recycler.
   8025          *
   8026          * @param index Index of child to remove and recycle
   8027          * @param recycler Recycler to use to recycle child
   8028          */
   8029         public void removeAndRecycleViewAt(int index, Recycler recycler) {
   8030             final View view = getChildAt(index);
   8031             removeViewAt(index);
   8032             recycler.recycleView(view);
   8033         }
   8034 
   8035         /**
   8036          * Return the current number of child views attached to the parent RecyclerView.
   8037          * This does not include child views that were temporarily detached and/or scrapped.
   8038          *
   8039          * @return Number of attached children
   8040          */
   8041         public int getChildCount() {
   8042             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
   8043         }
   8044 
   8045         /**
   8046          * Return the child view at the given index
   8047          * @param index Index of child to return
   8048          * @return Child view at index
   8049          */
   8050         public View getChildAt(int index) {
   8051             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
   8052         }
   8053 
   8054         /**
   8055          * Return the width measurement spec mode of the RecyclerView.
   8056          * <p>
   8057          * This value is set only if the LayoutManager opts into the auto measure api via
   8058          * {@link #setAutoMeasureEnabled(boolean)}.
   8059          * <p>
   8060          * When RecyclerView is running a layout, this value is always set to
   8061          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
   8062          *
   8063          * @return Width measure spec mode.
   8064          *
   8065          * @see View.MeasureSpec#getMode(int)
   8066          * @see View#onMeasure(int, int)
   8067          */
   8068         public int getWidthMode() {
   8069             return mWidthMode;
   8070         }
   8071 
   8072         /**
   8073          * Return the height measurement spec mode of the RecyclerView.
   8074          * <p>
   8075          * This value is set only if the LayoutManager opts into the auto measure api via
   8076          * {@link #setAutoMeasureEnabled(boolean)}.
   8077          * <p>
   8078          * When RecyclerView is running a layout, this value is always set to
   8079          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
   8080          *
   8081          * @return Height measure spec mode.
   8082          *
   8083          * @see View.MeasureSpec#getMode(int)
   8084          * @see View#onMeasure(int, int)
   8085          */
   8086         public int getHeightMode() {
   8087             return mHeightMode;
   8088         }
   8089 
   8090         /**
   8091          * Return the width of the parent RecyclerView
   8092          *
   8093          * @return Width in pixels
   8094          */
   8095         public int getWidth() {
   8096             return mWidth;
   8097         }
   8098 
   8099         /**
   8100          * Return the height of the parent RecyclerView
   8101          *
   8102          * @return Height in pixels
   8103          */
   8104         public int getHeight() {
   8105             return mHeight;
   8106         }
   8107 
   8108         /**
   8109          * Return the left padding of the parent RecyclerView
   8110          *
   8111          * @return Padding in pixels
   8112          */
   8113         public int getPaddingLeft() {
   8114             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
   8115         }
   8116 
   8117         /**
   8118          * Return the top padding of the parent RecyclerView
   8119          *
   8120          * @return Padding in pixels
   8121          */
   8122         public int getPaddingTop() {
   8123             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
   8124         }
   8125 
   8126         /**
   8127          * Return the right padding of the parent RecyclerView
   8128          *
   8129          * @return Padding in pixels
   8130          */
   8131         public int getPaddingRight() {
   8132             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
   8133         }
   8134 
   8135         /**
   8136          * Return the bottom padding of the parent RecyclerView
   8137          *
   8138          * @return Padding in pixels
   8139          */
   8140         public int getPaddingBottom() {
   8141             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
   8142         }
   8143 
   8144         /**
   8145          * Return the start padding of the parent RecyclerView
   8146          *
   8147          * @return Padding in pixels
   8148          */
   8149         public int getPaddingStart() {
   8150             return mRecyclerView != null ? mRecyclerView.getPaddingStart() : 0;
   8151         }
   8152 
   8153         /**
   8154          * Return the end padding of the parent RecyclerView
   8155          *
   8156          * @return Padding in pixels
   8157          */
   8158         public int getPaddingEnd() {
   8159             return mRecyclerView != null ? mRecyclerView.getPaddingEnd() : 0;
   8160         }
   8161 
   8162         /**
   8163          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
   8164          *
   8165          * @return True if the RecyclerView has focus, false otherwise.
   8166          * @see View#isFocused()
   8167          */
   8168         public boolean isFocused() {
   8169             return mRecyclerView != null && mRecyclerView.isFocused();
   8170         }
   8171 
   8172         /**
   8173          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
   8174          *
   8175          * @return true if the RecyclerView has or contains focus
   8176          * @see View#hasFocus()
   8177          */
   8178         public boolean hasFocus() {
   8179             return mRecyclerView != null && mRecyclerView.hasFocus();
   8180         }
   8181 
   8182         /**
   8183          * Returns the item View which has or contains focus.
   8184          *
   8185          * @return A direct child of RecyclerView which has focus or contains the focused child.
   8186          */
   8187         public View getFocusedChild() {
   8188             if (mRecyclerView == null) {
   8189                 return null;
   8190             }
   8191             final View focused = mRecyclerView.getFocusedChild();
   8192             if (focused == null || mChildHelper.isHidden(focused)) {
   8193                 return null;
   8194             }
   8195             return focused;
   8196         }
   8197 
   8198         /**
   8199          * Returns the number of items in the adapter bound to the parent RecyclerView.
   8200          * <p>
   8201          * Note that this number is not necessarily equal to
   8202          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
   8203          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
   8204          * For more details, check the documentation for
   8205          * {@link State#getItemCount() State#getItemCount()}.
   8206          *
   8207          * @return The number of items in the bound adapter
   8208          * @see State#getItemCount()
   8209          */
   8210         public int getItemCount() {
   8211             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
   8212             return a != null ? a.getItemCount() : 0;
   8213         }
   8214 
   8215         /**
   8216          * Offset all child views attached to the parent RecyclerView by dx pixels along
   8217          * the horizontal axis.
   8218          *
   8219          * @param dx Pixels to offset by
   8220          */
   8221         public void offsetChildrenHorizontal(int dx) {
   8222             if (mRecyclerView != null) {
   8223                 mRecyclerView.offsetChildrenHorizontal(dx);
   8224             }
   8225         }
   8226 
   8227         /**
   8228          * Offset all child views attached to the parent RecyclerView by dy pixels along
   8229          * the vertical axis.
   8230          *
   8231          * @param dy Pixels to offset by
   8232          */
   8233         public void offsetChildrenVertical(int dy) {
   8234             if (mRecyclerView != null) {
   8235                 mRecyclerView.offsetChildrenVertical(dy);
   8236             }
   8237         }
   8238 
   8239         /**
   8240          * Flags a view so that it will not be scrapped or recycled.
   8241          * <p>
   8242          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
   8243          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
   8244          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
   8245          * ignore the child.
   8246          * <p>
   8247          * Before this child can be recycled again, you have to call
   8248          * {@link #stopIgnoringView(View)}.
   8249          * <p>
   8250          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   8251          *
   8252          * @param view View to ignore.
   8253          * @see #stopIgnoringView(View)
   8254          */
   8255         public void ignoreView(View view) {
   8256             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
   8257                 // checking this because calling this method on a recycled or detached view may
   8258                 // cause loss of state.
   8259                 throw new IllegalArgumentException("View should be fully attached to be ignored");
   8260             }
   8261             final ViewHolder vh = getChildViewHolderInt(view);
   8262             vh.addFlags(ViewHolder.FLAG_IGNORE);
   8263             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
   8264         }
   8265 
   8266         /**
   8267          * View can be scrapped and recycled again.
   8268          * <p>
   8269          * Note that calling this method removes all information in the view holder.
   8270          * <p>
   8271          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   8272          *
   8273          * @param view View to ignore.
   8274          */
   8275         public void stopIgnoringView(View view) {
   8276             final ViewHolder vh = getChildViewHolderInt(view);
   8277             vh.stopIgnoring();
   8278             vh.resetInternal();
   8279             vh.addFlags(ViewHolder.FLAG_INVALID);
   8280         }
   8281 
   8282         /**
   8283          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
   8284          * into the given Recycler. The Recycler may prefer to reuse scrap views before
   8285          * other views that were previously recycled.
   8286          *
   8287          * @param recycler Recycler to scrap views into
   8288          */
   8289         public void detachAndScrapAttachedViews(Recycler recycler) {
   8290             final int childCount = getChildCount();
   8291             for (int i = childCount - 1; i >= 0; i--) {
   8292                 final View v = getChildAt(i);
   8293                 scrapOrRecycleView(recycler, i, v);
   8294             }
   8295         }
   8296 
   8297         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
   8298             final ViewHolder viewHolder = getChildViewHolderInt(view);
   8299             if (viewHolder.shouldIgnore()) {
   8300                 if (DEBUG) {
   8301                     Log.d(TAG, "ignoring view " + viewHolder);
   8302                 }
   8303                 return;
   8304             }
   8305             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
   8306                     && !mRecyclerView.mAdapter.hasStableIds()) {
   8307                 removeViewAt(index);
   8308                 recycler.recycleViewHolderInternal(viewHolder);
   8309             } else {
   8310                 detachViewAt(index);
   8311                 recycler.scrapView(view);
   8312                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
   8313             }
   8314         }
   8315 
   8316         /**
   8317          * Recycles the scrapped views.
   8318          * <p>
   8319          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
   8320          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
   8321          * call remove and invalidate RecyclerView to ensure UI update.
   8322          *
   8323          * @param recycler Recycler
   8324          */
   8325         void removeAndRecycleScrapInt(Recycler recycler) {
   8326             final int scrapCount = recycler.getScrapCount();
   8327             // Loop backward, recycler might be changed by removeDetachedView()
   8328             for (int i = scrapCount - 1; i >= 0; i--) {
   8329                 final View scrap = recycler.getScrapViewAt(i);
   8330                 final ViewHolder vh = getChildViewHolderInt(scrap);
   8331                 if (vh.shouldIgnore()) {
   8332                     continue;
   8333                 }
   8334                 // If the scrap view is animating, we need to cancel them first. If we cancel it
   8335                 // here, ItemAnimator callback may recycle it which will cause double recycling.
   8336                 // To avoid this, we mark it as not recycleable before calling the item animator.
   8337                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
   8338                 // the view) may recycle it too, so we guard it before we call user APIs.
   8339                 vh.setIsRecyclable(false);
   8340                 if (vh.isTmpDetached()) {
   8341                     mRecyclerView.removeDetachedView(scrap, false);
   8342                 }
   8343                 if (mRecyclerView.mItemAnimator != null) {
   8344                     mRecyclerView.mItemAnimator.endAnimation(vh);
   8345                 }
   8346                 vh.setIsRecyclable(true);
   8347                 recycler.quickRecycleScrapView(scrap);
   8348             }
   8349             recycler.clearScrap();
   8350             if (scrapCount > 0) {
   8351                 mRecyclerView.invalidate();
   8352             }
   8353         }
   8354 
   8355 
   8356         /**
   8357          * Measure a child view using standard measurement policy, taking the padding
   8358          * of the parent RecyclerView and any added item decorations into account.
   8359          *
   8360          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   8361          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   8362          *
   8363          * @param child Child view to measure
   8364          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   8365          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   8366          */
   8367         public void measureChild(View child, int widthUsed, int heightUsed) {
   8368             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8369 
   8370             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   8371             widthUsed += insets.left + insets.right;
   8372             heightUsed += insets.top + insets.bottom;
   8373             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
   8374                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
   8375                     canScrollHorizontally());
   8376             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
   8377                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
   8378                     canScrollVertically());
   8379             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
   8380                 child.measure(widthSpec, heightSpec);
   8381             }
   8382         }
   8383 
   8384         /**
   8385          * RecyclerView internally does its own View measurement caching which should help with
   8386          * WRAP_CONTENT.
   8387          * <p>
   8388          * Use this method if the View is already measured once in this layout pass.
   8389          */
   8390         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
   8391             return !mMeasurementCacheEnabled
   8392                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
   8393                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
   8394         }
   8395 
   8396         // we may consider making this public
   8397         /**
   8398          * RecyclerView internally does its own View measurement caching which should help with
   8399          * WRAP_CONTENT.
   8400          * <p>
   8401          * Use this method if the View is not yet measured and you need to decide whether to
   8402          * measure this View or not.
   8403          */
   8404         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
   8405             return child.isLayoutRequested()
   8406                     || !mMeasurementCacheEnabled
   8407                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
   8408                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
   8409         }
   8410 
   8411         /**
   8412          * In addition to the View Framework's measurement cache, RecyclerView uses its own
   8413          * additional measurement cache for its children to avoid re-measuring them when not
   8414          * necessary. It is on by default but it can be turned off via
   8415          * {@link #setMeasurementCacheEnabled(boolean)}.
   8416          *
   8417          * @return True if measurement cache is enabled, false otherwise.
   8418          *
   8419          * @see #setMeasurementCacheEnabled(boolean)
   8420          */
   8421         public boolean isMeasurementCacheEnabled() {
   8422             return mMeasurementCacheEnabled;
   8423         }
   8424 
   8425         /**
   8426          * Sets whether RecyclerView should use its own measurement cache for the children. This is
   8427          * a more aggressive cache than the framework uses.
   8428          *
   8429          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
   8430          *
   8431          * @see #isMeasurementCacheEnabled()
   8432          */
   8433         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
   8434             mMeasurementCacheEnabled = measurementCacheEnabled;
   8435         }
   8436 
   8437         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
   8438             final int specMode = MeasureSpec.getMode(spec);
   8439             final int specSize = MeasureSpec.getSize(spec);
   8440             if (dimension > 0 && childSize != dimension) {
   8441                 return false;
   8442             }
   8443             switch (specMode) {
   8444                 case MeasureSpec.UNSPECIFIED:
   8445                     return true;
   8446                 case MeasureSpec.AT_MOST:
   8447                     return specSize >= childSize;
   8448                 case MeasureSpec.EXACTLY:
   8449                     return  specSize == childSize;
   8450             }
   8451             return false;
   8452         }
   8453 
   8454         /**
   8455          * Measure a child view using standard measurement policy, taking the padding
   8456          * of the parent RecyclerView, any added item decorations and the child margins
   8457          * into account.
   8458          *
   8459          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   8460          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   8461          *
   8462          * @param child Child view to measure
   8463          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   8464          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   8465          */
   8466         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
   8467             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8468 
   8469             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   8470             widthUsed += insets.left + insets.right;
   8471             heightUsed += insets.top + insets.bottom;
   8472 
   8473             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
   8474                     getPaddingLeft() + getPaddingRight()
   8475                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
   8476                     canScrollHorizontally());
   8477             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
   8478                     getPaddingTop() + getPaddingBottom()
   8479                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
   8480                     canScrollVertically());
   8481             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
   8482                 child.measure(widthSpec, heightSpec);
   8483             }
   8484         }
   8485 
   8486         /**
   8487          * Calculate a MeasureSpec value for measuring a child view in one dimension.
   8488          *
   8489          * @param parentSize Size of the parent view where the child will be placed
   8490          * @param padding Total space currently consumed by other elements of the parent
   8491          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
   8492          *                       Generally obtained from the child view's LayoutParams
   8493          * @param canScroll true if the parent RecyclerView can scroll in this dimension
   8494          *
   8495          * @return a MeasureSpec value for the child view
   8496          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
   8497          */
   8498         @Deprecated
   8499         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
   8500                 boolean canScroll) {
   8501             int size = Math.max(0, parentSize - padding);
   8502             int resultSize = 0;
   8503             int resultMode = 0;
   8504             if (canScroll) {
   8505                 if (childDimension >= 0) {
   8506                     resultSize = childDimension;
   8507                     resultMode = MeasureSpec.EXACTLY;
   8508                 } else {
   8509                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
   8510                     // instead using UNSPECIFIED.
   8511                     resultSize = 0;
   8512                     resultMode = MeasureSpec.UNSPECIFIED;
   8513                 }
   8514             } else {
   8515                 if (childDimension >= 0) {
   8516                     resultSize = childDimension;
   8517                     resultMode = MeasureSpec.EXACTLY;
   8518                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   8519                     resultSize = size;
   8520                     // TODO this should be my spec.
   8521                     resultMode = MeasureSpec.EXACTLY;
   8522                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   8523                     resultSize = size;
   8524                     resultMode = MeasureSpec.AT_MOST;
   8525                 }
   8526             }
   8527             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
   8528         }
   8529 
   8530         /**
   8531          * Calculate a MeasureSpec value for measuring a child view in one dimension.
   8532          *
   8533          * @param parentSize Size of the parent view where the child will be placed
   8534          * @param parentMode The measurement spec mode of the parent
   8535          * @param padding Total space currently consumed by other elements of parent
   8536          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
   8537          *                       Generally obtained from the child view's LayoutParams
   8538          * @param canScroll true if the parent RecyclerView can scroll in this dimension
   8539          *
   8540          * @return a MeasureSpec value for the child view
   8541          */
   8542         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
   8543                 int childDimension, boolean canScroll) {
   8544             int size = Math.max(0, parentSize - padding);
   8545             int resultSize = 0;
   8546             int resultMode = 0;
   8547             if (canScroll) {
   8548                 if (childDimension >= 0) {
   8549                     resultSize = childDimension;
   8550                     resultMode = MeasureSpec.EXACTLY;
   8551                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   8552                     switch (parentMode) {
   8553                         case MeasureSpec.AT_MOST:
   8554                         case MeasureSpec.EXACTLY:
   8555                             resultSize = size;
   8556                             resultMode = parentMode;
   8557                             break;
   8558                         case MeasureSpec.UNSPECIFIED:
   8559                             resultSize = 0;
   8560                             resultMode = MeasureSpec.UNSPECIFIED;
   8561                             break;
   8562                     }
   8563                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   8564                     resultSize = 0;
   8565                     resultMode = MeasureSpec.UNSPECIFIED;
   8566                 }
   8567             } else {
   8568                 if (childDimension >= 0) {
   8569                     resultSize = childDimension;
   8570                     resultMode = MeasureSpec.EXACTLY;
   8571                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   8572                     resultSize = size;
   8573                     resultMode = parentMode;
   8574                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   8575                     resultSize = size;
   8576                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
   8577                         resultMode = MeasureSpec.AT_MOST;
   8578                     } else {
   8579                         resultMode = MeasureSpec.UNSPECIFIED;
   8580                     }
   8581 
   8582                 }
   8583             }
   8584             //noinspection WrongConstant
   8585             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
   8586         }
   8587 
   8588         /**
   8589          * Returns the measured width of the given child, plus the additional size of
   8590          * any insets applied by {@link ItemDecoration ItemDecorations}.
   8591          *
   8592          * @param child Child view to query
   8593          * @return child's measured width plus <code>ItemDecoration</code> insets
   8594          *
   8595          * @see View#getMeasuredWidth()
   8596          */
   8597         public int getDecoratedMeasuredWidth(View child) {
   8598             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8599             return child.getMeasuredWidth() + insets.left + insets.right;
   8600         }
   8601 
   8602         /**
   8603          * Returns the measured height of the given child, plus the additional size of
   8604          * any insets applied by {@link ItemDecoration ItemDecorations}.
   8605          *
   8606          * @param child Child view to query
   8607          * @return child's measured height plus <code>ItemDecoration</code> insets
   8608          *
   8609          * @see View#getMeasuredHeight()
   8610          */
   8611         public int getDecoratedMeasuredHeight(View child) {
   8612             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8613             return child.getMeasuredHeight() + insets.top + insets.bottom;
   8614         }
   8615 
   8616         /**
   8617          * Lay out the given child view within the RecyclerView using coordinates that
   8618          * include any current {@link ItemDecoration ItemDecorations}.
   8619          *
   8620          * <p>LayoutManagers should prefer working in sizes and coordinates that include
   8621          * item decoration insets whenever possible. This allows the LayoutManager to effectively
   8622          * ignore decoration insets within measurement and layout code. See the following
   8623          * methods:</p>
   8624          * <ul>
   8625          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
   8626          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
   8627          *     <li>{@link #measureChild(View, int, int)}</li>
   8628          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
   8629          *     <li>{@link #getDecoratedLeft(View)}</li>
   8630          *     <li>{@link #getDecoratedTop(View)}</li>
   8631          *     <li>{@link #getDecoratedRight(View)}</li>
   8632          *     <li>{@link #getDecoratedBottom(View)}</li>
   8633          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
   8634          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
   8635          * </ul>
   8636          *
   8637          * @param child Child to lay out
   8638          * @param left Left edge, with item decoration insets included
   8639          * @param top Top edge, with item decoration insets included
   8640          * @param right Right edge, with item decoration insets included
   8641          * @param bottom Bottom edge, with item decoration insets included
   8642          *
   8643          * @see View#layout(int, int, int, int)
   8644          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
   8645          */
   8646         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
   8647             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8648             child.layout(left + insets.left, top + insets.top, right - insets.right,
   8649                     bottom - insets.bottom);
   8650         }
   8651 
   8652         /**
   8653          * Lay out the given child view within the RecyclerView using coordinates that
   8654          * include any current {@link ItemDecoration ItemDecorations} and margins.
   8655          *
   8656          * <p>LayoutManagers should prefer working in sizes and coordinates that include
   8657          * item decoration insets whenever possible. This allows the LayoutManager to effectively
   8658          * ignore decoration insets within measurement and layout code. See the following
   8659          * methods:</p>
   8660          * <ul>
   8661          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
   8662          *     <li>{@link #measureChild(View, int, int)}</li>
   8663          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
   8664          *     <li>{@link #getDecoratedLeft(View)}</li>
   8665          *     <li>{@link #getDecoratedTop(View)}</li>
   8666          *     <li>{@link #getDecoratedRight(View)}</li>
   8667          *     <li>{@link #getDecoratedBottom(View)}</li>
   8668          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
   8669          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
   8670          * </ul>
   8671          *
   8672          * @param child Child to lay out
   8673          * @param left Left edge, with item decoration insets and left margin included
   8674          * @param top Top edge, with item decoration insets and top margin included
   8675          * @param right Right edge, with item decoration insets and right margin included
   8676          * @param bottom Bottom edge, with item decoration insets and bottom margin included
   8677          *
   8678          * @see View#layout(int, int, int, int)
   8679          * @see #layoutDecorated(View, int, int, int, int)
   8680          */
   8681         public void layoutDecoratedWithMargins(View child, int left, int top, int right,
   8682                 int bottom) {
   8683             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8684             final Rect insets = lp.mDecorInsets;
   8685             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
   8686                     right - insets.right - lp.rightMargin,
   8687                     bottom - insets.bottom - lp.bottomMargin);
   8688         }
   8689 
   8690         /**
   8691          * Calculates the bounding box of the View while taking into account its matrix changes
   8692          * (translation, scale etc) with respect to the RecyclerView.
   8693          * <p>
   8694          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
   8695          * the View's matrix so that the decor offsets also go through the same transformation.
   8696          *
   8697          * @param child The ItemView whose bounding box should be calculated.
   8698          * @param includeDecorInsets True if the decor insets should be included in the bounding box
   8699          * @param out The rectangle into which the output will be written.
   8700          */
   8701         public void getTransformedBoundingBox(View child, boolean includeDecorInsets, Rect out) {
   8702             if (includeDecorInsets) {
   8703                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   8704                 out.set(-insets.left, -insets.top,
   8705                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
   8706             } else {
   8707                 out.set(0, 0, child.getWidth(), child.getHeight());
   8708             }
   8709 
   8710             if (mRecyclerView != null) {
   8711                 final Matrix childMatrix = child.getMatrix();
   8712                 if (childMatrix != null && !childMatrix.isIdentity()) {
   8713                     final RectF tempRectF = mRecyclerView.mTempRectF;
   8714                     tempRectF.set(out);
   8715                     childMatrix.mapRect(tempRectF);
   8716                     out.set(
   8717                             (int) Math.floor(tempRectF.left),
   8718                             (int) Math.floor(tempRectF.top),
   8719                             (int) Math.ceil(tempRectF.right),
   8720                             (int) Math.ceil(tempRectF.bottom)
   8721                     );
   8722                 }
   8723             }
   8724             out.offset(child.getLeft(), child.getTop());
   8725         }
   8726 
   8727         /**
   8728          * Returns the bounds of the view including its decoration and margins.
   8729          *
   8730          * @param view The view element to check
   8731          * @param outBounds A rect that will receive the bounds of the element including its
   8732          *                  decoration and margins.
   8733          */
   8734         public void getDecoratedBoundsWithMargins(View view, Rect outBounds) {
   8735             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
   8736         }
   8737 
   8738         /**
   8739          * Returns the left edge of the given child view within its parent, offset by any applied
   8740          * {@link ItemDecoration ItemDecorations}.
   8741          *
   8742          * @param child Child to query
   8743          * @return Child left edge with offsets applied
   8744          * @see #getLeftDecorationWidth(View)
   8745          */
   8746         public int getDecoratedLeft(View child) {
   8747             return child.getLeft() - getLeftDecorationWidth(child);
   8748         }
   8749 
   8750         /**
   8751          * Returns the top edge of the given child view within its parent, offset by any applied
   8752          * {@link ItemDecoration ItemDecorations}.
   8753          *
   8754          * @param child Child to query
   8755          * @return Child top edge with offsets applied
   8756          * @see #getTopDecorationHeight(View)
   8757          */
   8758         public int getDecoratedTop(View child) {
   8759             return child.getTop() - getTopDecorationHeight(child);
   8760         }
   8761 
   8762         /**
   8763          * Returns the right edge of the given child view within its parent, offset by any applied
   8764          * {@link ItemDecoration ItemDecorations}.
   8765          *
   8766          * @param child Child to query
   8767          * @return Child right edge with offsets applied
   8768          * @see #getRightDecorationWidth(View)
   8769          */
   8770         public int getDecoratedRight(View child) {
   8771             return child.getRight() + getRightDecorationWidth(child);
   8772         }
   8773 
   8774         /**
   8775          * Returns the bottom edge of the given child view within its parent, offset by any applied
   8776          * {@link ItemDecoration ItemDecorations}.
   8777          *
   8778          * @param child Child to query
   8779          * @return Child bottom edge with offsets applied
   8780          * @see #getBottomDecorationHeight(View)
   8781          */
   8782         public int getDecoratedBottom(View child) {
   8783             return child.getBottom() + getBottomDecorationHeight(child);
   8784         }
   8785 
   8786         /**
   8787          * Calculates the item decor insets applied to the given child and updates the provided
   8788          * Rect instance with the inset values.
   8789          * <ul>
   8790          *     <li>The Rect's left is set to the total width of left decorations.</li>
   8791          *     <li>The Rect's top is set to the total height of top decorations.</li>
   8792          *     <li>The Rect's right is set to the total width of right decorations.</li>
   8793          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
   8794          * </ul>
   8795          * <p>
   8796          * Note that item decorations are automatically calculated when one of the LayoutManager's
   8797          * measure child methods is called. If you need to measure the child with custom specs via
   8798          * {@link View#measure(int, int)}, you can use this method to get decorations.
   8799          *
   8800          * @param child The child view whose decorations should be calculated
   8801          * @param outRect The Rect to hold result values
   8802          */
   8803         public void calculateItemDecorationsForChild(View child, Rect outRect) {
   8804             if (mRecyclerView == null) {
   8805                 outRect.set(0, 0, 0, 0);
   8806                 return;
   8807             }
   8808             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   8809             outRect.set(insets);
   8810         }
   8811 
   8812         /**
   8813          * Returns the total height of item decorations applied to child's top.
   8814          * <p>
   8815          * Note that this value is not updated until the View is measured or
   8816          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8817          *
   8818          * @param child Child to query
   8819          * @return The total height of item decorations applied to the child's top.
   8820          * @see #getDecoratedTop(View)
   8821          * @see #calculateItemDecorationsForChild(View, Rect)
   8822          */
   8823         public int getTopDecorationHeight(View child) {
   8824             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
   8825         }
   8826 
   8827         /**
   8828          * Returns the total height of item decorations applied to child's bottom.
   8829          * <p>
   8830          * Note that this value is not updated until the View is measured or
   8831          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8832          *
   8833          * @param child Child to query
   8834          * @return The total height of item decorations applied to the child's bottom.
   8835          * @see #getDecoratedBottom(View)
   8836          * @see #calculateItemDecorationsForChild(View, Rect)
   8837          */
   8838         public int getBottomDecorationHeight(View child) {
   8839             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
   8840         }
   8841 
   8842         /**
   8843          * Returns the total width of item decorations applied to child's left.
   8844          * <p>
   8845          * Note that this value is not updated until the View is measured or
   8846          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8847          *
   8848          * @param child Child to query
   8849          * @return The total width of item decorations applied to the child's left.
   8850          * @see #getDecoratedLeft(View)
   8851          * @see #calculateItemDecorationsForChild(View, Rect)
   8852          */
   8853         public int getLeftDecorationWidth(View child) {
   8854             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
   8855         }
   8856 
   8857         /**
   8858          * Returns the total width of item decorations applied to child's right.
   8859          * <p>
   8860          * Note that this value is not updated until the View is measured or
   8861          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   8862          *
   8863          * @param child Child to query
   8864          * @return The total width of item decorations applied to the child's right.
   8865          * @see #getDecoratedRight(View)
   8866          * @see #calculateItemDecorationsForChild(View, Rect)
   8867          */
   8868         public int getRightDecorationWidth(View child) {
   8869             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
   8870         }
   8871 
   8872         /**
   8873          * Called when searching for a focusable view in the given direction has failed
   8874          * for the current content of the RecyclerView.
   8875          *
   8876          * <p>This is the LayoutManager's opportunity to populate views in the given direction
   8877          * to fulfill the request if it can. The LayoutManager should attach and return
   8878          * the view to be focused. The default implementation returns null.</p>
   8879          *
   8880          * @param focused   The currently focused view
   8881          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   8882          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   8883          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   8884          *                  or 0 for not applicable
   8885          * @param recycler  The recycler to use for obtaining views for currently offscreen items
   8886          * @param state     Transient state of RecyclerView
   8887          * @return The chosen view to be focused
   8888          */
   8889         @Nullable
   8890         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
   8891                 State state) {
   8892             return null;
   8893         }
   8894 
   8895         /**
   8896          * This method gives a LayoutManager an opportunity to intercept the initial focus search
   8897          * before the default behavior of {@link FocusFinder} is used. If this method returns
   8898          * null FocusFinder will attempt to find a focusable child view. If it fails
   8899          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
   8900          * will be called to give the LayoutManager an opportunity to add new views for items
   8901          * that did not have attached views representing them. The LayoutManager should not add
   8902          * or remove views from this method.
   8903          *
   8904          * @param focused The currently focused view
   8905          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   8906          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   8907          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   8908          * @return A descendant view to focus or null to fall back to default behavior.
   8909          *         The default implementation returns null.
   8910          */
   8911         public View onInterceptFocusSearch(View focused, int direction) {
   8912             return null;
   8913         }
   8914 
   8915         /**
   8916          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
   8917          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
   8918          * android.graphics.Rect, boolean)} for more details.
   8919          *
   8920          * <p>The base implementation will attempt to perform a standard programmatic scroll
   8921          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
   8922          *
   8923          * @param child The direct child making the request.
   8924          * @param rect  The rectangle in the child's coordinates the child
   8925          *              wishes to be on the screen.
   8926          * @param immediate True to forbid animated or delayed scrolling,
   8927          *                  false otherwise
   8928          * @return Whether the group scrolled to handle the operation
   8929          */
   8930         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
   8931                 boolean immediate) {
   8932             final int parentLeft = getPaddingLeft();
   8933             final int parentTop = getPaddingTop();
   8934             final int parentRight = getWidth() - getPaddingRight();
   8935             final int parentBottom = getHeight() - getPaddingBottom();
   8936             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
   8937             final int childTop = child.getTop() + rect.top - child.getScrollY();
   8938             final int childRight = childLeft + rect.width();
   8939             final int childBottom = childTop + rect.height();
   8940 
   8941             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
   8942             final int offScreenTop = Math.min(0, childTop - parentTop);
   8943             final int offScreenRight = Math.max(0, childRight - parentRight);
   8944             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
   8945 
   8946             // Favor the "start" layout direction over the end when bringing one side or the other
   8947             // of a large rect into view. If we decide to bring in end because start is already
   8948             // visible, limit the scroll such that start won't go out of bounds.
   8949             final int dx;
   8950             if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
   8951                 dx = offScreenRight != 0 ? offScreenRight
   8952                         : Math.max(offScreenLeft, childRight - parentRight);
   8953             } else {
   8954                 dx = offScreenLeft != 0 ? offScreenLeft
   8955                         : Math.min(childLeft - parentLeft, offScreenRight);
   8956             }
   8957 
   8958             // Favor bringing the top into view over the bottom. If top is already visible and
   8959             // we should scroll to make bottom visible, make sure top does not go out of bounds.
   8960             final int dy = offScreenTop != 0 ? offScreenTop
   8961                     : Math.min(childTop - parentTop, offScreenBottom);
   8962 
   8963             if (dx != 0 || dy != 0) {
   8964                 if (immediate) {
   8965                     parent.scrollBy(dx, dy);
   8966                 } else {
   8967                     parent.smoothScrollBy(dx, dy);
   8968                 }
   8969                 return true;
   8970             }
   8971             return false;
   8972         }
   8973 
   8974         /**
   8975          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
   8976          */
   8977         @Deprecated
   8978         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
   8979             // eat the request if we are in the middle of a scroll or layout
   8980             return isSmoothScrolling() || parent.isComputingLayout();
   8981         }
   8982 
   8983         /**
   8984          * Called when a descendant view of the RecyclerView requests focus.
   8985          *
   8986          * <p>A LayoutManager wishing to keep focused views aligned in a specific
   8987          * portion of the view may implement that behavior in an override of this method.</p>
   8988          *
   8989          * <p>If the LayoutManager executes different behavior that should override the default
   8990          * behavior of scrolling the focused child on screen instead of running alongside it,
   8991          * this method should return true.</p>
   8992          *
   8993          * @param parent  The RecyclerView hosting this LayoutManager
   8994          * @param state   Current state of RecyclerView
   8995          * @param child   Direct child of the RecyclerView containing the newly focused view
   8996          * @param focused The newly focused view. This may be the same view as child or it may be
   8997          *                null
   8998          * @return true if the default scroll behavior should be suppressed
   8999          */
   9000         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
   9001                 View focused) {
   9002             return onRequestChildFocus(parent, child, focused);
   9003         }
   9004 
   9005         /**
   9006          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
   9007          * The LayoutManager may use this opportunity to clear caches and configure state such
   9008          * that it can relayout appropriately with the new data and potentially new view types.
   9009          *
   9010          * <p>The default implementation removes all currently attached views.</p>
   9011          *
   9012          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
   9013          *                   adapter.
   9014          * @param newAdapter The new adapter instance. Might be null if
   9015          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
   9016          */
   9017         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
   9018         }
   9019 
   9020         /**
   9021          * Called to populate focusable views within the RecyclerView.
   9022          *
   9023          * <p>The LayoutManager implementation should return <code>true</code> if the default
   9024          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
   9025          * suppressed.</p>
   9026          *
   9027          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
   9028          * to fall back to the default ViewGroup behavior.</p>
   9029          *
   9030          * @param recyclerView The RecyclerView hosting this LayoutManager
   9031          * @param views List of output views. This method should add valid focusable views
   9032          *              to this list.
   9033          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   9034          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   9035          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   9036          * @param focusableMode The type of focusables to be added.
   9037          *
   9038          * @return true to suppress the default behavior, false to add default focusables after
   9039          *         this method returns.
   9040          *
   9041          * @see #FOCUSABLES_ALL
   9042          * @see #FOCUSABLES_TOUCH_MODE
   9043          */
   9044         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
   9045                 int direction, int focusableMode) {
   9046             return false;
   9047         }
   9048 
   9049         /**
   9050          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
   9051          * detailed information on what has actually changed.
   9052          *
   9053          * @param recyclerView
   9054          */
   9055         public void onItemsChanged(RecyclerView recyclerView) {
   9056         }
   9057 
   9058         /**
   9059          * Called when items have been added to the adapter. The LayoutManager may choose to
   9060          * requestLayout if the inserted items would require refreshing the currently visible set
   9061          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
   9062          *
   9063          * @param recyclerView
   9064          * @param positionStart
   9065          * @param itemCount
   9066          */
   9067         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
   9068         }
   9069 
   9070         /**
   9071          * Called when items have been removed from the adapter.
   9072          *
   9073          * @param recyclerView
   9074          * @param positionStart
   9075          * @param itemCount
   9076          */
   9077         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
   9078         }
   9079 
   9080         /**
   9081          * Called when items have been changed in the adapter.
   9082          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
   9083          * instead, then this callback will not be invoked.
   9084          *
   9085          * @param recyclerView
   9086          * @param positionStart
   9087          * @param itemCount
   9088          */
   9089         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
   9090         }
   9091 
   9092         /**
   9093          * Called when items have been changed in the adapter and with optional payload.
   9094          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
   9095          *
   9096          * @param recyclerView
   9097          * @param positionStart
   9098          * @param itemCount
   9099          * @param payload
   9100          */
   9101         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
   9102                 Object payload) {
   9103             onItemsUpdated(recyclerView, positionStart, itemCount);
   9104         }
   9105 
   9106         /**
   9107          * Called when an item is moved withing the adapter.
   9108          * <p>
   9109          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
   9110          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
   9111          * is called.
   9112          *
   9113          * @param recyclerView
   9114          * @param from
   9115          * @param to
   9116          * @param itemCount
   9117          */
   9118         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
   9119 
   9120         }
   9121 
   9122 
   9123         /**
   9124          * <p>Override this method if you want to support scroll bars.</p>
   9125          *
   9126          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
   9127          *
   9128          * <p>Default implementation returns 0.</p>
   9129          *
   9130          * @param state Current state of RecyclerView
   9131          * @return The horizontal extent of the scrollbar's thumb
   9132          * @see RecyclerView#computeHorizontalScrollExtent()
   9133          */
   9134         public int computeHorizontalScrollExtent(State state) {
   9135             return 0;
   9136         }
   9137 
   9138         /**
   9139          * <p>Override this method if you want to support scroll bars.</p>
   9140          *
   9141          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
   9142          *
   9143          * <p>Default implementation returns 0.</p>
   9144          *
   9145          * @param state Current State of RecyclerView where you can find total item count
   9146          * @return The horizontal offset of the scrollbar's thumb
   9147          * @see RecyclerView#computeHorizontalScrollOffset()
   9148          */
   9149         public int computeHorizontalScrollOffset(State state) {
   9150             return 0;
   9151         }
   9152 
   9153         /**
   9154          * <p>Override this method if you want to support scroll bars.</p>
   9155          *
   9156          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
   9157          *
   9158          * <p>Default implementation returns 0.</p>
   9159          *
   9160          * @param state Current State of RecyclerView where you can find total item count
   9161          * @return The total horizontal range represented by the vertical scrollbar
   9162          * @see RecyclerView#computeHorizontalScrollRange()
   9163          */
   9164         public int computeHorizontalScrollRange(State state) {
   9165             return 0;
   9166         }
   9167 
   9168         /**
   9169          * <p>Override this method if you want to support scroll bars.</p>
   9170          *
   9171          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
   9172          *
   9173          * <p>Default implementation returns 0.</p>
   9174          *
   9175          * @param state Current state of RecyclerView
   9176          * @return The vertical extent of the scrollbar's thumb
   9177          * @see RecyclerView#computeVerticalScrollExtent()
   9178          */
   9179         public int computeVerticalScrollExtent(State state) {
   9180             return 0;
   9181         }
   9182 
   9183         /**
   9184          * <p>Override this method if you want to support scroll bars.</p>
   9185          *
   9186          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
   9187          *
   9188          * <p>Default implementation returns 0.</p>
   9189          *
   9190          * @param state Current State of RecyclerView where you can find total item count
   9191          * @return The vertical offset of the scrollbar's thumb
   9192          * @see RecyclerView#computeVerticalScrollOffset()
   9193          */
   9194         public int computeVerticalScrollOffset(State state) {
   9195             return 0;
   9196         }
   9197 
   9198         /**
   9199          * <p>Override this method if you want to support scroll bars.</p>
   9200          *
   9201          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
   9202          *
   9203          * <p>Default implementation returns 0.</p>
   9204          *
   9205          * @param state Current State of RecyclerView where you can find total item count
   9206          * @return The total vertical range represented by the vertical scrollbar
   9207          * @see RecyclerView#computeVerticalScrollRange()
   9208          */
   9209         public int computeVerticalScrollRange(State state) {
   9210             return 0;
   9211         }
   9212 
   9213         /**
   9214          * Measure the attached RecyclerView. Implementations must call
   9215          * {@link #setMeasuredDimension(int, int)} before returning.
   9216          *
   9217          * <p>The default implementation will handle EXACTLY measurements and respect
   9218          * the minimum width and height properties of the host RecyclerView if measured
   9219          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
   9220          * will consume all available space.</p>
   9221          *
   9222          * @param recycler Recycler
   9223          * @param state Transient state of RecyclerView
   9224          * @param widthSpec Width {@link android.view.View.MeasureSpec}
   9225          * @param heightSpec Height {@link android.view.View.MeasureSpec}
   9226          */
   9227         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
   9228             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
   9229         }
   9230 
   9231         /**
   9232          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
   9233          * host RecyclerView.
   9234          *
   9235          * @param widthSize Measured width
   9236          * @param heightSize Measured height
   9237          */
   9238         public void setMeasuredDimension(int widthSize, int heightSize) {
   9239             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
   9240         }
   9241 
   9242         /**
   9243          * @return The host RecyclerView's {@link View#getMinimumWidth()}
   9244          */
   9245         public int getMinimumWidth() {
   9246             return mRecyclerView.getMinimumWidth();
   9247         }
   9248 
   9249         /**
   9250          * @return The host RecyclerView's {@link View#getMinimumHeight()}
   9251          */
   9252         public int getMinimumHeight() {
   9253             return mRecyclerView.getMinimumHeight();
   9254         }
   9255         /**
   9256          * <p>Called when the LayoutManager should save its state. This is a good time to save your
   9257          * scroll position, configuration and anything else that may be required to restore the same
   9258          * layout state if the LayoutManager is recreated.</p>
   9259          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
   9260          * restore. This will let you share information between your LayoutManagers but it is also
   9261          * your responsibility to make sure they use the same parcelable class.</p>
   9262          *
   9263          * @return Necessary information for LayoutManager to be able to restore its state
   9264          */
   9265         public Parcelable onSaveInstanceState() {
   9266             return null;
   9267         }
   9268 
   9269 
   9270         public void onRestoreInstanceState(Parcelable state) {
   9271 
   9272         }
   9273 
   9274         void stopSmoothScroller() {
   9275             if (mSmoothScroller != null) {
   9276                 mSmoothScroller.stop();
   9277             }
   9278         }
   9279 
   9280         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
   9281             if (mSmoothScroller == smoothScroller) {
   9282                 mSmoothScroller = null;
   9283             }
   9284         }
   9285 
   9286         /**
   9287          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
   9288          *
   9289          * @param state The new scroll state for RecyclerView
   9290          */
   9291         public void onScrollStateChanged(int state) {
   9292         }
   9293 
   9294         /**
   9295          * Removes all views and recycles them using the given recycler.
   9296          * <p>
   9297          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
   9298          * <p>
   9299          * If a View is marked as "ignored", it is not removed nor recycled.
   9300          *
   9301          * @param recycler Recycler to use to recycle children
   9302          * @see #removeAndRecycleView(View, Recycler)
   9303          * @see #removeAndRecycleViewAt(int, Recycler)
   9304          * @see #ignoreView(View)
   9305          */
   9306         public void removeAndRecycleAllViews(Recycler recycler) {
   9307             for (int i = getChildCount() - 1; i >= 0; i--) {
   9308                 final View view = getChildAt(i);
   9309                 if (!getChildViewHolderInt(view).shouldIgnore()) {
   9310                     removeAndRecycleViewAt(i, recycler);
   9311                 }
   9312             }
   9313         }
   9314 
   9315         // called by accessibility delegate
   9316         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
   9317             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
   9318         }
   9319 
   9320         /**
   9321          * Called by the AccessibilityDelegate when the information about the current layout should
   9322          * be populated.
   9323          * <p>
   9324          * Default implementation adds a {@link
   9325          * android.view.accessibility.AccessibilityNodeInfo.CollectionInfo}.
   9326          * <p>
   9327          * You should override
   9328          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   9329          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   9330          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
   9331          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
   9332          * more accurate accessibility information.
   9333          *
   9334          * @param recycler The Recycler that can be used to convert view positions into adapter
   9335          *                 positions
   9336          * @param state    The current state of RecyclerView
   9337          * @param info     The info that should be filled by the LayoutManager
   9338          * @see View#onInitializeAccessibilityNodeInfo(
   9339          *android.view.accessibility.AccessibilityNodeInfo)
   9340          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   9341          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   9342          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
   9343          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   9344          */
   9345         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
   9346                 AccessibilityNodeInfo info) {
   9347             if (mRecyclerView.canScrollVertically(-1)
   9348                     || mRecyclerView.canScrollHorizontally(-1)) {
   9349                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
   9350                 info.setScrollable(true);
   9351             }
   9352             if (mRecyclerView.canScrollVertically(1)
   9353                     || mRecyclerView.canScrollHorizontally(1)) {
   9354                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
   9355                 info.setScrollable(true);
   9356             }
   9357             final AccessibilityNodeInfo.CollectionInfo collectionInfo =
   9358                     AccessibilityNodeInfo.CollectionInfo
   9359                             .obtain(getRowCountForAccessibility(recycler, state),
   9360                                     getColumnCountForAccessibility(recycler, state),
   9361                                     isLayoutHierarchical(recycler, state),
   9362                                     getSelectionModeForAccessibility(recycler, state));
   9363             info.setCollectionInfo(collectionInfo);
   9364         }
   9365 
   9366         // called by accessibility delegate
   9367         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
   9368             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
   9369         }
   9370 
   9371         /**
   9372          * Called by the accessibility delegate to initialize an accessibility event.
   9373          * <p>
   9374          * Default implementation adds item count and scroll information to the event.
   9375          *
   9376          * @param recycler The Recycler that can be used to convert view positions into adapter
   9377          *                 positions
   9378          * @param state    The current state of RecyclerView
   9379          * @param event    The event instance to initialize
   9380          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
   9381          */
   9382         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
   9383                 AccessibilityEvent event) {
   9384             if (mRecyclerView == null || event == null) {
   9385                 return;
   9386             }
   9387             event.setScrollable(mRecyclerView.canScrollVertically(1)
   9388                     || mRecyclerView.canScrollVertically(-1)
   9389                     || mRecyclerView.canScrollHorizontally(-1)
   9390                     || mRecyclerView.canScrollHorizontally(1));
   9391 
   9392             if (mRecyclerView.mAdapter != null) {
   9393                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
   9394             }
   9395         }
   9396 
   9397         // called by accessibility delegate
   9398         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfo info) {
   9399             final ViewHolder vh = getChildViewHolderInt(host);
   9400             // avoid trying to create accessibility node info for removed children
   9401             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
   9402                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
   9403                         mRecyclerView.mState, host, info);
   9404             }
   9405         }
   9406 
   9407         /**
   9408          * Called by the AccessibilityDelegate when the accessibility information for a specific
   9409          * item should be populated.
   9410          * <p>
   9411          * Default implementation adds basic positioning information about the item.
   9412          *
   9413          * @param recycler The Recycler that can be used to convert view positions into adapter
   9414          *                 positions
   9415          * @param state    The current state of RecyclerView
   9416          * @param host     The child for which accessibility node info should be populated
   9417          * @param info     The info to fill out about the item
   9418          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
   9419          * android.view.accessibility.AccessibilityNodeInfo)
   9420          */
   9421         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
   9422                 View host, AccessibilityNodeInfo info) {
   9423             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
   9424             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
   9425             final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
   9426                     AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndexGuess, 1,
   9427                             columnIndexGuess, 1, false, false);
   9428             info.setCollectionItemInfo(itemInfo);
   9429         }
   9430 
   9431         /**
   9432          * A LayoutManager can call this method to force RecyclerView to run simple animations in
   9433          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
   9434          * change).
   9435          * <p>
   9436          * Note that, calling this method will not guarantee that RecyclerView will run animations
   9437          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
   9438          * not run any animations but will still clear this flag after the layout is complete.
   9439          *
   9440          */
   9441         public void requestSimpleAnimationsInNextLayout() {
   9442             mRequestedSimpleAnimations = true;
   9443         }
   9444 
   9445         /**
   9446          * Returns the selection mode for accessibility. Should be
   9447          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE},
   9448          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_SINGLE} or
   9449          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_MULTIPLE}.
   9450          * <p>
   9451          * Default implementation returns
   9452          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
   9453          *
   9454          * @param recycler The Recycler that can be used to convert view positions into adapter
   9455          *                 positions
   9456          * @param state    The current state of RecyclerView
   9457          * @return Selection mode for accessibility. Default implementation returns
   9458          * {@link AccessibilityNodeInfo.CollectionInfo#SELECTION_MODE_NONE}.
   9459          */
   9460         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
   9461             return AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE;
   9462         }
   9463 
   9464         /**
   9465          * Returns the number of rows for accessibility.
   9466          * <p>
   9467          * Default implementation returns the number of items in the adapter if LayoutManager
   9468          * supports vertical scrolling or 1 if LayoutManager does not support vertical
   9469          * scrolling.
   9470          *
   9471          * @param recycler The Recycler that can be used to convert view positions into adapter
   9472          *                 positions
   9473          * @param state    The current state of RecyclerView
   9474          * @return The number of rows in LayoutManager for accessibility.
   9475          */
   9476         public int getRowCountForAccessibility(Recycler recycler, State state) {
   9477             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   9478                 return 1;
   9479             }
   9480             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
   9481         }
   9482 
   9483         /**
   9484          * Returns the number of columns for accessibility.
   9485          * <p>
   9486          * Default implementation returns the number of items in the adapter if LayoutManager
   9487          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
   9488          * scrolling.
   9489          *
   9490          * @param recycler The Recycler that can be used to convert view positions into adapter
   9491          *                 positions
   9492          * @param state    The current state of RecyclerView
   9493          * @return The number of rows in LayoutManager for accessibility.
   9494          */
   9495         public int getColumnCountForAccessibility(Recycler recycler, State state) {
   9496             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   9497                 return 1;
   9498             }
   9499             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
   9500         }
   9501 
   9502         /**
   9503          * Returns whether layout is hierarchical or not to be used for accessibility.
   9504          * <p>
   9505          * Default implementation returns false.
   9506          *
   9507          * @param recycler The Recycler that can be used to convert view positions into adapter
   9508          *                 positions
   9509          * @param state    The current state of RecyclerView
   9510          * @return True if layout is hierarchical.
   9511          */
   9512         public boolean isLayoutHierarchical(Recycler recycler, State state) {
   9513             return false;
   9514         }
   9515 
   9516         // called by accessibility delegate
   9517         boolean performAccessibilityAction(int action, Bundle args) {
   9518             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
   9519                     action, args);
   9520         }
   9521 
   9522         /**
   9523          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
   9524          *
   9525          * @param recycler  The Recycler that can be used to convert view positions into adapter
   9526          *                  positions
   9527          * @param state     The current state of RecyclerView
   9528          * @param action    The action to perform
   9529          * @param args      Optional action arguments
   9530          * @see View#performAccessibilityAction(int, android.os.Bundle)
   9531          */
   9532         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
   9533                 Bundle args) {
   9534             if (mRecyclerView == null) {
   9535                 return false;
   9536             }
   9537             int vScroll = 0, hScroll = 0;
   9538             switch (action) {
   9539                 case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
   9540                     if (mRecyclerView.canScrollVertically(-1)) {
   9541                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
   9542                     }
   9543                     if (mRecyclerView.canScrollHorizontally(-1)) {
   9544                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
   9545                     }
   9546                     break;
   9547                 case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
   9548                     if (mRecyclerView.canScrollVertically(1)) {
   9549                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
   9550                     }
   9551                     if (mRecyclerView.canScrollHorizontally(1)) {
   9552                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
   9553                     }
   9554                     break;
   9555             }
   9556             if (vScroll == 0 && hScroll == 0) {
   9557                 return false;
   9558             }
   9559             mRecyclerView.smoothScrollBy(hScroll, vScroll);
   9560             return true;
   9561         }
   9562 
   9563         // called by accessibility delegate
   9564         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
   9565             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
   9566                     view, action, args);
   9567         }
   9568 
   9569         /**
   9570          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
   9571          * children of LayoutManager.
   9572          * <p>
   9573          * Default implementation does not do anything.
   9574          *
   9575          * @param recycler The Recycler that can be used to convert view positions into adapter
   9576          *                 positions
   9577          * @param state    The current state of RecyclerView
   9578          * @param view     The child view on which the action is performed
   9579          * @param action   The action to perform
   9580          * @param args     Optional action arguments
   9581          * @return true if action is handled
   9582          * @see View#performAccessibilityAction(int, android.os.Bundle)
   9583          */
   9584         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
   9585                 int action, Bundle args) {
   9586             return false;
   9587         }
   9588 
   9589         /**
   9590          * Parse the xml attributes to get the most common properties used by layout managers.
   9591          *
   9592          * @return an object containing the properties as specified in the attrs.
   9593          */
   9594         public static Properties getProperties(Context context, AttributeSet attrs,
   9595                 int defStyleAttr, int defStyleRes) {
   9596             Properties properties = new Properties();
   9597             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
   9598                     defStyleAttr, defStyleRes);
   9599             properties.orientation = a.getInt(R.styleable.RecyclerView_orientation, VERTICAL);
   9600             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
   9601             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
   9602             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
   9603             a.recycle();
   9604             return properties;
   9605         }
   9606 
   9607         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
   9608             setMeasureSpecs(
   9609                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
   9610                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
   9611             );
   9612         }
   9613 
   9614         /**
   9615          * Internal API to allow LayoutManagers to be measured twice.
   9616          * <p>
   9617          * This is not public because LayoutManagers should be able to handle their layouts in one
   9618          * pass but it is very convenient to make existing LayoutManagers support wrapping content
   9619          * when both orientations are undefined.
   9620          * <p>
   9621          * This API will be removed after default LayoutManagers properly implement wrap content in
   9622          * non-scroll orientation.
   9623          */
   9624         boolean shouldMeasureTwice() {
   9625             return false;
   9626         }
   9627 
   9628         boolean hasFlexibleChildInBothOrientations() {
   9629             final int childCount = getChildCount();
   9630             for (int i = 0; i < childCount; i++) {
   9631                 final View child = getChildAt(i);
   9632                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
   9633                 if (lp.width < 0 && lp.height < 0) {
   9634                     return true;
   9635                 }
   9636             }
   9637             return false;
   9638         }
   9639 
   9640         /**
   9641          * Some general properties that a LayoutManager may want to use.
   9642          */
   9643         public static class Properties {
   9644             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
   9645             public int orientation;
   9646             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
   9647             public int spanCount;
   9648             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
   9649             public boolean reverseLayout;
   9650             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
   9651             public boolean stackFromEnd;
   9652         }
   9653     }
   9654 
   9655     /**
   9656      * An ItemDecoration allows the application to add a special drawing and layout offset
   9657      * to specific item views from the adapter's data set. This can be useful for drawing dividers
   9658      * between items, highlights, visual grouping boundaries and more.
   9659      *
   9660      * <p>All ItemDecorations are drawn in the order they were added, before the item
   9661      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
   9662      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
   9663      * RecyclerView.State)}.</p>
   9664      */
   9665     public abstract static class ItemDecoration {
   9666         /**
   9667          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   9668          * Any content drawn by this method will be drawn before the item views are drawn,
   9669          * and will thus appear underneath the views.
   9670          *
   9671          * @param c Canvas to draw into
   9672          * @param parent RecyclerView this ItemDecoration is drawing into
   9673          * @param state The current state of RecyclerView
   9674          */
   9675         public void onDraw(Canvas c, RecyclerView parent, State state) {
   9676             onDraw(c, parent);
   9677         }
   9678 
   9679         /**
   9680          * @deprecated
   9681          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
   9682          */
   9683         @Deprecated
   9684         public void onDraw(Canvas c, RecyclerView parent) {
   9685         }
   9686 
   9687         /**
   9688          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   9689          * Any content drawn by this method will be drawn after the item views are drawn
   9690          * and will thus appear over the views.
   9691          *
   9692          * @param c Canvas to draw into
   9693          * @param parent RecyclerView this ItemDecoration is drawing into
   9694          * @param state The current state of RecyclerView.
   9695          */
   9696         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
   9697             onDrawOver(c, parent);
   9698         }
   9699 
   9700         /**
   9701          * @deprecated
   9702          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
   9703          */
   9704         @Deprecated
   9705         public void onDrawOver(Canvas c, RecyclerView parent) {
   9706         }
   9707 
   9708 
   9709         /**
   9710          * @deprecated
   9711          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
   9712          */
   9713         @Deprecated
   9714         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
   9715             outRect.set(0, 0, 0, 0);
   9716         }
   9717 
   9718         /**
   9719          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
   9720          * the number of pixels that the item view should be inset by, similar to padding or margin.
   9721          * The default implementation sets the bounds of outRect to 0 and returns.
   9722          *
   9723          * <p>
   9724          * If this ItemDecoration does not affect the positioning of item views, it should set
   9725          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
   9726          * before returning.
   9727          *
   9728          * <p>
   9729          * If you need to access Adapter for additional data, you can call
   9730          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
   9731          * View.
   9732          *
   9733          * @param outRect Rect to receive the output.
   9734          * @param view    The child view to decorate
   9735          * @param parent  RecyclerView this ItemDecoration is decorating
   9736          * @param state   The current state of RecyclerView.
   9737          */
   9738         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
   9739             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
   9740                     parent);
   9741         }
   9742     }
   9743 
   9744     /**
   9745      * An OnItemTouchListener allows the application to intercept touch events in progress at the
   9746      * view hierarchy level of the RecyclerView before those touch events are considered for
   9747      * RecyclerView's own scrolling behavior.
   9748      *
   9749      * <p>This can be useful for applications that wish to implement various forms of gestural
   9750      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
   9751      * a touch interaction already in progress even if the RecyclerView is already handling that
   9752      * gesture stream itself for the purposes of scrolling.</p>
   9753      *
   9754      * @see SimpleOnItemTouchListener
   9755      */
   9756     public interface OnItemTouchListener {
   9757         /**
   9758          * Silently observe and/or take over touch events sent to the RecyclerView
   9759          * before they are handled by either the RecyclerView itself or its child views.
   9760          *
   9761          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
   9762          * in the order in which each listener was added, before any other touch processing
   9763          * by the RecyclerView itself or child views occurs.</p>
   9764          *
   9765          * @param e MotionEvent describing the touch event. All coordinates are in
   9766          *          the RecyclerView's coordinate system.
   9767          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
   9768          *         to continue with the current behavior and continue observing future events in
   9769          *         the gesture.
   9770          */
   9771         boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
   9772 
   9773         /**
   9774          * Process a touch event as part of a gesture that was claimed by returning true from
   9775          * a previous call to {@link #onInterceptTouchEvent}.
   9776          *
   9777          * @param e MotionEvent describing the touch event. All coordinates are in
   9778          *          the RecyclerView's coordinate system.
   9779          */
   9780         void onTouchEvent(RecyclerView rv, MotionEvent e);
   9781 
   9782         /**
   9783          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
   9784          * intercept touch events with
   9785          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
   9786          *
   9787          * @param disallowIntercept True if the child does not want the parent to
   9788          *            intercept touch events.
   9789          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
   9790          */
   9791         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
   9792     }
   9793 
   9794     /**
   9795      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
   9796      * and default return values.
   9797      * <p>
   9798      * You may prefer to extend this class if you don't need to override all methods. Another
   9799      * benefit of using this class is future compatibility. As the interface may change, we'll
   9800      * always provide a default implementation on this class so that your code won't break when
   9801      * you update to a new version of the support library.
   9802      */
   9803     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
   9804         @Override
   9805         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
   9806             return false;
   9807         }
   9808 
   9809         @Override
   9810         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
   9811         }
   9812 
   9813         @Override
   9814         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   9815         }
   9816     }
   9817 
   9818 
   9819     /**
   9820      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
   9821      * has occurred on that RecyclerView.
   9822      * <p>
   9823      * @see RecyclerView#addOnScrollListener(OnScrollListener)
   9824      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
   9825      *
   9826      */
   9827     public abstract static class OnScrollListener {
   9828         /**
   9829          * Callback method to be invoked when RecyclerView's scroll state changes.
   9830          *
   9831          * @param recyclerView The RecyclerView whose scroll state has changed.
   9832          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
   9833          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
   9834          */
   9835         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
   9836 
   9837         /**
   9838          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
   9839          * called after the scroll has completed.
   9840          * <p>
   9841          * This callback will also be called if visible item range changes after a layout
   9842          * calculation. In that case, dx and dy will be 0.
   9843          *
   9844          * @param recyclerView The RecyclerView which scrolled.
   9845          * @param dx The amount of horizontal scroll.
   9846          * @param dy The amount of vertical scroll.
   9847          */
   9848         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
   9849     }
   9850 
   9851     /**
   9852      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
   9853      * a view is recycled.
   9854      *
   9855      * @see RecyclerView#setRecyclerListener(RecyclerListener)
   9856      */
   9857     public interface RecyclerListener {
   9858 
   9859         /**
   9860          * This method is called whenever the view in the ViewHolder is recycled.
   9861          *
   9862          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   9863          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   9864          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   9865          * its adapter position.
   9866          *
   9867          * @param holder The ViewHolder containing the view that was recycled
   9868          */
   9869         void onViewRecycled(ViewHolder holder);
   9870     }
   9871 
   9872     /**
   9873      * A Listener interface that can be attached to a RecylcerView to get notified
   9874      * whenever a ViewHolder is attached to or detached from RecyclerView.
   9875      */
   9876     public interface OnChildAttachStateChangeListener {
   9877 
   9878         /**
   9879          * Called when a view is attached to the RecyclerView.
   9880          *
   9881          * @param view The View which is attached to the RecyclerView
   9882          */
   9883         void onChildViewAttachedToWindow(View view);
   9884 
   9885         /**
   9886          * Called when a view is detached from RecyclerView.
   9887          *
   9888          * @param view The View which is being detached from the RecyclerView
   9889          */
   9890         void onChildViewDetachedFromWindow(View view);
   9891     }
   9892 
   9893     /**
   9894      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
   9895      *
   9896      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
   9897      * potentially expensive {@link View#findViewById(int)} results.</p>
   9898      *
   9899      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
   9900      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
   9901      * their own custom ViewHolder implementations to store data that makes binding view contents
   9902      * easier. Implementations should assume that individual item views will hold strong references
   9903      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
   9904      * strong references to extra off-screen item views for caching purposes</p>
   9905      */
   9906     public abstract static class ViewHolder {
   9907         public final View itemView;
   9908         WeakReference<RecyclerView> mNestedRecyclerView;
   9909         int mPosition = NO_POSITION;
   9910         int mOldPosition = NO_POSITION;
   9911         long mItemId = NO_ID;
   9912         int mItemViewType = INVALID_TYPE;
   9913         int mPreLayoutPosition = NO_POSITION;
   9914 
   9915         // The item that this holder is shadowing during an item change event/animation
   9916         ViewHolder mShadowedHolder = null;
   9917         // The item that is shadowing this holder during an item change event/animation
   9918         ViewHolder mShadowingHolder = null;
   9919 
   9920         /**
   9921          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
   9922          * are all valid.
   9923          */
   9924         static final int FLAG_BOUND = 1 << 0;
   9925 
   9926         /**
   9927          * The data this ViewHolder's view reflects is stale and needs to be rebound
   9928          * by the adapter. mPosition and mItemId are consistent.
   9929          */
   9930         static final int FLAG_UPDATE = 1 << 1;
   9931 
   9932         /**
   9933          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
   9934          * are not to be trusted and may no longer match the item view type.
   9935          * This ViewHolder must be fully rebound to different data.
   9936          */
   9937         static final int FLAG_INVALID = 1 << 2;
   9938 
   9939         /**
   9940          * This ViewHolder points at data that represents an item previously removed from the
   9941          * data set. Its view may still be used for things like outgoing animations.
   9942          */
   9943         static final int FLAG_REMOVED = 1 << 3;
   9944 
   9945         /**
   9946          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
   9947          * and is intended to keep views around during animations.
   9948          */
   9949         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
   9950 
   9951         /**
   9952          * This ViewHolder is returned from scrap which means we are expecting an addView call
   9953          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
   9954          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
   9955          * the RecyclerView.
   9956          */
   9957         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
   9958 
   9959         /**
   9960          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
   9961          * it unless LayoutManager is replaced.
   9962          * It is still fully visible to the LayoutManager.
   9963          */
   9964         static final int FLAG_IGNORE = 1 << 7;
   9965 
   9966         /**
   9967          * When the View is detached form the parent, we set this flag so that we can take correct
   9968          * action when we need to remove it or add it back.
   9969          */
   9970         static final int FLAG_TMP_DETACHED = 1 << 8;
   9971 
   9972         /**
   9973          * Set when we can no longer determine the adapter position of this ViewHolder until it is
   9974          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
   9975          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
   9976          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
   9977          * re-calculated.
   9978          */
   9979         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
   9980 
   9981         /**
   9982          * Set when a addChangePayload(null) is called
   9983          */
   9984         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
   9985 
   9986         /**
   9987          * Used by ItemAnimator when a ViewHolder's position changes
   9988          */
   9989         static final int FLAG_MOVED = 1 << 11;
   9990 
   9991         /**
   9992          * Used by ItemAnimator when a ViewHolder appears in pre-layout
   9993          */
   9994         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
   9995 
   9996         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
   9997 
   9998         /**
   9999          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
   10000          * hidden list (as if it was scrap) without being recycled in between.
   10001          *
   10002          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
   10003          *   a) Animation ends, view is recycled and used from the recycle pool.
   10004          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
   10005          *
   10006          * This flag is used to represent "case b" where the ViewHolder is reused without being
   10007          * recycled (thus "bounced" from the hidden list). This state requires special handling
   10008          * because the ViewHolder must be added to pre layout maps for animations as if it was
   10009          * already there.
   10010          */
   10011         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
   10012 
   10013         private int mFlags;
   10014 
   10015         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
   10016 
   10017         List<Object> mPayloads = null;
   10018         List<Object> mUnmodifiedPayloads = null;
   10019 
   10020         private int mIsRecyclableCount = 0;
   10021 
   10022         // If non-null, view is currently considered scrap and may be reused for other data by the
   10023         // scrap container.
   10024         private Recycler mScrapContainer = null;
   10025         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
   10026         private boolean mInChangeScrap = false;
   10027 
   10028         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
   10029         // marked as unimportant for accessibility.
   10030         private int mWasImportantForAccessibilityBeforeHidden =
   10031                 View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   10032         // set if we defer the accessibility state change of the view holder
   10033         @VisibleForTesting
   10034         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
   10035 
   10036         /**
   10037          * Is set when VH is bound from the adapter and cleaned right before it is sent to
   10038          * {@link RecycledViewPool}.
   10039          */
   10040         RecyclerView mOwnerRecyclerView;
   10041 
   10042         public ViewHolder(View itemView) {
   10043             if (itemView == null) {
   10044                 throw new IllegalArgumentException("itemView may not be null");
   10045             }
   10046             this.itemView = itemView;
   10047         }
   10048 
   10049         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
   10050             addFlags(ViewHolder.FLAG_REMOVED);
   10051             offsetPosition(offset, applyToPreLayout);
   10052             mPosition = mNewPosition;
   10053         }
   10054 
   10055         void offsetPosition(int offset, boolean applyToPreLayout) {
   10056             if (mOldPosition == NO_POSITION) {
   10057                 mOldPosition = mPosition;
   10058             }
   10059             if (mPreLayoutPosition == NO_POSITION) {
   10060                 mPreLayoutPosition = mPosition;
   10061             }
   10062             if (applyToPreLayout) {
   10063                 mPreLayoutPosition += offset;
   10064             }
   10065             mPosition += offset;
   10066             if (itemView.getLayoutParams() != null) {
   10067                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
   10068             }
   10069         }
   10070 
   10071         void clearOldPosition() {
   10072             mOldPosition = NO_POSITION;
   10073             mPreLayoutPosition = NO_POSITION;
   10074         }
   10075 
   10076         void saveOldPosition() {
   10077             if (mOldPosition == NO_POSITION) {
   10078                 mOldPosition = mPosition;
   10079             }
   10080         }
   10081 
   10082         boolean shouldIgnore() {
   10083             return (mFlags & FLAG_IGNORE) != 0;
   10084         }
   10085 
   10086         /**
   10087          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
   10088          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
   10089          * {@link #getAdapterPosition()} depending on your use case.
   10090          *
   10091          * @see #getLayoutPosition()
   10092          * @see #getAdapterPosition()
   10093          */
   10094         @Deprecated
   10095         public final int getPosition() {
   10096             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   10097         }
   10098 
   10099         /**
   10100          * Returns the position of the ViewHolder in terms of the latest layout pass.
   10101          * <p>
   10102          * This position is mostly used by RecyclerView components to be consistent while
   10103          * RecyclerView lazily processes adapter updates.
   10104          * <p>
   10105          * For performance and animation reasons, RecyclerView batches all adapter updates until the
   10106          * next layout pass. This may cause mismatches between the Adapter position of the item and
   10107          * the position it had in the latest layout calculations.
   10108          * <p>
   10109          * LayoutManagers should always call this method while doing calculations based on item
   10110          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
   10111          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
   10112          * of the item.
   10113          * <p>
   10114          * If LayoutManager needs to call an external method that requires the adapter position of
   10115          * the item, it can use {@link #getAdapterPosition()} or
   10116          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
   10117          *
   10118          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
   10119          * @see #getAdapterPosition()
   10120          */
   10121         public final int getLayoutPosition() {
   10122             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   10123         }
   10124 
   10125         /**
   10126          * Returns the Adapter position of the item represented by this ViewHolder.
   10127          * <p>
   10128          * Note that this might be different than the {@link #getLayoutPosition()} if there are
   10129          * pending adapter updates but a new layout pass has not happened yet.
   10130          * <p>
   10131          * RecyclerView does not handle any adapter updates until the next layout traversal. This
   10132          * may create temporary inconsistencies between what user sees on the screen and what
   10133          * adapter contents have. This inconsistency is not important since it will be less than
   10134          * 16ms but it might be a problem if you want to use ViewHolder position to access the
   10135          * adapter. Sometimes, you may need to get the exact adapter position to do
   10136          * some actions in response to user events. In that case, you should use this method which
   10137          * will calculate the Adapter position of the ViewHolder.
   10138          * <p>
   10139          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
   10140          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
   10141          *
   10142          * @return The adapter position of the item if it still exists in the adapter.
   10143          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
   10144          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
   10145          * layout pass or the ViewHolder has already been recycled.
   10146          */
   10147         public final int getAdapterPosition() {
   10148             if (mOwnerRecyclerView == null) {
   10149                 return NO_POSITION;
   10150             }
   10151             return mOwnerRecyclerView.getAdapterPositionFor(this);
   10152         }
   10153 
   10154         /**
   10155          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
   10156          * to perform animations.
   10157          * <p>
   10158          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
   10159          * adapter index in the previous layout.
   10160          *
   10161          * @return The previous adapter index of the Item represented by this ViewHolder or
   10162          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
   10163          * complete).
   10164          */
   10165         public final int getOldPosition() {
   10166             return mOldPosition;
   10167         }
   10168 
   10169         /**
   10170          * Returns The itemId represented by this ViewHolder.
   10171          *
   10172          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
   10173          * otherwise
   10174          */
   10175         public final long getItemId() {
   10176             return mItemId;
   10177         }
   10178 
   10179         /**
   10180          * @return The view type of this ViewHolder.
   10181          */
   10182         public final int getItemViewType() {
   10183             return mItemViewType;
   10184         }
   10185 
   10186         boolean isScrap() {
   10187             return mScrapContainer != null;
   10188         }
   10189 
   10190         void unScrap() {
   10191             mScrapContainer.unscrapView(this);
   10192         }
   10193 
   10194         boolean wasReturnedFromScrap() {
   10195             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
   10196         }
   10197 
   10198         void clearReturnedFromScrapFlag() {
   10199             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
   10200         }
   10201 
   10202         void clearTmpDetachFlag() {
   10203             mFlags = mFlags & ~FLAG_TMP_DETACHED;
   10204         }
   10205 
   10206         void stopIgnoring() {
   10207             mFlags = mFlags & ~FLAG_IGNORE;
   10208         }
   10209 
   10210         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
   10211             mScrapContainer = recycler;
   10212             mInChangeScrap = isChangeScrap;
   10213         }
   10214 
   10215         boolean isInvalid() {
   10216             return (mFlags & FLAG_INVALID) != 0;
   10217         }
   10218 
   10219         boolean needsUpdate() {
   10220             return (mFlags & FLAG_UPDATE) != 0;
   10221         }
   10222 
   10223         boolean isBound() {
   10224             return (mFlags & FLAG_BOUND) != 0;
   10225         }
   10226 
   10227         boolean isRemoved() {
   10228             return (mFlags & FLAG_REMOVED) != 0;
   10229         }
   10230 
   10231         boolean hasAnyOfTheFlags(int flags) {
   10232             return (mFlags & flags) != 0;
   10233         }
   10234 
   10235         boolean isTmpDetached() {
   10236             return (mFlags & FLAG_TMP_DETACHED) != 0;
   10237         }
   10238 
   10239         boolean isAdapterPositionUnknown() {
   10240             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
   10241         }
   10242 
   10243         void setFlags(int flags, int mask) {
   10244             mFlags = (mFlags & ~mask) | (flags & mask);
   10245         }
   10246 
   10247         void addFlags(int flags) {
   10248             mFlags |= flags;
   10249         }
   10250 
   10251         void addChangePayload(Object payload) {
   10252             if (payload == null) {
   10253                 addFlags(FLAG_ADAPTER_FULLUPDATE);
   10254             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   10255                 createPayloadsIfNeeded();
   10256                 mPayloads.add(payload);
   10257             }
   10258         }
   10259 
   10260         private void createPayloadsIfNeeded() {
   10261             if (mPayloads == null) {
   10262                 mPayloads = new ArrayList<Object>();
   10263                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
   10264             }
   10265         }
   10266 
   10267         void clearPayload() {
   10268             if (mPayloads != null) {
   10269                 mPayloads.clear();
   10270             }
   10271             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
   10272         }
   10273 
   10274         List<Object> getUnmodifiedPayloads() {
   10275             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   10276                 if (mPayloads == null || mPayloads.size() == 0) {
   10277                     // Initial state,  no update being called.
   10278                     return FULLUPDATE_PAYLOADS;
   10279                 }
   10280                 // there are none-null payloads
   10281                 return mUnmodifiedPayloads;
   10282             } else {
   10283                 // a full update has been called.
   10284                 return FULLUPDATE_PAYLOADS;
   10285             }
   10286         }
   10287 
   10288         void resetInternal() {
   10289             mFlags = 0;
   10290             mPosition = NO_POSITION;
   10291             mOldPosition = NO_POSITION;
   10292             mItemId = NO_ID;
   10293             mPreLayoutPosition = NO_POSITION;
   10294             mIsRecyclableCount = 0;
   10295             mShadowedHolder = null;
   10296             mShadowingHolder = null;
   10297             clearPayload();
   10298             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   10299             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
   10300             clearNestedRecyclerViewIfNotNested(this);
   10301         }
   10302 
   10303         /**
   10304          * Called when the child view enters the hidden state
   10305          */
   10306         private void onEnteredHiddenState(RecyclerView parent) {
   10307             // While the view item is in hidden state, make it invisible for the accessibility.
   10308             mWasImportantForAccessibilityBeforeHidden =
   10309                     itemView.getImportantForAccessibility();
   10310             parent.setChildImportantForAccessibilityInternal(this,
   10311                     View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
   10312         }
   10313 
   10314         /**
   10315          * Called when the child view leaves the hidden state
   10316          */
   10317         private void onLeftHiddenState(RecyclerView parent) {
   10318             parent.setChildImportantForAccessibilityInternal(this,
   10319                     mWasImportantForAccessibilityBeforeHidden);
   10320             mWasImportantForAccessibilityBeforeHidden = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   10321         }
   10322 
   10323         @Override
   10324         public String toString() {
   10325             final StringBuilder sb = new StringBuilder("ViewHolder{"
   10326                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
   10327                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
   10328             if (isScrap()) {
   10329                 sb.append(" scrap ")
   10330                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
   10331             }
   10332             if (isInvalid()) sb.append(" invalid");
   10333             if (!isBound()) sb.append(" unbound");
   10334             if (needsUpdate()) sb.append(" update");
   10335             if (isRemoved()) sb.append(" removed");
   10336             if (shouldIgnore()) sb.append(" ignored");
   10337             if (isTmpDetached()) sb.append(" tmpDetached");
   10338             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
   10339             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
   10340 
   10341             if (itemView.getParent() == null) sb.append(" no parent");
   10342             sb.append("}");
   10343             return sb.toString();
   10344         }
   10345 
   10346         /**
   10347          * Informs the recycler whether this item can be recycled. Views which are not
   10348          * recyclable will not be reused for other items until setIsRecyclable() is
   10349          * later set to true. Calls to setIsRecyclable() should always be paired (one
   10350          * call to setIsRecyclabe(false) should always be matched with a later call to
   10351          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
   10352          * reference-counted.
   10353          *
   10354          * @param recyclable Whether this item is available to be recycled. Default value
   10355          * is true.
   10356          *
   10357          * @see #isRecyclable()
   10358          */
   10359         public final void setIsRecyclable(boolean recyclable) {
   10360             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
   10361             if (mIsRecyclableCount < 0) {
   10362                 mIsRecyclableCount = 0;
   10363                 if (DEBUG) {
   10364                     throw new RuntimeException("isRecyclable decremented below 0: "
   10365                             + "unmatched pair of setIsRecyable() calls for " + this);
   10366                 }
   10367                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
   10368                         + "unmatched pair of setIsRecyable() calls for " + this);
   10369             } else if (!recyclable && mIsRecyclableCount == 1) {
   10370                 mFlags |= FLAG_NOT_RECYCLABLE;
   10371             } else if (recyclable && mIsRecyclableCount == 0) {
   10372                 mFlags &= ~FLAG_NOT_RECYCLABLE;
   10373             }
   10374             if (DEBUG) {
   10375                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
   10376             }
   10377         }
   10378 
   10379         /**
   10380          * @return true if this item is available to be recycled, false otherwise.
   10381          *
   10382          * @see #setIsRecyclable(boolean)
   10383          */
   10384         public final boolean isRecyclable() {
   10385             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
   10386                     && !itemView.hasTransientState();
   10387         }
   10388 
   10389         /**
   10390          * Returns whether we have animations referring to this view holder or not.
   10391          * This is similar to isRecyclable flag but does not check transient state.
   10392          */
   10393         private boolean shouldBeKeptAsChild() {
   10394             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
   10395         }
   10396 
   10397         /**
   10398          * @return True if ViewHolder is not referenced by RecyclerView animations but has
   10399          * transient state which will prevent it from being recycled.
   10400          */
   10401         private boolean doesTransientStatePreventRecycling() {
   10402             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && itemView.hasTransientState();
   10403         }
   10404 
   10405         boolean isUpdated() {
   10406             return (mFlags & FLAG_UPDATE) != 0;
   10407         }
   10408     }
   10409 
   10410     /**
   10411      * This method is here so that we can control the important for a11y changes and test it.
   10412      */
   10413     @VisibleForTesting
   10414     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
   10415             int importantForAccessibility) {
   10416         if (isComputingLayout()) {
   10417             viewHolder.mPendingAccessibilityState = importantForAccessibility;
   10418             mPendingAccessibilityImportanceChange.add(viewHolder);
   10419             return false;
   10420         }
   10421         viewHolder.itemView.setImportantForAccessibility(importantForAccessibility);
   10422         return true;
   10423     }
   10424 
   10425     void dispatchPendingImportantForAccessibilityChanges() {
   10426         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
   10427             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
   10428             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
   10429                 continue;
   10430             }
   10431             int state = viewHolder.mPendingAccessibilityState;
   10432             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
   10433                 //noinspection WrongConstant
   10434                 viewHolder.itemView.setImportantForAccessibility(state);
   10435                 viewHolder.mPendingAccessibilityState =
   10436                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
   10437             }
   10438         }
   10439         mPendingAccessibilityImportanceChange.clear();
   10440     }
   10441 
   10442     int getAdapterPositionFor(ViewHolder viewHolder) {
   10443         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
   10444                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
   10445                 || !viewHolder.isBound()) {
   10446             return RecyclerView.NO_POSITION;
   10447         }
   10448         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
   10449     }
   10450 
   10451     /**
   10452      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
   10453      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
   10454      * to create their own subclass of this <code>LayoutParams</code> class
   10455      * to store any additional required per-child view metadata about the layout.
   10456      */
   10457     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
   10458         ViewHolder mViewHolder;
   10459         final Rect mDecorInsets = new Rect();
   10460         boolean mInsetsDirty = true;
   10461         // Flag is set to true if the view is bound while it is detached from RV.
   10462         // In this case, we need to manually call invalidate after view is added to guarantee that
   10463         // invalidation is populated through the View hierarchy
   10464         boolean mPendingInvalidate = false;
   10465 
   10466         public LayoutParams(Context c, AttributeSet attrs) {
   10467             super(c, attrs);
   10468         }
   10469 
   10470         public LayoutParams(int width, int height) {
   10471             super(width, height);
   10472         }
   10473 
   10474         public LayoutParams(MarginLayoutParams source) {
   10475             super(source);
   10476         }
   10477 
   10478         public LayoutParams(ViewGroup.LayoutParams source) {
   10479             super(source);
   10480         }
   10481 
   10482         public LayoutParams(LayoutParams source) {
   10483             super((ViewGroup.LayoutParams) source);
   10484         }
   10485 
   10486         /**
   10487          * Returns true if the view this LayoutParams is attached to needs to have its content
   10488          * updated from the corresponding adapter.
   10489          *
   10490          * @return true if the view should have its content updated
   10491          */
   10492         public boolean viewNeedsUpdate() {
   10493             return mViewHolder.needsUpdate();
   10494         }
   10495 
   10496         /**
   10497          * Returns true if the view this LayoutParams is attached to is now representing
   10498          * potentially invalid data. A LayoutManager should scrap/recycle it.
   10499          *
   10500          * @return true if the view is invalid
   10501          */
   10502         public boolean isViewInvalid() {
   10503             return mViewHolder.isInvalid();
   10504         }
   10505 
   10506         /**
   10507          * Returns true if the adapter data item corresponding to the view this LayoutParams
   10508          * is attached to has been removed from the data set. A LayoutManager may choose to
   10509          * treat it differently in order to animate its outgoing or disappearing state.
   10510          *
   10511          * @return true if the item the view corresponds to was removed from the data set
   10512          */
   10513         public boolean isItemRemoved() {
   10514             return mViewHolder.isRemoved();
   10515         }
   10516 
   10517         /**
   10518          * Returns true if the adapter data item corresponding to the view this LayoutParams
   10519          * is attached to has been changed in the data set. A LayoutManager may choose to
   10520          * treat it differently in order to animate its changing state.
   10521          *
   10522          * @return true if the item the view corresponds to was changed in the data set
   10523          */
   10524         public boolean isItemChanged() {
   10525             return mViewHolder.isUpdated();
   10526         }
   10527 
   10528         /**
   10529          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
   10530          */
   10531         @Deprecated
   10532         public int getViewPosition() {
   10533             return mViewHolder.getPosition();
   10534         }
   10535 
   10536         /**
   10537          * Returns the adapter position that the view this LayoutParams is attached to corresponds
   10538          * to as of latest layout calculation.
   10539          *
   10540          * @return the adapter position this view as of latest layout pass
   10541          */
   10542         public int getViewLayoutPosition() {
   10543             return mViewHolder.getLayoutPosition();
   10544         }
   10545 
   10546         /**
   10547          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
   10548          * corresponds to.
   10549          *
   10550          * @return the up-to-date adapter position this view. It may return
   10551          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
   10552          * its up-to-date position cannot be calculated.
   10553          */
   10554         public int getViewAdapterPosition() {
   10555             return mViewHolder.getAdapterPosition();
   10556         }
   10557     }
   10558 
   10559     /**
   10560      * Observer base class for watching changes to an {@link Adapter}.
   10561      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
   10562      */
   10563     public abstract static class AdapterDataObserver {
   10564         public void onChanged() {
   10565             // Do nothing
   10566         }
   10567 
   10568         public void onItemRangeChanged(int positionStart, int itemCount) {
   10569             // do nothing
   10570         }
   10571 
   10572         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   10573             // fallback to onItemRangeChanged(positionStart, itemCount) if app
   10574             // does not override this method.
   10575             onItemRangeChanged(positionStart, itemCount);
   10576         }
   10577 
   10578         public void onItemRangeInserted(int positionStart, int itemCount) {
   10579             // do nothing
   10580         }
   10581 
   10582         public void onItemRangeRemoved(int positionStart, int itemCount) {
   10583             // do nothing
   10584         }
   10585 
   10586         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   10587             // do nothing
   10588         }
   10589     }
   10590 
   10591     /**
   10592      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
   10593      * provides methods to trigger a programmatic scroll.</p>
   10594      *
   10595      * @see LinearSmoothScroller
   10596      */
   10597     public abstract static class SmoothScroller {
   10598 
   10599         private int mTargetPosition = RecyclerView.NO_POSITION;
   10600 
   10601         private RecyclerView mRecyclerView;
   10602 
   10603         private LayoutManager mLayoutManager;
   10604 
   10605         private boolean mPendingInitialRun;
   10606 
   10607         private boolean mRunning;
   10608 
   10609         private View mTargetView;
   10610 
   10611         private final Action mRecyclingAction;
   10612 
   10613         public SmoothScroller() {
   10614             mRecyclingAction = new Action(0, 0);
   10615         }
   10616 
   10617         /**
   10618          * Starts a smooth scroll for the given target position.
   10619          * <p>In each animation step, {@link RecyclerView} will check
   10620          * for the target view and call either
   10621          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   10622          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
   10623          * SmoothScroller is stopped.</p>
   10624          *
   10625          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
   10626          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
   10627          * stop calling SmoothScroller in each animation step.</p>
   10628          */
   10629         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
   10630             mRecyclerView = recyclerView;
   10631             mLayoutManager = layoutManager;
   10632             if (mTargetPosition == RecyclerView.NO_POSITION) {
   10633                 throw new IllegalArgumentException("Invalid target position");
   10634             }
   10635             mRecyclerView.mState.mTargetPosition = mTargetPosition;
   10636             mRunning = true;
   10637             mPendingInitialRun = true;
   10638             mTargetView = findViewByPosition(getTargetPosition());
   10639             onStart();
   10640             mRecyclerView.mViewFlinger.postOnAnimation();
   10641         }
   10642 
   10643         public void setTargetPosition(int targetPosition) {
   10644             mTargetPosition = targetPosition;
   10645         }
   10646 
   10647         /**
   10648          * @return The LayoutManager to which this SmoothScroller is attached. Will return
   10649          * <code>null</code> after the SmoothScroller is stopped.
   10650          */
   10651         @Nullable
   10652         public LayoutManager getLayoutManager() {
   10653             return mLayoutManager;
   10654         }
   10655 
   10656         /**
   10657          * Stops running the SmoothScroller in each animation callback. Note that this does not
   10658          * cancel any existing {@link Action} updated by
   10659          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   10660          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
   10661          */
   10662         protected final void stop() {
   10663             if (!mRunning) {
   10664                 return;
   10665             }
   10666             onStop();
   10667             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
   10668             mTargetView = null;
   10669             mTargetPosition = RecyclerView.NO_POSITION;
   10670             mPendingInitialRun = false;
   10671             mRunning = false;
   10672             // trigger a cleanup
   10673             mLayoutManager.onSmoothScrollerStopped(this);
   10674             // clear references to avoid any potential leak by a custom smooth scroller
   10675             mLayoutManager = null;
   10676             mRecyclerView = null;
   10677         }
   10678 
   10679         /**
   10680          * Returns true if SmoothScroller has been started but has not received the first
   10681          * animation
   10682          * callback yet.
   10683          *
   10684          * @return True if this SmoothScroller is waiting to start
   10685          */
   10686         public boolean isPendingInitialRun() {
   10687             return mPendingInitialRun;
   10688         }
   10689 
   10690 
   10691         /**
   10692          * @return True if SmoothScroller is currently active
   10693          */
   10694         public boolean isRunning() {
   10695             return mRunning;
   10696         }
   10697 
   10698         /**
   10699          * Returns the adapter position of the target item
   10700          *
   10701          * @return Adapter position of the target item or
   10702          * {@link RecyclerView#NO_POSITION} if no target view is set.
   10703          */
   10704         public int getTargetPosition() {
   10705             return mTargetPosition;
   10706         }
   10707 
   10708         private void onAnimation(int dx, int dy) {
   10709             final RecyclerView recyclerView = mRecyclerView;
   10710             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
   10711                 stop();
   10712             }
   10713             mPendingInitialRun = false;
   10714             if (mTargetView != null) {
   10715                 // verify target position
   10716                 if (getChildPosition(mTargetView) == mTargetPosition) {
   10717                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
   10718                     mRecyclingAction.runIfNecessary(recyclerView);
   10719                     stop();
   10720                 } else {
   10721                     Log.e(TAG, "Passed over target position while smooth scrolling.");
   10722                     mTargetView = null;
   10723                 }
   10724             }
   10725             if (mRunning) {
   10726                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
   10727                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
   10728                 mRecyclingAction.runIfNecessary(recyclerView);
   10729                 if (hadJumpTarget) {
   10730                     // It is not stopped so needs to be restarted
   10731                     if (mRunning) {
   10732                         mPendingInitialRun = true;
   10733                         recyclerView.mViewFlinger.postOnAnimation();
   10734                     } else {
   10735                         stop(); // done
   10736                     }
   10737                 }
   10738             }
   10739         }
   10740 
   10741         /**
   10742          * @see RecyclerView#getChildLayoutPosition(android.view.View)
   10743          */
   10744         public int getChildPosition(View view) {
   10745             return mRecyclerView.getChildLayoutPosition(view);
   10746         }
   10747 
   10748         /**
   10749          * @see RecyclerView.LayoutManager#getChildCount()
   10750          */
   10751         public int getChildCount() {
   10752             return mRecyclerView.mLayout.getChildCount();
   10753         }
   10754 
   10755         /**
   10756          * @see RecyclerView.LayoutManager#findViewByPosition(int)
   10757          */
   10758         public View findViewByPosition(int position) {
   10759             return mRecyclerView.mLayout.findViewByPosition(position);
   10760         }
   10761 
   10762         /**
   10763          * @see RecyclerView#scrollToPosition(int)
   10764          * @deprecated Use {@link Action#jumpTo(int)}.
   10765          */
   10766         @Deprecated
   10767         public void instantScrollToPosition(int position) {
   10768             mRecyclerView.scrollToPosition(position);
   10769         }
   10770 
   10771         protected void onChildAttachedToWindow(View child) {
   10772             if (getChildPosition(child) == getTargetPosition()) {
   10773                 mTargetView = child;
   10774                 if (DEBUG) {
   10775                     Log.d(TAG, "smooth scroll target view has been attached");
   10776                 }
   10777             }
   10778         }
   10779 
   10780         /**
   10781          * Normalizes the vector.
   10782          * @param scrollVector The vector that points to the target scroll position
   10783          */
   10784         protected void normalize(PointF scrollVector) {
   10785             final double magnitude = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y
   10786                     * scrollVector.y);
   10787             scrollVector.x /= magnitude;
   10788             scrollVector.y /= magnitude;
   10789         }
   10790 
   10791         /**
   10792          * Called when smooth scroll is started. This might be a good time to do setup.
   10793          */
   10794         protected abstract void onStart();
   10795 
   10796         /**
   10797          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
   10798          * @see #stop()
   10799          */
   10800         protected abstract void onStop();
   10801 
   10802         /**
   10803          * <p>RecyclerView will call this method each time it scrolls until it can find the target
   10804          * position in the layout.</p>
   10805          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
   10806          * provided {@link Action} to define the next scroll.</p>
   10807          *
   10808          * @param dx        Last scroll amount horizontally
   10809          * @param dy        Last scroll amount vertically
   10810          * @param state     Transient state of RecyclerView
   10811          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
   10812          *                  update this object.
   10813          */
   10814         protected abstract void onSeekTargetStep(int dx, int dy, State state, Action action);
   10815 
   10816         /**
   10817          * Called when the target position is laid out. This is the last callback SmoothScroller
   10818          * will receive and it should update the provided {@link Action} to define the scroll
   10819          * details towards the target view.
   10820          * @param targetView    The view element which render the target position.
   10821          * @param state         Transient state of RecyclerView
   10822          * @param action        Action instance that you should update to define final scroll action
   10823          *                      towards the targetView
   10824          */
   10825         protected abstract void onTargetFound(View targetView, State state, Action action);
   10826 
   10827         /**
   10828          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
   10829          */
   10830         public static class Action {
   10831 
   10832             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
   10833 
   10834             private int mDx;
   10835 
   10836             private int mDy;
   10837 
   10838             private int mDuration;
   10839 
   10840             private int mJumpToPosition = NO_POSITION;
   10841 
   10842             private Interpolator mInterpolator;
   10843 
   10844             private boolean mChanged = false;
   10845 
   10846             // we track this variable to inform custom implementer if they are updating the action
   10847             // in every animation callback
   10848             private int mConsecutiveUpdates = 0;
   10849 
   10850             /**
   10851              * @param dx Pixels to scroll horizontally
   10852              * @param dy Pixels to scroll vertically
   10853              */
   10854             public Action(int dx, int dy) {
   10855                 this(dx, dy, UNDEFINED_DURATION, null);
   10856             }
   10857 
   10858             /**
   10859              * @param dx       Pixels to scroll horizontally
   10860              * @param dy       Pixels to scroll vertically
   10861              * @param duration Duration of the animation in milliseconds
   10862              */
   10863             public Action(int dx, int dy, int duration) {
   10864                 this(dx, dy, duration, null);
   10865             }
   10866 
   10867             /**
   10868              * @param dx           Pixels to scroll horizontally
   10869              * @param dy           Pixels to scroll vertically
   10870              * @param duration     Duration of the animation in milliseconds
   10871              * @param interpolator Interpolator to be used when calculating scroll position in each
   10872              *                     animation step
   10873              */
   10874             public Action(int dx, int dy, int duration, Interpolator interpolator) {
   10875                 mDx = dx;
   10876                 mDy = dy;
   10877                 mDuration = duration;
   10878                 mInterpolator = interpolator;
   10879             }
   10880 
   10881             /**
   10882              * Instead of specifying pixels to scroll, use the target position to jump using
   10883              * {@link RecyclerView#scrollToPosition(int)}.
   10884              * <p>
   10885              * You may prefer using this method if scroll target is really far away and you prefer
   10886              * to jump to a location and smooth scroll afterwards.
   10887              * <p>
   10888              * Note that calling this method takes priority over other update methods such as
   10889              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
   10890              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
   10891              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
   10892              * frame.
   10893              *
   10894              * @param targetPosition The target item position to scroll to using instant scrolling.
   10895              */
   10896             public void jumpTo(int targetPosition) {
   10897                 mJumpToPosition = targetPosition;
   10898             }
   10899 
   10900             boolean hasJumpTarget() {
   10901                 return mJumpToPosition >= 0;
   10902             }
   10903 
   10904             void runIfNecessary(RecyclerView recyclerView) {
   10905                 if (mJumpToPosition >= 0) {
   10906                     final int position = mJumpToPosition;
   10907                     mJumpToPosition = NO_POSITION;
   10908                     recyclerView.jumpToPositionForSmoothScroller(position);
   10909                     mChanged = false;
   10910                     return;
   10911                 }
   10912                 if (mChanged) {
   10913                     validate();
   10914                     if (mInterpolator == null) {
   10915                         if (mDuration == UNDEFINED_DURATION) {
   10916                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
   10917                         } else {
   10918                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
   10919                         }
   10920                     } else {
   10921                         recyclerView.mViewFlinger.smoothScrollBy(
   10922                                 mDx, mDy, mDuration, mInterpolator);
   10923                     }
   10924                     mConsecutiveUpdates++;
   10925                     if (mConsecutiveUpdates > 10) {
   10926                         // A new action is being set in every animation step. This looks like a bad
   10927                         // implementation. Inform developer.
   10928                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
   10929                                 + " you are not changing it unless necessary");
   10930                     }
   10931                     mChanged = false;
   10932                 } else {
   10933                     mConsecutiveUpdates = 0;
   10934                 }
   10935             }
   10936 
   10937             private void validate() {
   10938                 if (mInterpolator != null && mDuration < 1) {
   10939                     throw new IllegalStateException("If you provide an interpolator, you must"
   10940                             + " set a positive duration");
   10941                 } else if (mDuration < 1) {
   10942                     throw new IllegalStateException("Scroll duration must be a positive number");
   10943                 }
   10944             }
   10945 
   10946             public int getDx() {
   10947                 return mDx;
   10948             }
   10949 
   10950             public void setDx(int dx) {
   10951                 mChanged = true;
   10952                 mDx = dx;
   10953             }
   10954 
   10955             public int getDy() {
   10956                 return mDy;
   10957             }
   10958 
   10959             public void setDy(int dy) {
   10960                 mChanged = true;
   10961                 mDy = dy;
   10962             }
   10963 
   10964             public int getDuration() {
   10965                 return mDuration;
   10966             }
   10967 
   10968             public void setDuration(int duration) {
   10969                 mChanged = true;
   10970                 mDuration = duration;
   10971             }
   10972 
   10973             public Interpolator getInterpolator() {
   10974                 return mInterpolator;
   10975             }
   10976 
   10977             /**
   10978              * Sets the interpolator to calculate scroll steps
   10979              * @param interpolator The interpolator to use. If you specify an interpolator, you must
   10980              *                     also set the duration.
   10981              * @see #setDuration(int)
   10982              */
   10983             public void setInterpolator(Interpolator interpolator) {
   10984                 mChanged = true;
   10985                 mInterpolator = interpolator;
   10986             }
   10987 
   10988             /**
   10989              * Updates the action with given parameters.
   10990              * @param dx Pixels to scroll horizontally
   10991              * @param dy Pixels to scroll vertically
   10992              * @param duration Duration of the animation in milliseconds
   10993              * @param interpolator Interpolator to be used when calculating scroll position in each
   10994              *                     animation step
   10995              */
   10996             public void update(int dx, int dy, int duration, Interpolator interpolator) {
   10997                 mDx = dx;
   10998                 mDy = dy;
   10999                 mDuration = duration;
   11000                 mInterpolator = interpolator;
   11001                 mChanged = true;
   11002             }
   11003         }
   11004 
   11005         /**
   11006          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
   11007          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
   11008          */
   11009         public interface ScrollVectorProvider {
   11010             /**
   11011              * Should calculate the vector that points to the direction where the target position
   11012              * can be found.
   11013              * <p>
   11014              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
   11015              * the target position.
   11016              * <p>
   11017              * The magnitude of the vector is not important. It is always normalized before being
   11018              * used by the {@link LinearSmoothScroller}.
   11019              * <p>
   11020              * LayoutManager should not check whether the position exists in the adapter or not.
   11021              *
   11022              * @param targetPosition the target position to which the returned vector should point
   11023              *
   11024              * @return the scroll vector for a given position.
   11025              */
   11026             PointF computeScrollVectorForPosition(int targetPosition);
   11027         }
   11028     }
   11029 
   11030     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
   11031         public boolean hasObservers() {
   11032             return !mObservers.isEmpty();
   11033         }
   11034 
   11035         public void notifyChanged() {
   11036             // since onChanged() is implemented by the app, it could do anything, including
   11037             // removing itself from {@link mObservers} - and that could cause problems if
   11038             // an iterator is used on the ArrayList {@link mObservers}.
   11039             // to avoid such problems, just march thru the list in the reverse order.
   11040             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11041                 mObservers.get(i).onChanged();
   11042             }
   11043         }
   11044 
   11045         public void notifyItemRangeChanged(int positionStart, int itemCount) {
   11046             notifyItemRangeChanged(positionStart, itemCount, null);
   11047         }
   11048 
   11049         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
   11050             // since onItemRangeChanged() is implemented by the app, it could do anything, including
   11051             // removing itself from {@link mObservers} - and that could cause problems if
   11052             // an iterator is used on the ArrayList {@link mObservers}.
   11053             // to avoid such problems, just march thru the list in the reverse order.
   11054             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11055                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
   11056             }
   11057         }
   11058 
   11059         public void notifyItemRangeInserted(int positionStart, int itemCount) {
   11060             // since onItemRangeInserted() is implemented by the app, it could do anything,
   11061             // including removing itself from {@link mObservers} - and that could cause problems if
   11062             // an iterator is used on the ArrayList {@link mObservers}.
   11063             // to avoid such problems, just march thru the list in the reverse order.
   11064             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11065                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
   11066             }
   11067         }
   11068 
   11069         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
   11070             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
   11071             // removing itself from {@link mObservers} - and that could cause problems if
   11072             // an iterator is used on the ArrayList {@link mObservers}.
   11073             // to avoid such problems, just march thru the list in the reverse order.
   11074             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11075                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
   11076             }
   11077         }
   11078 
   11079         public void notifyItemMoved(int fromPosition, int toPosition) {
   11080             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11081                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
   11082             }
   11083         }
   11084     }
   11085 
   11086     /**
   11087      * This is public so that the CREATOR can be access on cold launch.
   11088      * @hide
   11089      */
   11090     public static class SavedState extends AbsSavedState {
   11091 
   11092         Parcelable mLayoutState;
   11093 
   11094         /**
   11095          * called by CREATOR
   11096          */
   11097         SavedState(Parcel in) {
   11098             super(in);
   11099             mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
   11100         }
   11101 
   11102         /**
   11103          * Called by onSaveInstanceState
   11104          */
   11105         SavedState(Parcelable superState) {
   11106             super(superState);
   11107         }
   11108 
   11109         @Override
   11110         public void writeToParcel(Parcel dest, int flags) {
   11111             super.writeToParcel(dest, flags);
   11112             dest.writeParcelable(mLayoutState, 0);
   11113         }
   11114 
   11115         void copyFrom(SavedState other) {
   11116             mLayoutState = other.mLayoutState;
   11117         }
   11118 
   11119         public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
   11120                     @Override
   11121                     public SavedState createFromParcel(Parcel in) {
   11122                         return new SavedState(in);
   11123                     }
   11124 
   11125                     @Override
   11126                     public SavedState[] newArray(int size) {
   11127                         return new SavedState[size];
   11128                     }
   11129                 };
   11130     }
   11131     /**
   11132      * <p>Contains useful information about the current RecyclerView state like target scroll
   11133      * position or view focus. State object can also keep arbitrary data, identified by resource
   11134      * ids.</p>
   11135      * <p>Often times, RecyclerView components will need to pass information between each other.
   11136      * To provide a well defined data bus between components, RecyclerView passes the same State
   11137      * object to component callbacks and these components can use it to exchange data.</p>
   11138      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
   11139      * data between your components without needing to manage their lifecycles.</p>
   11140      */
   11141     public static class State {
   11142         static final int STEP_START = 1;
   11143         static final int STEP_LAYOUT = 1 << 1;
   11144         static final int STEP_ANIMATIONS = 1 << 2;
   11145 
   11146         void assertLayoutStep(int accepted) {
   11147             if ((accepted & mLayoutStep) == 0) {
   11148                 throw new IllegalStateException("Layout state should be one of "
   11149                         + Integer.toBinaryString(accepted) + " but it is "
   11150                         + Integer.toBinaryString(mLayoutStep));
   11151             }
   11152         }
   11153 
   11154 
   11155         /** Owned by SmoothScroller */
   11156         private int mTargetPosition = RecyclerView.NO_POSITION;
   11157 
   11158         private SparseArray<Object> mData;
   11159 
   11160         ////////////////////////////////////////////////////////////////////////////////////////////
   11161         // Fields below are carried from one layout pass to the next
   11162         ////////////////////////////////////////////////////////////////////////////////////////////
   11163 
   11164         /**
   11165          * Number of items adapter had in the previous layout.
   11166          */
   11167         int mPreviousLayoutItemCount = 0;
   11168 
   11169         /**
   11170          * Number of items that were NOT laid out but has been deleted from the adapter after the
   11171          * previous layout.
   11172          */
   11173         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
   11174 
   11175         ////////////////////////////////////////////////////////////////////////////////////////////
   11176         // Fields below must be updated or cleared before they are used (generally before a pass)
   11177         ////////////////////////////////////////////////////////////////////////////////////////////
   11178 
   11179         @IntDef(flag = true, value = {
   11180                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
   11181         })
   11182         @Retention(RetentionPolicy.SOURCE)
   11183         @interface LayoutState {}
   11184 
   11185         @LayoutState
   11186         int mLayoutStep = STEP_START;
   11187 
   11188         /**
   11189          * Number of items adapter has.
   11190          */
   11191         int mItemCount = 0;
   11192 
   11193         boolean mStructureChanged = false;
   11194 
   11195         boolean mInPreLayout = false;
   11196 
   11197         boolean mTrackOldChangeHolders = false;
   11198 
   11199         boolean mIsMeasuring = false;
   11200 
   11201         ////////////////////////////////////////////////////////////////////////////////////////////
   11202         // Fields below are always reset outside of the pass (or passes) that use them
   11203         ////////////////////////////////////////////////////////////////////////////////////////////
   11204 
   11205         boolean mRunSimpleAnimations = false;
   11206 
   11207         boolean mRunPredictiveAnimations = false;
   11208 
   11209         /**
   11210          * This data is saved before a layout calculation happens. After the layout is finished,
   11211          * if the previously focused view has been replaced with another view for the same item, we
   11212          * move the focus to the new item automatically.
   11213          */
   11214         int mFocusedItemPosition;
   11215         long mFocusedItemId;
   11216         // when a sub child has focus, record its id and see if we can directly request focus on
   11217         // that one instead
   11218         int mFocusedSubChildId;
   11219 
   11220         ////////////////////////////////////////////////////////////////////////////////////////////
   11221 
   11222         State reset() {
   11223             mTargetPosition = RecyclerView.NO_POSITION;
   11224             if (mData != null) {
   11225                 mData.clear();
   11226             }
   11227             mItemCount = 0;
   11228             mStructureChanged = false;
   11229             mIsMeasuring = false;
   11230             return this;
   11231         }
   11232 
   11233         /**
   11234          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
   11235          * prior to any layout passes.
   11236          *
   11237          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
   11238          * that Recycler#getViewForPosition() can function safely.</p>
   11239          */
   11240         void prepareForNestedPrefetch(Adapter adapter) {
   11241             mLayoutStep = STEP_START;
   11242             mItemCount = adapter.getItemCount();
   11243             mStructureChanged = false;
   11244             mInPreLayout = false;
   11245             mTrackOldChangeHolders = false;
   11246             mIsMeasuring = false;
   11247         }
   11248 
   11249         /**
   11250          * Returns true if the RecyclerView is currently measuring the layout. This value is
   11251          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
   11252          * has non-exact measurement specs.
   11253          * <p>
   11254          * Note that if the LayoutManager supports predictive animations and it is calculating the
   11255          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
   11256          * {@code onMeasure} call. This is because pre-layout means the previous state of the
   11257          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
   11258          * LayoutManager is always guaranteed to receive another call to
   11259          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
   11260          *
   11261          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
   11262          */
   11263         public boolean isMeasuring() {
   11264             return mIsMeasuring;
   11265         }
   11266 
   11267         /**
   11268          * Returns true if
   11269          * @return
   11270          */
   11271         public boolean isPreLayout() {
   11272             return mInPreLayout;
   11273         }
   11274 
   11275         /**
   11276          * Returns whether RecyclerView will run predictive animations in this layout pass
   11277          * or not.
   11278          *
   11279          * @return true if RecyclerView is calculating predictive animations to be run at the end
   11280          *         of the layout pass.
   11281          */
   11282         public boolean willRunPredictiveAnimations() {
   11283             return mRunPredictiveAnimations;
   11284         }
   11285 
   11286         /**
   11287          * Returns whether RecyclerView will run simple animations in this layout pass
   11288          * or not.
   11289          *
   11290          * @return true if RecyclerView is calculating simple animations to be run at the end of
   11291          *         the layout pass.
   11292          */
   11293         public boolean willRunSimpleAnimations() {
   11294             return mRunSimpleAnimations;
   11295         }
   11296 
   11297         /**
   11298          * Removes the mapping from the specified id, if there was any.
   11299          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
   11300          *                   preserve cross functionality and avoid conflicts.
   11301          */
   11302         public void remove(int resourceId) {
   11303             if (mData == null) {
   11304                 return;
   11305             }
   11306             mData.remove(resourceId);
   11307         }
   11308 
   11309         /**
   11310          * Gets the Object mapped from the specified id, or <code>null</code>
   11311          * if no such data exists.
   11312          *
   11313          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
   11314          *                   to
   11315          *                   preserve cross functionality and avoid conflicts.
   11316          */
   11317         public <T> T get(int resourceId) {
   11318             if (mData == null) {
   11319                 return null;
   11320             }
   11321             return (T) mData.get(resourceId);
   11322         }
   11323 
   11324         /**
   11325          * Adds a mapping from the specified id to the specified value, replacing the previous
   11326          * mapping from the specified key if there was one.
   11327          *
   11328          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
   11329          *                   preserve cross functionality and avoid conflicts.
   11330          * @param data       The data you want to associate with the resourceId.
   11331          */
   11332         public void put(int resourceId, Object data) {
   11333             if (mData == null) {
   11334                 mData = new SparseArray<Object>();
   11335             }
   11336             mData.put(resourceId, data);
   11337         }
   11338 
   11339         /**
   11340          * If scroll is triggered to make a certain item visible, this value will return the
   11341          * adapter index of that item.
   11342          * @return Adapter index of the target item or
   11343          * {@link RecyclerView#NO_POSITION} if there is no target
   11344          * position.
   11345          */
   11346         public int getTargetScrollPosition() {
   11347             return mTargetPosition;
   11348         }
   11349 
   11350         /**
   11351          * Returns if current scroll has a target position.
   11352          * @return true if scroll is being triggered to make a certain position visible
   11353          * @see #getTargetScrollPosition()
   11354          */
   11355         public boolean hasTargetScrollPosition() {
   11356             return mTargetPosition != RecyclerView.NO_POSITION;
   11357         }
   11358 
   11359         /**
   11360          * @return true if the structure of the data set has changed since the last call to
   11361          *         onLayoutChildren, false otherwise
   11362          */
   11363         public boolean didStructureChange() {
   11364             return mStructureChanged;
   11365         }
   11366 
   11367         /**
   11368          * Returns the total number of items that can be laid out. Note that this number is not
   11369          * necessarily equal to the number of items in the adapter, so you should always use this
   11370          * number for your position calculations and never access the adapter directly.
   11371          * <p>
   11372          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
   11373          * data changes on existing Views. These calculations are used to decide which animations
   11374          * should be run.
   11375          * <p>
   11376          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
   11377          * present the correct state to LayoutManager in pre-layout pass.
   11378          * <p>
   11379          * For example, a newly added item is not included in pre-layout item count because
   11380          * pre-layout reflects the contents of the adapter before the item is added. Behind the
   11381          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
   11382          * LayoutManager does not know about the new item's existence in pre-layout. The item will
   11383          * be available in second layout pass and will be included in the item count. Similar
   11384          * adjustments are made for moved and removed items as well.
   11385          * <p>
   11386          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
   11387          *
   11388          * @return The number of items currently available
   11389          * @see LayoutManager#getItemCount()
   11390          */
   11391         public int getItemCount() {
   11392             return mInPreLayout
   11393                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
   11394                     : mItemCount;
   11395         }
   11396 
   11397         @Override
   11398         public String toString() {
   11399             return "State{"
   11400                     + "mTargetPosition=" + mTargetPosition
   11401                     + ", mData=" + mData
   11402                     + ", mItemCount=" + mItemCount
   11403                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
   11404                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
   11405                     + mDeletedInvisibleItemCountSincePreviousLayout
   11406                     + ", mStructureChanged=" + mStructureChanged
   11407                     + ", mInPreLayout=" + mInPreLayout
   11408                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
   11409                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
   11410                     + '}';
   11411         }
   11412     }
   11413 
   11414     /**
   11415      * This class defines the behavior of fling if the developer wishes to handle it.
   11416      * <p>
   11417      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
   11418      *
   11419      * @see #setOnFlingListener(OnFlingListener)
   11420      */
   11421     public abstract static class OnFlingListener {
   11422 
   11423         /**
   11424          * Override this to handle a fling given the velocities in both x and y directions.
   11425          * Note that this method will only be called if the associated {@link LayoutManager}
   11426          * supports scrolling and the fling is not handled by nested scrolls first.
   11427          *
   11428          * @param velocityX the fling velocity on the X axis
   11429          * @param velocityY the fling velocity on the Y axis
   11430          *
   11431          * @return true if the fling washandled, false otherwise.
   11432          */
   11433         public abstract boolean onFling(int velocityX, int velocityY);
   11434     }
   11435 
   11436     /**
   11437      * Internal listener that manages items after animations finish. This is how items are
   11438      * retained (not recycled) during animations, but allowed to be recycled afterwards.
   11439      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
   11440      * method on the animator's listener when it is done animating any item.
   11441      */
   11442     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
   11443 
   11444         ItemAnimatorRestoreListener() {
   11445         }
   11446 
   11447         @Override
   11448         public void onAnimationFinished(ViewHolder item) {
   11449             item.setIsRecyclable(true);
   11450             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
   11451                 item.mShadowedHolder = null;
   11452             }
   11453             // always null this because an OldViewHolder can never become NewViewHolder w/o being
   11454             // recycled.
   11455             item.mShadowingHolder = null;
   11456             if (!item.shouldBeKeptAsChild()) {
   11457                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
   11458                     removeDetachedView(item.itemView, false);
   11459                 }
   11460             }
   11461         }
   11462     }
   11463 
   11464     /**
   11465      * This class defines the animations that take place on items as changes are made
   11466      * to the adapter.
   11467      *
   11468      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
   11469      * ViewHolder items. The RecyclerView will manage retaining these items while they
   11470      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
   11471      * when a ViewHolder's animation is finished. In other words, there must be a matching
   11472      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
   11473      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
   11474      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11475      * animateChange()}
   11476      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
   11477      * and
   11478      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11479      * animateDisappearance()} call.
   11480      *
   11481      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
   11482      *
   11483      * @see #setItemAnimator(ItemAnimator)
   11484      */
   11485     @SuppressWarnings("UnusedParameters")
   11486     public abstract static class ItemAnimator {
   11487 
   11488         /**
   11489          * The Item represented by this ViewHolder is updated.
   11490          * <p>
   11491          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11492          */
   11493         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
   11494 
   11495         /**
   11496          * The Item represented by this ViewHolder is removed from the adapter.
   11497          * <p>
   11498          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11499          */
   11500         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
   11501 
   11502         /**
   11503          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
   11504          * represented by this ViewHolder is invalid.
   11505          * <p>
   11506          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11507          */
   11508         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
   11509 
   11510         /**
   11511          * The position of the Item represented by this ViewHolder has been changed. This flag is
   11512          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
   11513          * any adapter change that may have a side effect on this item. (e.g. The item before this
   11514          * one has been removed from the Adapter).
   11515          * <p>
   11516          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11517          */
   11518         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
   11519 
   11520         /**
   11521          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
   11522          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
   11523          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
   11524          * to add new items in pre-layout to specify their virtual location when they are invisible
   11525          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
   11526          * <p>
   11527          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11528          */
   11529         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
   11530                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
   11531 
   11532         /**
   11533          * The set of flags that might be passed to
   11534          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11535          */
   11536         @IntDef(flag = true, value = {
   11537                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
   11538                 FLAG_APPEARED_IN_PRE_LAYOUT
   11539         })
   11540         @Retention(RetentionPolicy.SOURCE)
   11541         public @interface AdapterChanges {}
   11542         private ItemAnimatorListener mListener = null;
   11543         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
   11544                 new ArrayList<ItemAnimatorFinishedListener>();
   11545 
   11546         private long mAddDuration = 120;
   11547         private long mRemoveDuration = 120;
   11548         private long mMoveDuration = 250;
   11549         private long mChangeDuration = 250;
   11550 
   11551         /**
   11552          * Gets the current duration for which all move animations will run.
   11553          *
   11554          * @return The current move duration
   11555          */
   11556         public long getMoveDuration() {
   11557             return mMoveDuration;
   11558         }
   11559 
   11560         /**
   11561          * Sets the duration for which all move animations will run.
   11562          *
   11563          * @param moveDuration The move duration
   11564          */
   11565         public void setMoveDuration(long moveDuration) {
   11566             mMoveDuration = moveDuration;
   11567         }
   11568 
   11569         /**
   11570          * Gets the current duration for which all add animations will run.
   11571          *
   11572          * @return The current add duration
   11573          */
   11574         public long getAddDuration() {
   11575             return mAddDuration;
   11576         }
   11577 
   11578         /**
   11579          * Sets the duration for which all add animations will run.
   11580          *
   11581          * @param addDuration The add duration
   11582          */
   11583         public void setAddDuration(long addDuration) {
   11584             mAddDuration = addDuration;
   11585         }
   11586 
   11587         /**
   11588          * Gets the current duration for which all remove animations will run.
   11589          *
   11590          * @return The current remove duration
   11591          */
   11592         public long getRemoveDuration() {
   11593             return mRemoveDuration;
   11594         }
   11595 
   11596         /**
   11597          * Sets the duration for which all remove animations will run.
   11598          *
   11599          * @param removeDuration The remove duration
   11600          */
   11601         public void setRemoveDuration(long removeDuration) {
   11602             mRemoveDuration = removeDuration;
   11603         }
   11604 
   11605         /**
   11606          * Gets the current duration for which all change animations will run.
   11607          *
   11608          * @return The current change duration
   11609          */
   11610         public long getChangeDuration() {
   11611             return mChangeDuration;
   11612         }
   11613 
   11614         /**
   11615          * Sets the duration for which all change animations will run.
   11616          *
   11617          * @param changeDuration The change duration
   11618          */
   11619         public void setChangeDuration(long changeDuration) {
   11620             mChangeDuration = changeDuration;
   11621         }
   11622 
   11623         /**
   11624          * Internal only:
   11625          * Sets the listener that must be called when the animator is finished
   11626          * animating the item (or immediately if no animation happens). This is set
   11627          * internally and is not intended to be set by external code.
   11628          *
   11629          * @param listener The listener that must be called.
   11630          */
   11631         void setListener(ItemAnimatorListener listener) {
   11632             mListener = listener;
   11633         }
   11634 
   11635         /**
   11636          * Called by the RecyclerView before the layout begins. Item animator should record
   11637          * necessary information about the View before it is potentially rebound, moved or removed.
   11638          * <p>
   11639          * The data returned from this method will be passed to the related <code>animate**</code>
   11640          * methods.
   11641          * <p>
   11642          * Note that this method may be called after pre-layout phase if LayoutManager adds new
   11643          * Views to the layout in pre-layout pass.
   11644          * <p>
   11645          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
   11646          * the View and the adapter change flags.
   11647          *
   11648          * @param state       The current State of RecyclerView which includes some useful data
   11649          *                    about the layout that will be calculated.
   11650          * @param viewHolder  The ViewHolder whose information should be recorded.
   11651          * @param changeFlags Additional information about what changes happened in the Adapter
   11652          *                    about the Item represented by this ViewHolder. For instance, if
   11653          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
   11654          * @param payloads    The payload list that was previously passed to
   11655          *                    {@link Adapter#notifyItemChanged(int, Object)} or
   11656          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
   11657          *
   11658          * @return An ItemHolderInfo instance that preserves necessary information about the
   11659          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
   11660          * after layout is complete.
   11661          *
   11662          * @see #recordPostLayoutInformation(State, ViewHolder)
   11663          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11664          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11665          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11666          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11667          */
   11668         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
   11669                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
   11670                 @NonNull List<Object> payloads) {
   11671             return obtainHolderInfo().setFrom(viewHolder);
   11672         }
   11673 
   11674         /**
   11675          * Called by the RecyclerView after the layout is complete. Item animator should record
   11676          * necessary information about the View's final state.
   11677          * <p>
   11678          * The data returned from this method will be passed to the related <code>animate**</code>
   11679          * methods.
   11680          * <p>
   11681          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
   11682          * the View.
   11683          *
   11684          * @param state      The current State of RecyclerView which includes some useful data about
   11685          *                   the layout that will be calculated.
   11686          * @param viewHolder The ViewHolder whose information should be recorded.
   11687          *
   11688          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
   11689          * This object will be passed back to related <code>animate**</code> methods when
   11690          * RecyclerView decides how items should be animated.
   11691          *
   11692          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   11693          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11694          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11695          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11696          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11697          */
   11698         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
   11699                 @NonNull ViewHolder viewHolder) {
   11700             return obtainHolderInfo().setFrom(viewHolder);
   11701         }
   11702 
   11703         /**
   11704          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
   11705          * <p>
   11706          * This means that the View was a child of the LayoutManager when layout started but has
   11707          * been removed by the LayoutManager. It might have been removed from the adapter or simply
   11708          * become invisible due to other factors. You can distinguish these two cases by checking
   11709          * the change flags that were passed to
   11710          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11711          * <p>
   11712          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
   11713          * animation callback method which will be called by the RecyclerView depends on the
   11714          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
   11715          * LayoutManager's decision whether to layout the changed version of a disappearing
   11716          * ViewHolder or not. RecyclerView will call
   11717          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11718          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
   11719          * returns {@code false} from
   11720          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
   11721          * LayoutManager lays out a new disappearing view that holds the updated information.
   11722          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
   11723          * <p>
   11724          * If LayoutManager supports predictive animations, it might provide a target disappear
   11725          * location for the View by laying it out in that location. When that happens,
   11726          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
   11727          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
   11728          * <p>
   11729          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   11730          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   11731          * decides not to animate the view).
   11732          *
   11733          * @param viewHolder    The ViewHolder which should be animated
   11734          * @param preLayoutInfo The information that was returned from
   11735          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11736          * @param postLayoutInfo The information that was returned from
   11737          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
   11738          *                       null if the LayoutManager did not layout the item.
   11739          *
   11740          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11741          * false otherwise.
   11742          */
   11743         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
   11744                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
   11745 
   11746         /**
   11747          * Called by the RecyclerView when a ViewHolder is added to the layout.
   11748          * <p>
   11749          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
   11750          * but has  been added by the LayoutManager. It might be newly added to the adapter or
   11751          * simply become visible due to other factors.
   11752          * <p>
   11753          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   11754          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   11755          * decides not to animate the view).
   11756          *
   11757          * @param viewHolder     The ViewHolder which should be animated
   11758          * @param preLayoutInfo  The information that was returned from
   11759          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11760          *                       Might be null if Item was just added to the adapter or
   11761          *                       LayoutManager does not support predictive animations or it could
   11762          *                       not predict that this ViewHolder will become visible.
   11763          * @param postLayoutInfo The information that was returned from {@link
   11764          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11765          *
   11766          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11767          * false otherwise.
   11768          */
   11769         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
   11770                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   11771 
   11772         /**
   11773          * Called by the RecyclerView when a ViewHolder is present in both before and after the
   11774          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
   11775          * for it or a {@link Adapter#notifyDataSetChanged()} call.
   11776          * <p>
   11777          * This ViewHolder still represents the same data that it was representing when the layout
   11778          * started but its position / size may be changed by the LayoutManager.
   11779          * <p>
   11780          * If the Item's layout position didn't change, RecyclerView still calls this method because
   11781          * it does not track this information (or does not necessarily know that an animation is
   11782          * not required). Your ItemAnimator should handle this case and if there is nothing to
   11783          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
   11784          * <code>false</code>.
   11785          * <p>
   11786          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   11787          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   11788          * decides not to animate the view).
   11789          *
   11790          * @param viewHolder     The ViewHolder which should be animated
   11791          * @param preLayoutInfo  The information that was returned from
   11792          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11793          * @param postLayoutInfo The information that was returned from {@link
   11794          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11795          *
   11796          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11797          * false otherwise.
   11798          */
   11799         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
   11800                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   11801 
   11802         /**
   11803          * Called by the RecyclerView when an adapter item is present both before and after the
   11804          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
   11805          * for it. This method may also be called when
   11806          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
   11807          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
   11808          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
   11809          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
   11810          * called for the new ViewHolder and the old one will be recycled.
   11811          * <p>
   11812          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
   11813          * a good possibility that item contents didn't really change but it is rebound from the
   11814          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
   11815          * screen didn't change and your animator should handle this case as well and avoid creating
   11816          * unnecessary animations.
   11817          * <p>
   11818          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
   11819          * previous presentation of the item as-is and supply a new ViewHolder for the updated
   11820          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
   11821          * This is useful if you don't know the contents of the Item and would like
   11822          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
   11823          * <p>
   11824          * When you are writing a custom item animator for your layout, it might be more performant
   11825          * and elegant to re-use the same ViewHolder and animate the content changes manually.
   11826          * <p>
   11827          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
   11828          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
   11829          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
   11830          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
   11831          * which represent the same Item. In that case, only the new ViewHolder is visible
   11832          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
   11833          * <p>
   11834          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
   11835          * ViewHolder when their animation is complete
   11836          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
   11837          * animate the view).
   11838          * <p>
   11839          *  If oldHolder and newHolder are the same instance, you should call
   11840          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
   11841          * <p>
   11842          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
   11843          * animation callback method which will be called by the RecyclerView depends on the
   11844          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
   11845          * LayoutManager's decision whether to layout the changed version of a disappearing
   11846          * ViewHolder or not. RecyclerView will call
   11847          * {@code animateChange} instead of
   11848          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11849          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
   11850          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
   11851          * LayoutManager lays out a new disappearing view that holds the updated information.
   11852          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
   11853          *
   11854          * @param oldHolder     The ViewHolder before the layout is started, might be the same
   11855          *                      instance with newHolder.
   11856          * @param newHolder     The ViewHolder after the layout is finished, might be the same
   11857          *                      instance with oldHolder.
   11858          * @param preLayoutInfo  The information that was returned from
   11859          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11860          * @param postLayoutInfo The information that was returned from {@link
   11861          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   11862          *
   11863          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   11864          * false otherwise.
   11865          */
   11866         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
   11867                 @NonNull ViewHolder newHolder,
   11868                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   11869 
   11870         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
   11871             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
   11872             if (viewHolder.isInvalid()) {
   11873                 return FLAG_INVALIDATED;
   11874             }
   11875             if ((flags & FLAG_INVALIDATED) == 0) {
   11876                 final int oldPos = viewHolder.getOldPosition();
   11877                 final int pos = viewHolder.getAdapterPosition();
   11878                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
   11879                     flags |= FLAG_MOVED;
   11880                 }
   11881             }
   11882             return flags;
   11883         }
   11884 
   11885         /**
   11886          * Called when there are pending animations waiting to be started. This state
   11887          * is governed by the return values from
   11888          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11889          * animateAppearance()},
   11890          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11891          * animateChange()}
   11892          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11893          * animatePersistence()}, and
   11894          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11895          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
   11896          * called later to start the associated animations. runPendingAnimations() will be scheduled
   11897          * to be run on the next frame.
   11898          */
   11899         public abstract void runPendingAnimations();
   11900 
   11901         /**
   11902          * Method called when an animation on a view should be ended immediately.
   11903          * This could happen when other events, like scrolling, occur, so that
   11904          * animating views can be quickly put into their proper end locations.
   11905          * Implementations should ensure that any animations running on the item
   11906          * are canceled and affected properties are set to their end values.
   11907          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
   11908          * animation since the animations are effectively done when this method is called.
   11909          *
   11910          * @param item The item for which an animation should be stopped.
   11911          */
   11912         public abstract void endAnimation(ViewHolder item);
   11913 
   11914         /**
   11915          * Method called when all item animations should be ended immediately.
   11916          * This could happen when other events, like scrolling, occur, so that
   11917          * animating views can be quickly put into their proper end locations.
   11918          * Implementations should ensure that any animations running on any items
   11919          * are canceled and affected properties are set to their end values.
   11920          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
   11921          * animation since the animations are effectively done when this method is called.
   11922          */
   11923         public abstract void endAnimations();
   11924 
   11925         /**
   11926          * Method which returns whether there are any item animations currently running.
   11927          * This method can be used to determine whether to delay other actions until
   11928          * animations end.
   11929          *
   11930          * @return true if there are any item animations currently running, false otherwise.
   11931          */
   11932         public abstract boolean isRunning();
   11933 
   11934         /**
   11935          * Method to be called by subclasses when an animation is finished.
   11936          * <p>
   11937          * For each call RecyclerView makes to
   11938          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11939          * animateAppearance()},
   11940          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11941          * animatePersistence()}, or
   11942          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11943          * animateDisappearance()}, there
   11944          * should
   11945          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
   11946          * <p>
   11947          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11948          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
   11949          * and <code>newHolder</code>  (if they are not the same instance).
   11950          *
   11951          * @param viewHolder The ViewHolder whose animation is finished.
   11952          * @see #onAnimationFinished(ViewHolder)
   11953          */
   11954         public final void dispatchAnimationFinished(ViewHolder viewHolder) {
   11955             onAnimationFinished(viewHolder);
   11956             if (mListener != null) {
   11957                 mListener.onAnimationFinished(viewHolder);
   11958             }
   11959         }
   11960 
   11961         /**
   11962          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
   11963          * ItemAnimator.
   11964          *
   11965          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
   11966          *                   animations running on this ViewHolder.
   11967          * @see #dispatchAnimationFinished(ViewHolder)
   11968          */
   11969         public void onAnimationFinished(ViewHolder viewHolder) {
   11970         }
   11971 
   11972         /**
   11973          * Method to be called by subclasses when an animation is started.
   11974          * <p>
   11975          * For each call RecyclerView makes to
   11976          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11977          * animateAppearance()},
   11978          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11979          * animatePersistence()}, or
   11980          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11981          * animateDisappearance()}, there should be a matching
   11982          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
   11983          * <p>
   11984          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   11985          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
   11986          * and <code>newHolder</code> (if they are not the same instance).
   11987          * <p>
   11988          * If your ItemAnimator decides not to animate a ViewHolder, it should call
   11989          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
   11990          * {@link #dispatchAnimationStarted(ViewHolder)}.
   11991          *
   11992          * @param viewHolder The ViewHolder whose animation is starting.
   11993          * @see #onAnimationStarted(ViewHolder)
   11994          */
   11995         public final void dispatchAnimationStarted(ViewHolder viewHolder) {
   11996             onAnimationStarted(viewHolder);
   11997         }
   11998 
   11999         /**
   12000          * Called when a new animation is started on the given ViewHolder.
   12001          *
   12002          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
   12003          *                   might already be animating and this might be another animation.
   12004          * @see #dispatchAnimationStarted(ViewHolder)
   12005          */
   12006         public void onAnimationStarted(ViewHolder viewHolder) {
   12007 
   12008         }
   12009 
   12010         /**
   12011          * Like {@link #isRunning()}, this method returns whether there are any item
   12012          * animations currently running. Additionally, the listener passed in will be called
   12013          * when there are no item animations running, either immediately (before the method
   12014          * returns) if no animations are currently running, or when the currently running
   12015          * animations are {@link #dispatchAnimationsFinished() finished}.
   12016          *
   12017          * <p>Note that the listener is transient - it is either called immediately and not
   12018          * stored at all, or stored only until it is called when running animations
   12019          * are finished sometime later.</p>
   12020          *
   12021          * @param listener A listener to be called immediately if no animations are running
   12022          * or later when currently-running animations have finished. A null listener is
   12023          * equivalent to calling {@link #isRunning()}.
   12024          * @return true if there are any item animations currently running, false otherwise.
   12025          */
   12026         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
   12027             boolean running = isRunning();
   12028             if (listener != null) {
   12029                 if (!running) {
   12030                     listener.onAnimationsFinished();
   12031                 } else {
   12032                     mFinishedListeners.add(listener);
   12033                 }
   12034             }
   12035             return running;
   12036         }
   12037 
   12038         /**
   12039          * When an item is changed, ItemAnimator can decide whether it wants to re-use
   12040          * the same ViewHolder for animations or RecyclerView should create a copy of the
   12041          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
   12042          * <p>
   12043          * Note that this method will only be called if the {@link ViewHolder} still has the same
   12044          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
   12045          * both {@link ViewHolder}s in the
   12046          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
   12047          * <p>
   12048          * If your application is using change payloads, you can override
   12049          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
   12050          *
   12051          * @param viewHolder The ViewHolder which represents the changed item's old content.
   12052          *
   12053          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
   12054          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
   12055          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
   12056          *
   12057          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
   12058          */
   12059         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
   12060             return true;
   12061         }
   12062 
   12063         /**
   12064          * When an item is changed, ItemAnimator can decide whether it wants to re-use
   12065          * the same ViewHolder for animations or RecyclerView should create a copy of the
   12066          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
   12067          * <p>
   12068          * Note that this method will only be called if the {@link ViewHolder} still has the same
   12069          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
   12070          * both {@link ViewHolder}s in the
   12071          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
   12072          *
   12073          * @param viewHolder The ViewHolder which represents the changed item's old content.
   12074          * @param payloads A non-null list of merged payloads that were sent with change
   12075          *                 notifications. Can be empty if the adapter is invalidated via
   12076          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
   12077          *                 payloads will be passed into
   12078          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
   12079          *                 method <b>if</b> this method returns <code>true</code>.
   12080          *
   12081          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
   12082          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
   12083          *         ItemAnimator to animate. Default implementation calls
   12084          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
   12085          *
   12086          * @see #canReuseUpdatedViewHolder(ViewHolder)
   12087          */
   12088         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
   12089                 @NonNull List<Object> payloads) {
   12090             return canReuseUpdatedViewHolder(viewHolder);
   12091         }
   12092 
   12093         /**
   12094          * This method should be called by ItemAnimator implementations to notify
   12095          * any listeners that all pending and active item animations are finished.
   12096          */
   12097         public final void dispatchAnimationsFinished() {
   12098             final int count = mFinishedListeners.size();
   12099             for (int i = 0; i < count; ++i) {
   12100                 mFinishedListeners.get(i).onAnimationsFinished();
   12101             }
   12102             mFinishedListeners.clear();
   12103         }
   12104 
   12105         /**
   12106          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
   12107          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
   12108          * <p>
   12109          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
   12110          * your own instances.
   12111          *
   12112          * @return A new {@link ItemHolderInfo}.
   12113          */
   12114         public ItemHolderInfo obtainHolderInfo() {
   12115             return new ItemHolderInfo();
   12116         }
   12117 
   12118         /**
   12119          * The interface to be implemented by listeners to animation events from this
   12120          * ItemAnimator. This is used internally and is not intended for developers to
   12121          * create directly.
   12122          */
   12123         interface ItemAnimatorListener {
   12124             void onAnimationFinished(ViewHolder item);
   12125         }
   12126 
   12127         /**
   12128          * This interface is used to inform listeners when all pending or running animations
   12129          * in an ItemAnimator are finished. This can be used, for example, to delay an action
   12130          * in a data set until currently-running animations are complete.
   12131          *
   12132          * @see #isRunning(ItemAnimatorFinishedListener)
   12133          */
   12134         public interface ItemAnimatorFinishedListener {
   12135             /**
   12136              * Notifies when all pending or running animations in an ItemAnimator are finished.
   12137              */
   12138             void onAnimationsFinished();
   12139         }
   12140 
   12141         /**
   12142          * A simple data structure that holds information about an item's bounds.
   12143          * This information is used in calculating item animations. Default implementation of
   12144          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
   12145          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
   12146          * structure. You can extend this class if you would like to keep more information about
   12147          * the Views.
   12148          * <p>
   12149          * If you want to provide your own implementation but still use `super` methods to record
   12150          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
   12151          * instances.
   12152          */
   12153         public static class ItemHolderInfo {
   12154 
   12155             /**
   12156              * The left edge of the View (excluding decorations)
   12157              */
   12158             public int left;
   12159 
   12160             /**
   12161              * The top edge of the View (excluding decorations)
   12162              */
   12163             public int top;
   12164 
   12165             /**
   12166              * The right edge of the View (excluding decorations)
   12167              */
   12168             public int right;
   12169 
   12170             /**
   12171              * The bottom edge of the View (excluding decorations)
   12172              */
   12173             public int bottom;
   12174 
   12175             /**
   12176              * The change flags that were passed to
   12177              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
   12178              */
   12179             @AdapterChanges
   12180             public int changeFlags;
   12181 
   12182             public ItemHolderInfo() {
   12183             }
   12184 
   12185             /**
   12186              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
   12187              * the given ViewHolder. Clears all {@link #changeFlags}.
   12188              *
   12189              * @param holder The ViewHolder whose bounds should be copied.
   12190              * @return This {@link ItemHolderInfo}
   12191              */
   12192             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder) {
   12193                 return setFrom(holder, 0);
   12194             }
   12195 
   12196             /**
   12197              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
   12198              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
   12199              *
   12200              * @param holder The ViewHolder whose bounds should be copied.
   12201              * @param flags  The adapter change flags that were passed into
   12202              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
   12203              *               List)}.
   12204              * @return This {@link ItemHolderInfo}
   12205              */
   12206             public ItemHolderInfo setFrom(RecyclerView.ViewHolder holder,
   12207                     @AdapterChanges int flags) {
   12208                 final View view = holder.itemView;
   12209                 this.left = view.getLeft();
   12210                 this.top = view.getTop();
   12211                 this.right = view.getRight();
   12212                 this.bottom = view.getBottom();
   12213                 return this;
   12214             }
   12215         }
   12216     }
   12217 
   12218     @Override
   12219     protected int getChildDrawingOrder(int childCount, int i) {
   12220         if (mChildDrawingOrderCallback == null) {
   12221             return super.getChildDrawingOrder(childCount, i);
   12222         } else {
   12223             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
   12224         }
   12225     }
   12226 
   12227     /**
   12228      * A callback interface that can be used to alter the drawing order of RecyclerView children.
   12229      * <p>
   12230      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
   12231      * that applies to that method also applies to this callback. For example, changing the drawing
   12232      * order of two views will not have any effect if their elevation values are different since
   12233      * elevation overrides the result of this callback.
   12234      */
   12235     public interface ChildDrawingOrderCallback {
   12236         /**
   12237          * Returns the index of the child to draw for this iteration. Override this
   12238          * if you want to change the drawing order of children. By default, it
   12239          * returns i.
   12240          *
   12241          * @param i The current iteration.
   12242          * @return The index of the child to draw this iteration.
   12243          *
   12244          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
   12245          */
   12246         int onGetChildDrawingOrder(int childCount, int i);
   12247     }
   12248 }
   12249