Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 package androidx.recyclerview.widget;
     19 
     20 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     21 import static androidx.core.view.ViewCompat.TYPE_NON_TOUCH;
     22 import static androidx.core.view.ViewCompat.TYPE_TOUCH;
     23 
     24 import android.annotation.SuppressLint;
     25 import android.content.Context;
     26 import android.content.res.Resources;
     27 import android.content.res.TypedArray;
     28 import android.database.Observable;
     29 import android.graphics.Canvas;
     30 import android.graphics.Matrix;
     31 import android.graphics.PointF;
     32 import android.graphics.Rect;
     33 import android.graphics.RectF;
     34 import android.graphics.drawable.Drawable;
     35 import android.graphics.drawable.StateListDrawable;
     36 import android.os.Build;
     37 import android.os.Bundle;
     38 import android.os.Parcel;
     39 import android.os.Parcelable;
     40 import android.os.SystemClock;
     41 import android.util.AttributeSet;
     42 import android.util.Log;
     43 import android.util.SparseArray;
     44 import android.view.Display;
     45 import android.view.FocusFinder;
     46 import android.view.InputDevice;
     47 import android.view.MotionEvent;
     48 import android.view.VelocityTracker;
     49 import android.view.View;
     50 import android.view.ViewConfiguration;
     51 import android.view.ViewGroup;
     52 import android.view.ViewParent;
     53 import android.view.accessibility.AccessibilityEvent;
     54 import android.view.accessibility.AccessibilityManager;
     55 import android.view.animation.Interpolator;
     56 import android.widget.EdgeEffect;
     57 import android.widget.LinearLayout;
     58 import android.widget.OverScroller;
     59 
     60 import androidx.annotation.CallSuper;
     61 import androidx.annotation.IntDef;
     62 import androidx.annotation.NonNull;
     63 import androidx.annotation.Nullable;
     64 import androidx.annotation.Px;
     65 import androidx.annotation.RestrictTo;
     66 import androidx.annotation.VisibleForTesting;
     67 import androidx.core.os.TraceCompat;
     68 import androidx.core.util.Preconditions;
     69 import androidx.core.view.InputDeviceCompat;
     70 import androidx.core.view.MotionEventCompat;
     71 import androidx.core.view.NestedScrollingChild2;
     72 import androidx.core.view.NestedScrollingChildHelper;
     73 import androidx.core.view.ScrollingView;
     74 import androidx.core.view.ViewCompat;
     75 import androidx.core.view.ViewConfigurationCompat;
     76 import androidx.core.view.accessibility.AccessibilityEventCompat;
     77 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
     78 import androidx.core.widget.EdgeEffectCompat;
     79 import androidx.customview.view.AbsSavedState;
     80 import androidx.recyclerview.R;
     81 import androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
     82 import androidx.viewpager.widget.ViewPager;
     83 
     84 import java.lang.annotation.Retention;
     85 import java.lang.annotation.RetentionPolicy;
     86 import java.lang.ref.WeakReference;
     87 import java.lang.reflect.Constructor;
     88 import java.lang.reflect.InvocationTargetException;
     89 import java.util.ArrayList;
     90 import java.util.Collections;
     91 import java.util.List;
     92 
     93 
     94 /**
     95  * A flexible view for providing a limited window into a large data set.
     96  *
     97  * <h3>Glossary of terms:</h3>
     98  *
     99  * <ul>
    100  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
    101  *     that represent items in a data set.</li>
    102  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
    103  *     <li><em>Index:</em> The index of an attached child view as used in a call to
    104  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
    105  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
    106  *     to a <em>position</em> within the adapter.</li>
    107  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
    108  *     position may be placed in a cache for later reuse to display the same type of data again
    109  *     later. This can drastically improve performance by skipping initial layout inflation
    110  *     or construction.</li>
    111  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
    112  *     state during layout. Scrap views may be reused without becoming fully detached
    113  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
    114  *     by the adapter if the view was considered <em>dirty</em>.</li>
    115  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
    116  *     being displayed.</li>
    117  * </ul>
    118  *
    119  * <h4>Positions in RecyclerView:</h4>
    120  * <p>
    121  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
    122  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
    123  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
    124  * It also helps with performance because all view bindings happen at the same time and unnecessary
    125  * bindings are avoided.
    126  * <p>
    127  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
    128  * <ul>
    129  *     <li>layout position: Position of an item in the latest layout calculation. This is the
    130  *     position from the LayoutManager's perspective.</li>
    131  *     <li>adapter position: Position of an item in the adapter. This is the position from
    132  *     the Adapter's perspective.</li>
    133  * </ul>
    134  * <p>
    135  * These two positions are the same except the time between dispatching <code>adapter.notify*
    136  * </code> events and calculating the updated layout.
    137  * <p>
    138  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
    139  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
    140  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
    141  * last layout calculation. You can rely on these positions to be consistent with what user is
    142  * currently seeing on the screen. For example, if you have a list of items on the screen and user
    143  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
    144  * is seeing.
    145  * <p>
    146  * The other set of position related methods are in the form of
    147  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
    148  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
    149  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
    150  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
    151  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
    152  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
    153  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
    154  * <code>null</code> results from these methods.
    155  * <p>
    156  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
    157  * writing an {@link Adapter}, you probably want to use adapter positions.
    158  *
    159  * @attr ref androidx.recyclerview.R.styleable#RecyclerView_layoutManager
    160  */
    161 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
    162 
    163     static final String TAG = "RecyclerView";
    164 
    165     static final boolean DEBUG = false;
    166 
    167     static final boolean VERBOSE_TRACING = false;
    168 
    169     private static final int[]  NESTED_SCROLLING_ATTRS =
    170             {16843830 /* android.R.attr.nestedScrollingEnabled */};
    171 
    172     private static final int[] CLIP_TO_PADDING_ATTR = {android.R.attr.clipToPadding};
    173 
    174     /**
    175      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
    176      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
    177      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
    178      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
    179      * this criteria.
    180      */
    181     static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
    182             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
    183     /**
    184      * On M+, an unspecified measure spec may include a hint which we can use. On older platforms,
    185      * this value might be garbage. To save LayoutManagers from it, RecyclerView sets the size to
    186      * 0 when mode is unspecified.
    187      */
    188     static final boolean ALLOW_SIZE_IN_UNSPECIFIED_SPEC = Build.VERSION.SDK_INT >= 23;
    189 
    190     static final boolean POST_UPDATES_ON_ANIMATION = Build.VERSION.SDK_INT >= 16;
    191 
    192     /**
    193      * On L+, with RenderThread, the UI thread has idle time after it has passed a frame off to
    194      * RenderThread but before the next frame begins. We schedule prefetch work in this window.
    195      */
    196     private static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
    197 
    198     /**
    199      * FocusFinder#findNextFocus is broken on ICS MR1 and older for View.FOCUS_BACKWARD direction.
    200      * We convert it to an absolute direction such as FOCUS_DOWN or FOCUS_LEFT.
    201      */
    202     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
    203 
    204     /**
    205      * on API 15-, a focused child can still be considered a focused child of RV even after
    206      * it's being removed or its focusable flag is set to false. This is because when this focused
    207      * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
    208      * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
    209      * to request focus on a new child, which will clear the focus on the old (detached) child as a
    210      * side-effect.
    211      */
    212     private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
    213 
    214     static final boolean DISPATCH_TEMP_DETACH = false;
    215 
    216     /** @hide */
    217     @RestrictTo(LIBRARY_GROUP)
    218     @IntDef({HORIZONTAL, VERTICAL})
    219     @Retention(RetentionPolicy.SOURCE)
    220     public @interface Orientation {}
    221 
    222     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    223     public static final int VERTICAL = LinearLayout.VERTICAL;
    224 
    225     static final int DEFAULT_ORIENTATION = VERTICAL;
    226     public static final int NO_POSITION = -1;
    227     public static final long NO_ID = -1;
    228     public static final int INVALID_TYPE = -1;
    229 
    230     /**
    231      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    232      * that the RecyclerView should use the standard touch slop for smooth,
    233      * continuous scrolling.
    234      */
    235     public static final int TOUCH_SLOP_DEFAULT = 0;
    236 
    237     /**
    238      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    239      * that the RecyclerView should use the standard touch slop for scrolling
    240      * widgets that snap to a page or other coarse-grained barrier.
    241      */
    242     public static final int TOUCH_SLOP_PAGING = 1;
    243 
    244     static final int MAX_SCROLL_DURATION = 2000;
    245 
    246     /**
    247      * RecyclerView is calculating a scroll.
    248      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
    249      * it. Try to avoid using EditText, focusable views or handle them with care.
    250      */
    251     static final String TRACE_SCROLL_TAG = "RV Scroll";
    252 
    253     /**
    254      * OnLayout has been called by the View system.
    255      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
    256      * update themselves directly. This will cause a full re-layout but when it happens via the
    257      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
    258      */
    259     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
    260 
    261     /**
    262      * NotifyDataSetChanged or equal has been called.
    263      * If this is taking a long time, try sending granular notify adapter changes instead of just
    264      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
    265      * might help.
    266      */
    267     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
    268 
    269     /**
    270      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
    271      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
    272      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
    273      * methods.
    274      */
    275     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
    276 
    277     /**
    278      * RecyclerView is rebinding a View.
    279      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
    280      * doing extra operations in onBindViewHolder call.
    281      */
    282     static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
    283 
    284     /**
    285      * RecyclerView is attempting to pre-populate off screen views.
    286      */
    287     static final String TRACE_PREFETCH_TAG = "RV Prefetch";
    288 
    289     /**
    290      * RecyclerView is attempting to pre-populate off screen itemviews within an off screen
    291      * RecyclerView.
    292      */
    293     static final String TRACE_NESTED_PREFETCH_TAG = "RV Nested Prefetch";
    294 
    295     /**
    296      * RecyclerView is creating a new View.
    297      * If too many of these present in Systrace:
    298      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
    299      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
    300      * > Adapter#onFailedToRecycleView(ViewHolder)})
    301      *
    302      * - There might be too many item view types.
    303      * > Try merging them
    304      *
    305      * - There might be too many itemChange animations and not enough space in RecyclerPool.
    306      * >Try increasing your pool size and item cache size.
    307      */
    308     static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
    309     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
    310             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
    311 
    312     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
    313 
    314     final Recycler mRecycler = new Recycler();
    315 
    316     private SavedState mPendingSavedState;
    317 
    318     /**
    319      * Handles adapter updates
    320      */
    321     AdapterHelper mAdapterHelper;
    322 
    323     /**
    324      * Handles abstraction between LayoutManager children and RecyclerView children
    325      */
    326     ChildHelper mChildHelper;
    327 
    328     /**
    329      * Keeps data about views to be used for animations
    330      */
    331     final ViewInfoStore mViewInfoStore = new ViewInfoStore();
    332 
    333     /**
    334      * Prior to L, there is no way to query this variable which is why we override the setter and
    335      * track it here.
    336      */
    337     boolean mClipToPadding;
    338 
    339     /**
    340      * Note: this Runnable is only ever posted if:
    341      * 1) We've been through first layout
    342      * 2) We know we have a fixed size (mHasFixedSize)
    343      * 3) We're attached
    344      */
    345     final Runnable mUpdateChildViewsRunnable = new Runnable() {
    346         @Override
    347         public void run() {
    348             if (!mFirstLayoutComplete || isLayoutRequested()) {
    349                 // a layout request will happen, we should not do layout here.
    350                 return;
    351             }
    352             if (!mIsAttached) {
    353                 requestLayout();
    354                 // if we are not attached yet, mark us as requiring layout and skip
    355                 return;
    356             }
    357             if (mLayoutFrozen) {
    358                 mLayoutWasDefered = true;
    359                 return; //we'll process updates when ice age ends.
    360             }
    361             consumePendingUpdateOperations();
    362         }
    363     };
    364 
    365     final Rect mTempRect = new Rect();
    366     private final Rect mTempRect2 = new Rect();
    367     final RectF mTempRectF = new RectF();
    368     Adapter mAdapter;
    369     @VisibleForTesting LayoutManager mLayout;
    370     RecyclerListener mRecyclerListener;
    371     final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<>();
    372     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
    373             new ArrayList<>();
    374     private OnItemTouchListener mActiveOnItemTouchListener;
    375     boolean mIsAttached;
    376     boolean mHasFixedSize;
    377     boolean mEnableFastScroller;
    378     @VisibleForTesting boolean mFirstLayoutComplete;
    379 
    380     /**
    381      * The current depth of nested calls to {@link #startInterceptRequestLayout()} (number of
    382      * calls to {@link #startInterceptRequestLayout()} - number of calls to
    383      * {@link #stopInterceptRequestLayout(boolean)} .  This is used to signal whether we
    384      * should defer layout operations caused by layout requests from children of
    385      * {@link RecyclerView}.
    386      */
    387     private int mInterceptRequestLayoutDepth = 0;
    388 
    389     /**
    390      * True if a call to requestLayout was intercepted and prevented from executing like normal and
    391      * we plan on continuing with normal execution later.
    392      */
    393     boolean mLayoutWasDefered;
    394 
    395     boolean mLayoutFrozen;
    396     private boolean mIgnoreMotionEventTillDown;
    397 
    398     // binary OR of change events that were eaten during a layout or scroll.
    399     private int mEatenAccessibilityChangeFlags;
    400     boolean mAdapterUpdateDuringMeasure;
    401 
    402     private final AccessibilityManager mAccessibilityManager;
    403     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
    404 
    405     /**
    406      * True after an event occurs that signals that the entire data set has changed. In that case,
    407      * we cannot run any animations since we don't know what happened until layout.
    408      *
    409      * Attached items are invalid until next layout, at which point layout will animate/replace
    410      * items as necessary, building up content from the (effectively) new adapter from scratch.
    411      *
    412      * Cached items must be discarded when setting this to true, so that the cache may be freely
    413      * used by prefetching until the next layout occurs.
    414      *
    415      * @see #processDataSetCompletelyChanged(boolean)
    416      */
    417     boolean mDataSetHasChangedAfterLayout = false;
    418 
    419     /**
    420      * True after the data set has completely changed and
    421      * {@link LayoutManager#onItemsChanged(RecyclerView)} should be called during the subsequent
    422      * measure/layout.
    423      *
    424      * @see #processDataSetCompletelyChanged(boolean)
    425      */
    426     boolean mDispatchItemsChangedEvent = false;
    427 
    428     /**
    429      * This variable is incremented during a dispatchLayout and/or scroll.
    430      * Some methods should not be called during these periods (e.g. adapter data change).
    431      * Doing so will create hard to find bugs so we better check it and throw an exception.
    432      *
    433      * @see #assertInLayoutOrScroll(String)
    434      * @see #assertNotInLayoutOrScroll(String)
    435      */
    436     private int mLayoutOrScrollCounter = 0;
    437 
    438     /**
    439      * Similar to mLayoutOrScrollCounter but logs a warning instead of throwing an exception
    440      * (for API compatibility).
    441      * <p>
    442      * It is a bad practice for a developer to update the data in a scroll callback since it is
    443      * potentially called during a layout.
    444      */
    445     private int mDispatchScrollCounter = 0;
    446 
    447     @NonNull
    448     private EdgeEffectFactory mEdgeEffectFactory = new EdgeEffectFactory();
    449     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
    450 
    451     ItemAnimator mItemAnimator = new DefaultItemAnimator();
    452 
    453     private static final int INVALID_POINTER = -1;
    454 
    455     /**
    456      * The RecyclerView is not currently scrolling.
    457      * @see #getScrollState()
    458      */
    459     public static final int SCROLL_STATE_IDLE = 0;
    460 
    461     /**
    462      * The RecyclerView is currently being dragged by outside input such as user touch input.
    463      * @see #getScrollState()
    464      */
    465     public static final int SCROLL_STATE_DRAGGING = 1;
    466 
    467     /**
    468      * The RecyclerView is currently animating to a final position while not under
    469      * outside control.
    470      * @see #getScrollState()
    471      */
    472     public static final int SCROLL_STATE_SETTLING = 2;
    473 
    474     static final long FOREVER_NS = Long.MAX_VALUE;
    475 
    476     // Touch/scrolling handling
    477 
    478     private int mScrollState = SCROLL_STATE_IDLE;
    479     private int mScrollPointerId = INVALID_POINTER;
    480     private VelocityTracker mVelocityTracker;
    481     private int mInitialTouchX;
    482     private int mInitialTouchY;
    483     private int mLastTouchX;
    484     private int mLastTouchY;
    485     private int mTouchSlop;
    486     private OnFlingListener mOnFlingListener;
    487     private final int mMinFlingVelocity;
    488     private final int mMaxFlingVelocity;
    489 
    490     // This value is used when handling rotary encoder generic motion events.
    491     private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
    492     private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
    493 
    494     private boolean mPreserveFocusAfterLayout = true;
    495 
    496     final ViewFlinger mViewFlinger = new ViewFlinger();
    497 
    498     GapWorker mGapWorker;
    499     GapWorker.LayoutPrefetchRegistryImpl mPrefetchRegistry =
    500             ALLOW_THREAD_GAP_WORK ? new GapWorker.LayoutPrefetchRegistryImpl() : null;
    501 
    502     final State mState = new State();
    503 
    504     private OnScrollListener mScrollListener;
    505     private List<OnScrollListener> mScrollListeners;
    506 
    507     // For use in item animations
    508     boolean mItemsAddedOrRemoved = false;
    509     boolean mItemsChanged = false;
    510     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
    511             new ItemAnimatorRestoreListener();
    512     boolean mPostedAnimatorRunner = false;
    513     RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
    514     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
    515 
    516     // simple array to keep min and max child position during a layout calculation
    517     // preserved not to create a new one in each layout pass
    518     private final int[] mMinMaxLayoutPositions = new int[2];
    519 
    520     private NestedScrollingChildHelper mScrollingChildHelper;
    521     private final int[] mScrollOffset = new int[2];
    522     private final int[] mScrollConsumed = new int[2];
    523     private final int[] mNestedOffsets = new int[2];
    524 
    525     /**
    526      * Reusable int array for use in calls to {@link #scrollStep(int, int, int[])} so that the
    527      * method may mutate it to "return" 2 ints.
    528      */
    529     private final int[] mScrollStepConsumed = new int[2];
    530 
    531     /**
    532      * These are views that had their a11y importance changed during a layout. We defer these events
    533      * until the end of the layout because a11y service may make sync calls back to the RV while
    534      * the View's state is undefined.
    535      */
    536     @VisibleForTesting
    537     final List<ViewHolder> mPendingAccessibilityImportanceChange = new ArrayList<>();
    538 
    539     private Runnable mItemAnimatorRunner = new Runnable() {
    540         @Override
    541         public void run() {
    542             if (mItemAnimator != null) {
    543                 mItemAnimator.runPendingAnimations();
    544             }
    545             mPostedAnimatorRunner = false;
    546         }
    547     };
    548 
    549     static final Interpolator sQuinticInterpolator = new Interpolator() {
    550         @Override
    551         public float getInterpolation(float t) {
    552             t -= 1.0f;
    553             return t * t * t * t * t + 1.0f;
    554         }
    555     };
    556 
    557     /**
    558      * The callback to convert view info diffs into animations.
    559      */
    560     private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
    561             new ViewInfoStore.ProcessCallback() {
    562                 @Override
    563                 public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
    564                         @Nullable ItemHolderInfo postInfo) {
    565                     mRecycler.unscrapView(viewHolder);
    566                     animateDisappearance(viewHolder, info, postInfo);
    567                 }
    568                 @Override
    569                 public void processAppeared(ViewHolder viewHolder,
    570                         ItemHolderInfo preInfo, ItemHolderInfo info) {
    571                     animateAppearance(viewHolder, preInfo, info);
    572                 }
    573 
    574                 @Override
    575                 public void processPersistent(ViewHolder viewHolder,
    576                         @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
    577                     viewHolder.setIsRecyclable(false);
    578                     if (mDataSetHasChangedAfterLayout) {
    579                         // since it was rebound, use change instead as we'll be mapping them from
    580                         // stable ids. If stable ids were false, we would not be running any
    581                         // animations
    582                         if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
    583                                 postInfo)) {
    584                             postAnimationRunner();
    585                         }
    586                     } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
    587                         postAnimationRunner();
    588                     }
    589                 }
    590                 @Override
    591                 public void unused(ViewHolder viewHolder) {
    592                     mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
    593                 }
    594             };
    595 
    596     public RecyclerView(@NonNull Context context) {
    597         this(context, null);
    598     }
    599 
    600     public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
    601         this(context, attrs, 0);
    602     }
    603 
    604     public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
    605         super(context, attrs, defStyle);
    606         if (attrs != null) {
    607             TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);
    608             mClipToPadding = a.getBoolean(0, true);
    609             a.recycle();
    610         } else {
    611             mClipToPadding = true;
    612         }
    613         setScrollContainer(true);
    614         setFocusableInTouchMode(true);
    615 
    616         final ViewConfiguration vc = ViewConfiguration.get(context);
    617         mTouchSlop = vc.getScaledTouchSlop();
    618         mScaledHorizontalScrollFactor =
    619                 ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
    620         mScaledVerticalScrollFactor =
    621                 ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
    622         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
    623         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
    624         setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
    625 
    626         mItemAnimator.setListener(mItemAnimatorListener);
    627         initAdapterManager();
    628         initChildrenHelper();
    629         initAutofill();
    630         // If not explicitly specified this view is important for accessibility.
    631         if (ViewCompat.getImportantForAccessibility(this)
    632                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    633             ViewCompat.setImportantForAccessibility(this,
    634                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
    635         }
    636         mAccessibilityManager = (AccessibilityManager) getContext()
    637                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
    638         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
    639         // Create the layoutManager if specified.
    640 
    641         boolean nestedScrollingEnabled = true;
    642 
    643         if (attrs != null) {
    644             int defStyleRes = 0;
    645             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
    646                     defStyle, defStyleRes);
    647             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
    648             int descendantFocusability = a.getInt(
    649                     R.styleable.RecyclerView_android_descendantFocusability, -1);
    650             if (descendantFocusability == -1) {
    651                 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    652             }
    653             mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
    654             if (mEnableFastScroller) {
    655                 StateListDrawable verticalThumbDrawable = (StateListDrawable) a
    656                         .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
    657                 Drawable verticalTrackDrawable = a
    658                         .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
    659                 StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
    660                         .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
    661                 Drawable horizontalTrackDrawable = a
    662                         .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
    663                 initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
    664                         horizontalThumbDrawable, horizontalTrackDrawable);
    665             }
    666             a.recycle();
    667             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
    668 
    669             if (Build.VERSION.SDK_INT >= 21) {
    670                 a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
    671                         defStyle, defStyleRes);
    672                 nestedScrollingEnabled = a.getBoolean(0, true);
    673                 a.recycle();
    674             }
    675         } else {
    676             setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    677         }
    678 
    679         // Re-set whether nested scrolling is enabled so that it is set on all API levels
    680         setNestedScrollingEnabled(nestedScrollingEnabled);
    681     }
    682 
    683     /**
    684      * Label appended to all public exception strings, used to help find which RV in an app is
    685      * hitting an exception.
    686      */
    687     String exceptionLabel() {
    688         return " " + super.toString()
    689                 + ", adapter:" + mAdapter
    690                 + ", layout:" + mLayout
    691                 + ", context:" + getContext();
    692     }
    693 
    694     /**
    695      * If not explicitly specified, this view and its children don't support autofill.
    696      * <p>
    697      * This is done because autofill's means of uniquely identifying views doesn't work out of the
    698      * box with View recycling.
    699      */
    700     @SuppressLint("InlinedApi")
    701     private void initAutofill() {
    702         if (ViewCompat.getImportantForAutofill(this) == View.IMPORTANT_FOR_AUTOFILL_AUTO) {
    703             ViewCompat.setImportantForAutofill(this,
    704                     View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
    705         }
    706     }
    707 
    708     /**
    709      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
    710      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
    711      */
    712     @Nullable
    713     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
    714         return mAccessibilityDelegate;
    715     }
    716 
    717     /**
    718      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
    719      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
    720      */
    721     public void setAccessibilityDelegateCompat(
    722             @Nullable RecyclerViewAccessibilityDelegate accessibilityDelegate) {
    723         mAccessibilityDelegate = accessibilityDelegate;
    724         ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
    725     }
    726 
    727     /**
    728      * Instantiate and set a LayoutManager, if specified in the attributes.
    729      */
    730     private void createLayoutManager(Context context, String className, AttributeSet attrs,
    731             int defStyleAttr, int defStyleRes) {
    732         if (className != null) {
    733             className = className.trim();
    734             if (!className.isEmpty()) {
    735                 className = getFullClassName(context, className);
    736                 try {
    737                     ClassLoader classLoader;
    738                     if (isInEditMode()) {
    739                         // Stupid layoutlib cannot handle simple class loaders.
    740                         classLoader = this.getClass().getClassLoader();
    741                     } else {
    742                         classLoader = context.getClassLoader();
    743                     }
    744                     Class<? extends LayoutManager> layoutManagerClass =
    745                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
    746                     Constructor<? extends LayoutManager> constructor;
    747                     Object[] constructorArgs = null;
    748                     try {
    749                         constructor = layoutManagerClass
    750                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
    751                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
    752                     } catch (NoSuchMethodException e) {
    753                         try {
    754                             constructor = layoutManagerClass.getConstructor();
    755                         } catch (NoSuchMethodException e1) {
    756                             e1.initCause(e);
    757                             throw new IllegalStateException(attrs.getPositionDescription()
    758                                     + ": Error creating LayoutManager " + className, e1);
    759                         }
    760                     }
    761                     constructor.setAccessible(true);
    762                     setLayoutManager(constructor.newInstance(constructorArgs));
    763                 } catch (ClassNotFoundException e) {
    764                     throw new IllegalStateException(attrs.getPositionDescription()
    765                             + ": Unable to find LayoutManager " + className, e);
    766                 } catch (InvocationTargetException e) {
    767                     throw new IllegalStateException(attrs.getPositionDescription()
    768                             + ": Could not instantiate the LayoutManager: " + className, e);
    769                 } catch (InstantiationException e) {
    770                     throw new IllegalStateException(attrs.getPositionDescription()
    771                             + ": Could not instantiate the LayoutManager: " + className, e);
    772                 } catch (IllegalAccessException e) {
    773                     throw new IllegalStateException(attrs.getPositionDescription()
    774                             + ": Cannot access non-public constructor " + className, e);
    775                 } catch (ClassCastException e) {
    776                     throw new IllegalStateException(attrs.getPositionDescription()
    777                             + ": Class is not a LayoutManager " + className, e);
    778                 }
    779             }
    780         }
    781     }
    782 
    783     private String getFullClassName(Context context, String className) {
    784         if (className.charAt(0) == '.') {
    785             return context.getPackageName() + className;
    786         }
    787         if (className.contains(".")) {
    788             return className;
    789         }
    790         return RecyclerView.class.getPackage().getName() + '.' + className;
    791     }
    792 
    793     private void initChildrenHelper() {
    794         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
    795             @Override
    796             public int getChildCount() {
    797                 return RecyclerView.this.getChildCount();
    798             }
    799 
    800             @Override
    801             public void addView(View child, int index) {
    802                 if (VERBOSE_TRACING) {
    803                     TraceCompat.beginSection("RV addView");
    804                 }
    805                 RecyclerView.this.addView(child, index);
    806                 if (VERBOSE_TRACING) {
    807                     TraceCompat.endSection();
    808                 }
    809                 dispatchChildAttached(child);
    810             }
    811 
    812             @Override
    813             public int indexOfChild(View view) {
    814                 return RecyclerView.this.indexOfChild(view);
    815             }
    816 
    817             @Override
    818             public void removeViewAt(int index) {
    819                 final View child = RecyclerView.this.getChildAt(index);
    820                 if (child != null) {
    821                     dispatchChildDetached(child);
    822 
    823                     // Clear any android.view.animation.Animation that may prevent the item from
    824                     // detaching when being removed. If a child is re-added before the
    825                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
    826                     child.clearAnimation();
    827                 }
    828                 if (VERBOSE_TRACING) {
    829                     TraceCompat.beginSection("RV removeViewAt");
    830                 }
    831                 RecyclerView.this.removeViewAt(index);
    832                 if (VERBOSE_TRACING) {
    833                     TraceCompat.endSection();
    834                 }
    835             }
    836 
    837             @Override
    838             public View getChildAt(int offset) {
    839                 return RecyclerView.this.getChildAt(offset);
    840             }
    841 
    842             @Override
    843             public void removeAllViews() {
    844                 final int count = getChildCount();
    845                 for (int i = 0; i < count; i++) {
    846                     View child = getChildAt(i);
    847                     dispatchChildDetached(child);
    848 
    849                     // Clear any android.view.animation.Animation that may prevent the item from
    850                     // detaching when being removed. If a child is re-added before the
    851                     // lazy detach occurs, it will receive invalid attach/detach sequencing.
    852                     child.clearAnimation();
    853                 }
    854                 RecyclerView.this.removeAllViews();
    855             }
    856 
    857             @Override
    858             public ViewHolder getChildViewHolder(View view) {
    859                 return getChildViewHolderInt(view);
    860             }
    861 
    862             @Override
    863             public void attachViewToParent(View child, int index,
    864                     ViewGroup.LayoutParams layoutParams) {
    865                 final ViewHolder vh = getChildViewHolderInt(child);
    866                 if (vh != null) {
    867                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
    868                         throw new IllegalArgumentException("Called attach on a child which is not"
    869                                 + " detached: " + vh + exceptionLabel());
    870                     }
    871                     if (DEBUG) {
    872                         Log.d(TAG, "reAttach " + vh);
    873                     }
    874                     vh.clearTmpDetachFlag();
    875                 }
    876                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
    877             }
    878 
    879             @Override
    880             public void detachViewFromParent(int offset) {
    881                 final View view = getChildAt(offset);
    882                 if (view != null) {
    883                     final ViewHolder vh = getChildViewHolderInt(view);
    884                     if (vh != null) {
    885                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
    886                             throw new IllegalArgumentException("called detach on an already"
    887                                     + " detached child " + vh + exceptionLabel());
    888                         }
    889                         if (DEBUG) {
    890                             Log.d(TAG, "tmpDetach " + vh);
    891                         }
    892                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
    893                     }
    894                 }
    895                 RecyclerView.this.detachViewFromParent(offset);
    896             }
    897 
    898             @Override
    899             public void onEnteredHiddenState(View child) {
    900                 final ViewHolder vh = getChildViewHolderInt(child);
    901                 if (vh != null) {
    902                     vh.onEnteredHiddenState(RecyclerView.this);
    903                 }
    904             }
    905 
    906             @Override
    907             public void onLeftHiddenState(View child) {
    908                 final ViewHolder vh = getChildViewHolderInt(child);
    909                 if (vh != null) {
    910                     vh.onLeftHiddenState(RecyclerView.this);
    911                 }
    912             }
    913         });
    914     }
    915 
    916     void initAdapterManager() {
    917         mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
    918             @Override
    919             public ViewHolder findViewHolder(int position) {
    920                 final ViewHolder vh = findViewHolderForPosition(position, true);
    921                 if (vh == null) {
    922                     return null;
    923                 }
    924                 // ensure it is not hidden because for adapter helper, the only thing matter is that
    925                 // LM thinks view is a child.
    926                 if (mChildHelper.isHidden(vh.itemView)) {
    927                     if (DEBUG) {
    928                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
    929                     }
    930                     return null;
    931                 }
    932                 return vh;
    933             }
    934 
    935             @Override
    936             public void offsetPositionsForRemovingInvisible(int start, int count) {
    937                 offsetPositionRecordsForRemove(start, count, true);
    938                 mItemsAddedOrRemoved = true;
    939                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
    940             }
    941 
    942             @Override
    943             public void offsetPositionsForRemovingLaidOutOrNewView(
    944                     int positionStart, int itemCount) {
    945                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
    946                 mItemsAddedOrRemoved = true;
    947             }
    948 
    949 
    950             @Override
    951             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
    952                 viewRangeUpdate(positionStart, itemCount, payload);
    953                 mItemsChanged = true;
    954             }
    955 
    956             @Override
    957             public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
    958                 dispatchUpdate(op);
    959             }
    960 
    961             void dispatchUpdate(AdapterHelper.UpdateOp op) {
    962                 switch (op.cmd) {
    963                     case AdapterHelper.UpdateOp.ADD:
    964                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
    965                         break;
    966                     case AdapterHelper.UpdateOp.REMOVE:
    967                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
    968                         break;
    969                     case AdapterHelper.UpdateOp.UPDATE:
    970                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
    971                                 op.payload);
    972                         break;
    973                     case AdapterHelper.UpdateOp.MOVE:
    974                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
    975                         break;
    976                 }
    977             }
    978 
    979             @Override
    980             public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
    981                 dispatchUpdate(op);
    982             }
    983 
    984             @Override
    985             public void offsetPositionsForAdd(int positionStart, int itemCount) {
    986                 offsetPositionRecordsForInsert(positionStart, itemCount);
    987                 mItemsAddedOrRemoved = true;
    988             }
    989 
    990             @Override
    991             public void offsetPositionsForMove(int from, int to) {
    992                 offsetPositionRecordsForMove(from, to);
    993                 // should we create mItemsMoved ?
    994                 mItemsAddedOrRemoved = true;
    995             }
    996         });
    997     }
    998 
    999     /**
   1000      * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
   1001      * size is not affected by the adapter contents. RecyclerView can still change its size based
   1002      * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
   1003      * size of its children or contents of its adapter (except the number of items in the adapter).
   1004      * <p>
   1005      * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
   1006      * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
   1007      *
   1008      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
   1009      */
   1010     public void setHasFixedSize(boolean hasFixedSize) {
   1011         mHasFixedSize = hasFixedSize;
   1012     }
   1013 
   1014     /**
   1015      * @return true if the app has specified that changes in adapter content cannot change
   1016      * the size of the RecyclerView itself.
   1017      */
   1018     public boolean hasFixedSize() {
   1019         return mHasFixedSize;
   1020     }
   1021 
   1022     @Override
   1023     public void setClipToPadding(boolean clipToPadding) {
   1024         if (clipToPadding != mClipToPadding) {
   1025             invalidateGlows();
   1026         }
   1027         mClipToPadding = clipToPadding;
   1028         super.setClipToPadding(clipToPadding);
   1029         if (mFirstLayoutComplete) {
   1030             requestLayout();
   1031         }
   1032     }
   1033 
   1034     /**
   1035      * Returns whether this RecyclerView will clip its children to its padding, and resize (but
   1036      * not clip) any EdgeEffect to the padded region, if padding is present.
   1037      * <p>
   1038      * By default, children are clipped to the padding of their parent
   1039      * RecyclerView. This clipping behavior is only enabled if padding is non-zero.
   1040      *
   1041      * @return true if this RecyclerView clips children to its padding and resizes (but doesn't
   1042      *         clip) any EdgeEffect to the padded region, false otherwise.
   1043      *
   1044      * @attr name android:clipToPadding
   1045      */
   1046     @Override
   1047     public boolean getClipToPadding() {
   1048         return mClipToPadding;
   1049     }
   1050 
   1051     /**
   1052      * Configure the scrolling touch slop for a specific use case.
   1053      *
   1054      * Set up the RecyclerView's scrolling motion threshold based on common usages.
   1055      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
   1056      *
   1057      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
   1058      *                     the intended usage of this RecyclerView
   1059      */
   1060     public void setScrollingTouchSlop(int slopConstant) {
   1061         final ViewConfiguration vc = ViewConfiguration.get(getContext());
   1062         switch (slopConstant) {
   1063             default:
   1064                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
   1065                         + slopConstant + "; using default value");
   1066                 // fall-through
   1067             case TOUCH_SLOP_DEFAULT:
   1068                 mTouchSlop = vc.getScaledTouchSlop();
   1069                 break;
   1070 
   1071             case TOUCH_SLOP_PAGING:
   1072                 mTouchSlop = vc.getScaledPagingTouchSlop();
   1073                 break;
   1074         }
   1075     }
   1076 
   1077     /**
   1078      * Swaps the current adapter with the provided one. It is similar to
   1079      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
   1080      * {@link ViewHolder} and does not clear the RecycledViewPool.
   1081      * <p>
   1082      * Note that it still calls onAdapterChanged callbacks.
   1083      *
   1084      * @param adapter The new adapter to set, or null to set no adapter.
   1085      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
   1086      *                                      Views. If adapters have stable ids and/or you want to
   1087      *                                      animate the disappearing views, you may prefer to set
   1088      *                                      this to false.
   1089      * @see #setAdapter(Adapter)
   1090      */
   1091     public void swapAdapter(@Nullable Adapter adapter, boolean removeAndRecycleExistingViews) {
   1092         // bail out if layout is frozen
   1093         setLayoutFrozen(false);
   1094         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
   1095         processDataSetCompletelyChanged(true);
   1096         requestLayout();
   1097     }
   1098     /**
   1099      * Set a new adapter to provide child views on demand.
   1100      * <p>
   1101      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
   1102      * only one adapter, it will be cleared.
   1103      *
   1104      * @param adapter The new adapter to set, or null to set no adapter.
   1105      * @see #swapAdapter(Adapter, boolean)
   1106      */
   1107     public void setAdapter(@Nullable Adapter adapter) {
   1108         // bail out if layout is frozen
   1109         setLayoutFrozen(false);
   1110         setAdapterInternal(adapter, false, true);
   1111         processDataSetCompletelyChanged(false);
   1112         requestLayout();
   1113     }
   1114 
   1115     /**
   1116      * Removes and recycles all views - both those currently attached, and those in the Recycler.
   1117      */
   1118     void removeAndRecycleViews() {
   1119         // end all running animations
   1120         if (mItemAnimator != null) {
   1121             mItemAnimator.endAnimations();
   1122         }
   1123         // Since animations are ended, mLayout.children should be equal to
   1124         // recyclerView.children. This may not be true if item animator's end does not work as
   1125         // expected. (e.g. not release children instantly). It is safer to use mLayout's child
   1126         // count.
   1127         if (mLayout != null) {
   1128             mLayout.removeAndRecycleAllViews(mRecycler);
   1129             mLayout.removeAndRecycleScrapInt(mRecycler);
   1130         }
   1131         // we should clear it here before adapters are swapped to ensure correct callbacks.
   1132         mRecycler.clear();
   1133     }
   1134 
   1135     /**
   1136      * Replaces the current adapter with the new one and triggers listeners.
   1137      * @param adapter The new adapter
   1138      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
   1139      *                               item types with the current adapter (helps us avoid cache
   1140      *                               invalidation).
   1141      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
   1142      *                               compatibleWithPrevious is false, this parameter is ignored.
   1143      */
   1144     private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
   1145             boolean removeAndRecycleViews) {
   1146         if (mAdapter != null) {
   1147             mAdapter.unregisterAdapterDataObserver(mObserver);
   1148             mAdapter.onDetachedFromRecyclerView(this);
   1149         }
   1150         if (!compatibleWithPrevious || removeAndRecycleViews) {
   1151             removeAndRecycleViews();
   1152         }
   1153         mAdapterHelper.reset();
   1154         final Adapter oldAdapter = mAdapter;
   1155         mAdapter = adapter;
   1156         if (adapter != null) {
   1157             adapter.registerAdapterDataObserver(mObserver);
   1158             adapter.onAttachedToRecyclerView(this);
   1159         }
   1160         if (mLayout != null) {
   1161             mLayout.onAdapterChanged(oldAdapter, mAdapter);
   1162         }
   1163         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
   1164         mState.mStructureChanged = true;
   1165     }
   1166 
   1167     /**
   1168      * Retrieves the previously set adapter or null if no adapter is set.
   1169      *
   1170      * @return The previously set adapter
   1171      * @see #setAdapter(Adapter)
   1172      */
   1173     @Nullable
   1174     public Adapter getAdapter() {
   1175         return mAdapter;
   1176     }
   1177 
   1178     /**
   1179      * Register a listener that will be notified whenever a child view is recycled.
   1180      *
   1181      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
   1182      * that a child view is no longer needed. If an application associates expensive
   1183      * or heavyweight data with item views, this may be a good place to release
   1184      * or free those resources.</p>
   1185      *
   1186      * @param listener Listener to register, or null to clear
   1187      */
   1188     public void setRecyclerListener(@Nullable RecyclerListener listener) {
   1189         mRecyclerListener = listener;
   1190     }
   1191 
   1192     /**
   1193      * <p>Return the offset of the RecyclerView's text baseline from the its top
   1194      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
   1195      * this method returns -1.</p>
   1196      *
   1197      * @return the offset of the baseline within the RecyclerView's bounds or -1
   1198      *         if baseline alignment is not supported
   1199      */
   1200     @Override
   1201     public int getBaseline() {
   1202         if (mLayout != null) {
   1203             return mLayout.getBaseline();
   1204         } else {
   1205             return super.getBaseline();
   1206         }
   1207     }
   1208 
   1209     /**
   1210      * Register a listener that will be notified whenever a child view is attached to or detached
   1211      * from RecyclerView.
   1212      *
   1213      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
   1214      * that a child view is no longer needed. If an application associates expensive
   1215      * or heavyweight data with item views, this may be a good place to release
   1216      * or free those resources.</p>
   1217      *
   1218      * @param listener Listener to register
   1219      */
   1220     public void addOnChildAttachStateChangeListener(
   1221             @NonNull OnChildAttachStateChangeListener listener) {
   1222         if (mOnChildAttachStateListeners == null) {
   1223             mOnChildAttachStateListeners = new ArrayList<>();
   1224         }
   1225         mOnChildAttachStateListeners.add(listener);
   1226     }
   1227 
   1228     /**
   1229      * Removes the provided listener from child attached state listeners list.
   1230      *
   1231      * @param listener Listener to unregister
   1232      */
   1233     public void removeOnChildAttachStateChangeListener(
   1234             @NonNull OnChildAttachStateChangeListener listener) {
   1235         if (mOnChildAttachStateListeners == null) {
   1236             return;
   1237         }
   1238         mOnChildAttachStateListeners.remove(listener);
   1239     }
   1240 
   1241     /**
   1242      * Removes all listeners that were added via
   1243      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
   1244      */
   1245     public void clearOnChildAttachStateChangeListeners() {
   1246         if (mOnChildAttachStateListeners != null) {
   1247             mOnChildAttachStateListeners.clear();
   1248         }
   1249     }
   1250 
   1251     /**
   1252      * Set the {@link LayoutManager} that this RecyclerView will use.
   1253      *
   1254      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
   1255      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
   1256      * layout arrangements for child views. These arrangements are controlled by the
   1257      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
   1258      *
   1259      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
   1260      *
   1261      * @param layout LayoutManager to use
   1262      */
   1263     public void setLayoutManager(@Nullable LayoutManager layout) {
   1264         if (layout == mLayout) {
   1265             return;
   1266         }
   1267         stopScroll();
   1268         // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
   1269         // chance that LayoutManagers will re-use views.
   1270         if (mLayout != null) {
   1271             // end all running animations
   1272             if (mItemAnimator != null) {
   1273                 mItemAnimator.endAnimations();
   1274             }
   1275             mLayout.removeAndRecycleAllViews(mRecycler);
   1276             mLayout.removeAndRecycleScrapInt(mRecycler);
   1277             mRecycler.clear();
   1278 
   1279             if (mIsAttached) {
   1280                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
   1281             }
   1282             mLayout.setRecyclerView(null);
   1283             mLayout = null;
   1284         } else {
   1285             mRecycler.clear();
   1286         }
   1287         // this is just a defensive measure for faulty item animators.
   1288         mChildHelper.removeAllViewsUnfiltered();
   1289         mLayout = layout;
   1290         if (layout != null) {
   1291             if (layout.mRecyclerView != null) {
   1292                 throw new IllegalArgumentException("LayoutManager " + layout
   1293                         + " is already attached to a RecyclerView:"
   1294                         + layout.mRecyclerView.exceptionLabel());
   1295             }
   1296             mLayout.setRecyclerView(this);
   1297             if (mIsAttached) {
   1298                 mLayout.dispatchAttachedToWindow(this);
   1299             }
   1300         }
   1301         mRecycler.updateViewCacheSize();
   1302         requestLayout();
   1303     }
   1304 
   1305     /**
   1306      * Set a {@link OnFlingListener} for this {@link RecyclerView}.
   1307      * <p>
   1308      * If the {@link OnFlingListener} is set then it will receive
   1309      * calls to {@link #fling(int,int)} and will be able to intercept them.
   1310      *
   1311      * @param onFlingListener The {@link OnFlingListener} instance.
   1312      */
   1313     public void setOnFlingListener(@Nullable OnFlingListener onFlingListener) {
   1314         mOnFlingListener = onFlingListener;
   1315     }
   1316 
   1317     /**
   1318      * Get the current {@link OnFlingListener} from this {@link RecyclerView}.
   1319      *
   1320      * @return The {@link OnFlingListener} instance currently set (can be null).
   1321      */
   1322     @Nullable
   1323     public OnFlingListener getOnFlingListener() {
   1324         return mOnFlingListener;
   1325     }
   1326 
   1327     @Override
   1328     protected Parcelable onSaveInstanceState() {
   1329         SavedState state = new SavedState(super.onSaveInstanceState());
   1330         if (mPendingSavedState != null) {
   1331             state.copyFrom(mPendingSavedState);
   1332         } else if (mLayout != null) {
   1333             state.mLayoutState = mLayout.onSaveInstanceState();
   1334         } else {
   1335             state.mLayoutState = null;
   1336         }
   1337 
   1338         return state;
   1339     }
   1340 
   1341     @Override
   1342     protected void onRestoreInstanceState(Parcelable state) {
   1343         if (!(state instanceof SavedState)) {
   1344             super.onRestoreInstanceState(state);
   1345             return;
   1346         }
   1347 
   1348         mPendingSavedState = (SavedState) state;
   1349         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
   1350         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
   1351             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
   1352         }
   1353     }
   1354 
   1355     /**
   1356      * Override to prevent freezing of any views created by the adapter.
   1357      */
   1358     @Override
   1359     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
   1360         dispatchFreezeSelfOnly(container);
   1361     }
   1362 
   1363     /**
   1364      * Override to prevent thawing of any views created by the adapter.
   1365      */
   1366     @Override
   1367     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
   1368         dispatchThawSelfOnly(container);
   1369     }
   1370 
   1371     /**
   1372      * Adds a view to the animatingViews list.
   1373      * mAnimatingViews holds the child views that are currently being kept around
   1374      * purely for the purpose of being animated out of view. They are drawn as a regular
   1375      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
   1376      * as they are managed separately from the regular child views.
   1377      * @param viewHolder The ViewHolder to be removed
   1378      */
   1379     private void addAnimatingView(ViewHolder viewHolder) {
   1380         final View view = viewHolder.itemView;
   1381         final boolean alreadyParented = view.getParent() == this;
   1382         mRecycler.unscrapView(getChildViewHolder(view));
   1383         if (viewHolder.isTmpDetached()) {
   1384             // re-attach
   1385             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
   1386         } else if (!alreadyParented) {
   1387             mChildHelper.addView(view, true);
   1388         } else {
   1389             mChildHelper.hide(view);
   1390         }
   1391     }
   1392 
   1393     /**
   1394      * Removes a view from the animatingViews list.
   1395      * @param view The view to be removed
   1396      * @see #addAnimatingView(RecyclerView.ViewHolder)
   1397      * @return true if an animating view is removed
   1398      */
   1399     boolean removeAnimatingView(View view) {
   1400         startInterceptRequestLayout();
   1401         final boolean removed = mChildHelper.removeViewIfHidden(view);
   1402         if (removed) {
   1403             final ViewHolder viewHolder = getChildViewHolderInt(view);
   1404             mRecycler.unscrapView(viewHolder);
   1405             mRecycler.recycleViewHolderInternal(viewHolder);
   1406             if (DEBUG) {
   1407                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
   1408             }
   1409         }
   1410         // only clear request eaten flag if we removed the view.
   1411         stopInterceptRequestLayout(!removed);
   1412         return removed;
   1413     }
   1414 
   1415     /**
   1416      * Return the {@link LayoutManager} currently responsible for
   1417      * layout policy for this RecyclerView.
   1418      *
   1419      * @return The currently bound LayoutManager
   1420      */
   1421     @Nullable
   1422     public LayoutManager getLayoutManager() {
   1423         return mLayout;
   1424     }
   1425 
   1426     /**
   1427      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
   1428      * if no pool is set for this view a new one will be created. See
   1429      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
   1430      *
   1431      * @return The pool used to store recycled item views for reuse.
   1432      * @see #setRecycledViewPool(RecycledViewPool)
   1433      */
   1434     @NonNull
   1435     public RecycledViewPool getRecycledViewPool() {
   1436         return mRecycler.getRecycledViewPool();
   1437     }
   1438 
   1439     /**
   1440      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
   1441      * This can be useful if you have multiple RecyclerViews with adapters that use the same
   1442      * view types, for example if you have several data sets with the same kinds of item views
   1443      * displayed by a {@link ViewPager ViewPager}.
   1444      *
   1445      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
   1446      */
   1447     public void setRecycledViewPool(@Nullable RecycledViewPool pool) {
   1448         mRecycler.setRecycledViewPool(pool);
   1449     }
   1450 
   1451     /**
   1452      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
   1453      *
   1454      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
   1455      *
   1456      * @see ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)
   1457      */
   1458     public void setViewCacheExtension(@Nullable ViewCacheExtension extension) {
   1459         mRecycler.setViewCacheExtension(extension);
   1460     }
   1461 
   1462     /**
   1463      * Set the number of offscreen views to retain before adding them to the potentially shared
   1464      * {@link #getRecycledViewPool() recycled view pool}.
   1465      *
   1466      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
   1467      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
   1468      * to rebind them.</p>
   1469      *
   1470      * @param size Number of views to cache offscreen before returning them to the general
   1471      *             recycled view pool
   1472      */
   1473     public void setItemViewCacheSize(int size) {
   1474         mRecycler.setViewCacheSize(size);
   1475     }
   1476 
   1477     /**
   1478      * Return the current scrolling state of the RecyclerView.
   1479      *
   1480      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
   1481      * {@link #SCROLL_STATE_SETTLING}
   1482      */
   1483     public int getScrollState() {
   1484         return mScrollState;
   1485     }
   1486 
   1487     void setScrollState(int state) {
   1488         if (state == mScrollState) {
   1489             return;
   1490         }
   1491         if (DEBUG) {
   1492             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
   1493                     new Exception());
   1494         }
   1495         mScrollState = state;
   1496         if (state != SCROLL_STATE_SETTLING) {
   1497             stopScrollersInternal();
   1498         }
   1499         dispatchOnScrollStateChanged(state);
   1500     }
   1501 
   1502     /**
   1503      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1504      * affect both measurement and drawing of individual item views.
   1505      *
   1506      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1507      * be run/queried/drawn first for their effects on item views. Padding added to views
   1508      * will be nested; a padding added by an earlier decoration will mean further
   1509      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1510      * given area.</p>
   1511      *
   1512      * @param decor Decoration to add
   1513      * @param index Position in the decoration chain to insert this decoration at. If this value
   1514      *              is negative the decoration will be added at the end.
   1515      */
   1516     public void addItemDecoration(@NonNull ItemDecoration decor, int index) {
   1517         if (mLayout != null) {
   1518             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
   1519                     + " layout");
   1520         }
   1521         if (mItemDecorations.isEmpty()) {
   1522             setWillNotDraw(false);
   1523         }
   1524         if (index < 0) {
   1525             mItemDecorations.add(decor);
   1526         } else {
   1527             mItemDecorations.add(index, decor);
   1528         }
   1529         markItemDecorInsetsDirty();
   1530         requestLayout();
   1531     }
   1532 
   1533     /**
   1534      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1535      * affect both measurement and drawing of individual item views.
   1536      *
   1537      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1538      * be run/queried/drawn first for their effects on item views. Padding added to views
   1539      * will be nested; a padding added by an earlier decoration will mean further
   1540      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1541      * given area.</p>
   1542      *
   1543      * @param decor Decoration to add
   1544      */
   1545     public void addItemDecoration(@NonNull ItemDecoration decor) {
   1546         addItemDecoration(decor, -1);
   1547     }
   1548 
   1549     /**
   1550      * Returns an {@link ItemDecoration} previously added to this RecyclerView.
   1551      *
   1552      * @param index The index position of the desired ItemDecoration.
   1553      * @return the ItemDecoration at index position
   1554      * @throws IndexOutOfBoundsException on invalid index
   1555      */
   1556     @NonNull
   1557     public ItemDecoration getItemDecorationAt(int index) {
   1558         final int size = getItemDecorationCount();
   1559         if (index < 0 || index >= size) {
   1560             throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
   1561         }
   1562 
   1563         return mItemDecorations.get(index);
   1564     }
   1565 
   1566     /**
   1567      * Returns the number of {@link ItemDecoration} currently added to this RecyclerView.
   1568      *
   1569      * @return number of ItemDecorations currently added added to this RecyclerView.
   1570      */
   1571     public int getItemDecorationCount() {
   1572         return mItemDecorations.size();
   1573     }
   1574 
   1575     /**
   1576      * Removes the {@link ItemDecoration} associated with the supplied index position.
   1577      *
   1578      * @param index The index position of the ItemDecoration to be removed.
   1579      */
   1580     public void removeItemDecorationAt(int index) {
   1581         final int size = getItemDecorationCount();
   1582         if (index < 0 || index >= size) {
   1583             throw new IndexOutOfBoundsException(index + " is an invalid index for size " + size);
   1584         }
   1585 
   1586         removeItemDecoration(getItemDecorationAt(index));
   1587     }
   1588 
   1589     /**
   1590      * Remove an {@link ItemDecoration} from this RecyclerView.
   1591      *
   1592      * <p>The given decoration will no longer impact the measurement and drawing of
   1593      * item views.</p>
   1594      *
   1595      * @param decor Decoration to remove
   1596      * @see #addItemDecoration(ItemDecoration)
   1597      */
   1598     public void removeItemDecoration(@NonNull ItemDecoration decor) {
   1599         if (mLayout != null) {
   1600             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
   1601                     + " layout");
   1602         }
   1603         mItemDecorations.remove(decor);
   1604         if (mItemDecorations.isEmpty()) {
   1605             setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);
   1606         }
   1607         markItemDecorInsetsDirty();
   1608         requestLayout();
   1609     }
   1610 
   1611     /**
   1612      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
   1613      * <p>
   1614      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
   1615      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
   1616      * true if childDrawingOrderCallback is not null, false otherwise.
   1617      * <p>
   1618      * Note that child drawing order may be overridden by View's elevation.
   1619      *
   1620      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
   1621      *                                  system.
   1622      */
   1623     public void setChildDrawingOrderCallback(
   1624             @Nullable ChildDrawingOrderCallback childDrawingOrderCallback) {
   1625         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
   1626             return;
   1627         }
   1628         mChildDrawingOrderCallback = childDrawingOrderCallback;
   1629         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
   1630     }
   1631 
   1632     /**
   1633      * Set a listener that will be notified of any changes in scroll state or position.
   1634      *
   1635      * @param listener Listener to set or null to clear
   1636      *
   1637      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
   1638      *             {@link #removeOnScrollListener(OnScrollListener)}
   1639      */
   1640     @Deprecated
   1641     public void setOnScrollListener(@Nullable OnScrollListener listener) {
   1642         mScrollListener = listener;
   1643     }
   1644 
   1645     /**
   1646      * Add a listener that will be notified of any changes in scroll state or position.
   1647      *
   1648      * <p>Components that add a listener should take care to remove it when finished.
   1649      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
   1650      * to remove all attached listeners.</p>
   1651      *
   1652      * @param listener listener to set
   1653      */
   1654     public void addOnScrollListener(@NonNull OnScrollListener listener) {
   1655         if (mScrollListeners == null) {
   1656             mScrollListeners = new ArrayList<>();
   1657         }
   1658         mScrollListeners.add(listener);
   1659     }
   1660 
   1661     /**
   1662      * Remove a listener that was notified of any changes in scroll state or position.
   1663      *
   1664      * @param listener listener to set or null to clear
   1665      */
   1666     public void removeOnScrollListener(@NonNull OnScrollListener listener) {
   1667         if (mScrollListeners != null) {
   1668             mScrollListeners.remove(listener);
   1669         }
   1670     }
   1671 
   1672     /**
   1673      * Remove all secondary listener that were notified of any changes in scroll state or position.
   1674      */
   1675     public void clearOnScrollListeners() {
   1676         if (mScrollListeners != null) {
   1677             mScrollListeners.clear();
   1678         }
   1679     }
   1680 
   1681     /**
   1682      * Convenience method to scroll to a certain position.
   1683      *
   1684      * RecyclerView does not implement scrolling logic, rather forwards the call to
   1685      * {@link RecyclerView.LayoutManager#scrollToPosition(int)}
   1686      * @param position Scroll to this adapter position
   1687      * @see RecyclerView.LayoutManager#scrollToPosition(int)
   1688      */
   1689     public void scrollToPosition(int position) {
   1690         if (mLayoutFrozen) {
   1691             return;
   1692         }
   1693         stopScroll();
   1694         if (mLayout == null) {
   1695             Log.e(TAG, "Cannot scroll to position a LayoutManager set. "
   1696                     + "Call setLayoutManager with a non-null argument.");
   1697             return;
   1698         }
   1699         mLayout.scrollToPosition(position);
   1700         awakenScrollBars();
   1701     }
   1702 
   1703     void jumpToPositionForSmoothScroller(int position) {
   1704         if (mLayout == null) {
   1705             return;
   1706         }
   1707         mLayout.scrollToPosition(position);
   1708         awakenScrollBars();
   1709     }
   1710 
   1711     /**
   1712      * Starts a smooth scroll to an adapter position.
   1713      * <p>
   1714      * To support smooth scrolling, you must override
   1715      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
   1716      * {@link SmoothScroller}.
   1717      * <p>
   1718      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
   1719      * provide a custom smooth scroll logic, override
   1720      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
   1721      * LayoutManager.
   1722      *
   1723      * @param position The adapter position to scroll to
   1724      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
   1725      */
   1726     public void smoothScrollToPosition(int position) {
   1727         if (mLayoutFrozen) {
   1728             return;
   1729         }
   1730         if (mLayout == null) {
   1731             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
   1732                     + "Call setLayoutManager with a non-null argument.");
   1733             return;
   1734         }
   1735         mLayout.smoothScrollToPosition(this, mState, position);
   1736     }
   1737 
   1738     @Override
   1739     public void scrollTo(int x, int y) {
   1740         Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
   1741                 + "Use scrollToPosition instead");
   1742     }
   1743 
   1744     @Override
   1745     public void scrollBy(int x, int y) {
   1746         if (mLayout == null) {
   1747             Log.e(TAG, "Cannot scroll without a LayoutManager set. "
   1748                     + "Call setLayoutManager with a non-null argument.");
   1749             return;
   1750         }
   1751         if (mLayoutFrozen) {
   1752             return;
   1753         }
   1754         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   1755         final boolean canScrollVertical = mLayout.canScrollVertically();
   1756         if (canScrollHorizontal || canScrollVertical) {
   1757             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
   1758         }
   1759     }
   1760 
   1761     /**
   1762      * Scrolls the RV by 'dx' and 'dy' via calls to
   1763      * {@link LayoutManager#scrollHorizontallyBy(int, Recycler, State)} and
   1764      * {@link LayoutManager#scrollVerticallyBy(int, Recycler, State)}.
   1765      *
   1766      * Also sets how much of the scroll was actually consumed in 'consumed' parameter (indexes 0 and
   1767      * 1 for the x axis and y axis, respectively).
   1768      *
   1769      * This method should only be called in the context of an existing scroll operation such that
   1770      * any other necessary operations (such as a call to {@link #consumePendingUpdateOperations()})
   1771      * is already handled.
   1772      */
   1773     private void scrollStep(int dx, int dy, @Nullable int[] consumed) {
   1774         startInterceptRequestLayout();
   1775         onEnterLayoutOrScroll();
   1776 
   1777         TraceCompat.beginSection(TRACE_SCROLL_TAG);
   1778         fillRemainingScrollValues(mState);
   1779 
   1780         int consumedX = 0;
   1781         int consumedY = 0;
   1782         if (dx != 0) {
   1783             consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
   1784         }
   1785         if (dy != 0) {
   1786             consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
   1787         }
   1788 
   1789         TraceCompat.endSection();
   1790         repositionShadowingViews();
   1791 
   1792         onExitLayoutOrScroll();
   1793         stopInterceptRequestLayout(false);
   1794 
   1795         if (consumed != null) {
   1796             consumed[0] = consumedX;
   1797             consumed[1] = consumedY;
   1798         }
   1799     }
   1800 
   1801     /**
   1802      * Helper method reflect data changes to the state.
   1803      * <p>
   1804      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
   1805      * but data actually changed.
   1806      * <p>
   1807      * This method consumes all deferred changes to avoid that case.
   1808      */
   1809     void consumePendingUpdateOperations() {
   1810         if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
   1811             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
   1812             dispatchLayout();
   1813             TraceCompat.endSection();
   1814             return;
   1815         }
   1816         if (!mAdapterHelper.hasPendingUpdates()) {
   1817             return;
   1818         }
   1819 
   1820         // if it is only an item change (no add-remove-notifyDataSetChanged) we can check if any
   1821         // of the visible items is affected and if not, just ignore the change.
   1822         if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
   1823                 .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
   1824                         | AdapterHelper.UpdateOp.MOVE)) {
   1825             TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
   1826             startInterceptRequestLayout();
   1827             onEnterLayoutOrScroll();
   1828             mAdapterHelper.preProcess();
   1829             if (!mLayoutWasDefered) {
   1830                 if (hasUpdatedView()) {
   1831                     dispatchLayout();
   1832                 } else {
   1833                     // no need to layout, clean state
   1834                     mAdapterHelper.consumePostponedUpdates();
   1835                 }
   1836             }
   1837             stopInterceptRequestLayout(true);
   1838             onExitLayoutOrScroll();
   1839             TraceCompat.endSection();
   1840         } else if (mAdapterHelper.hasPendingUpdates()) {
   1841             TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
   1842             dispatchLayout();
   1843             TraceCompat.endSection();
   1844         }
   1845     }
   1846 
   1847     /**
   1848      * @return True if an existing view holder needs to be updated
   1849      */
   1850     private boolean hasUpdatedView() {
   1851         final int childCount = mChildHelper.getChildCount();
   1852         for (int i = 0; i < childCount; i++) {
   1853             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   1854             if (holder == null || holder.shouldIgnore()) {
   1855                 continue;
   1856             }
   1857             if (holder.isUpdated()) {
   1858                 return true;
   1859             }
   1860         }
   1861         return false;
   1862     }
   1863 
   1864     /**
   1865      * Does not perform bounds checking. Used by internal methods that have already validated input.
   1866      * <p>
   1867      * It also reports any unused scroll request to the related EdgeEffect.
   1868      *
   1869      * @param x The amount of horizontal scroll request
   1870      * @param y The amount of vertical scroll request
   1871      * @param ev The originating MotionEvent, or null if not from a touch event.
   1872      *
   1873      * @return Whether any scroll was consumed in either direction.
   1874      */
   1875     boolean scrollByInternal(int x, int y, MotionEvent ev) {
   1876         int unconsumedX = 0, unconsumedY = 0;
   1877         int consumedX = 0, consumedY = 0;
   1878 
   1879         consumePendingUpdateOperations();
   1880         if (mAdapter != null) {
   1881             scrollStep(x, y, mScrollStepConsumed);
   1882             consumedX = mScrollStepConsumed[0];
   1883             consumedY = mScrollStepConsumed[1];
   1884             unconsumedX = x - consumedX;
   1885             unconsumedY = y - consumedY;
   1886         }
   1887         if (!mItemDecorations.isEmpty()) {
   1888             invalidate();
   1889         }
   1890 
   1891         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,
   1892                 TYPE_TOUCH)) {
   1893             // Update the last touch co-ords, taking any scroll offset into account
   1894             mLastTouchX -= mScrollOffset[0];
   1895             mLastTouchY -= mScrollOffset[1];
   1896             if (ev != null) {
   1897                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   1898             }
   1899             mNestedOffsets[0] += mScrollOffset[0];
   1900             mNestedOffsets[1] += mScrollOffset[1];
   1901         } else if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   1902             if (ev != null && !MotionEventCompat.isFromSource(ev, InputDevice.SOURCE_MOUSE)) {
   1903                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
   1904             }
   1905             considerReleasingGlowsOnScroll(x, y);
   1906         }
   1907         if (consumedX != 0 || consumedY != 0) {
   1908             dispatchOnScrolled(consumedX, consumedY);
   1909         }
   1910         if (!awakenScrollBars()) {
   1911             invalidate();
   1912         }
   1913         return consumedX != 0 || consumedY != 0;
   1914     }
   1915 
   1916     /**
   1917      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
   1918      * range. This value is used to compute the length of the thumb within the scrollbar's track.
   1919      * </p>
   1920      *
   1921      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1922      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
   1923      *
   1924      * <p>Default implementation returns 0.</p>
   1925      *
   1926      * <p>If you want to support scroll bars, override
   1927      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
   1928      * LayoutManager. </p>
   1929      *
   1930      * @return The horizontal offset of the scrollbar's thumb
   1931      * @see RecyclerView.LayoutManager#computeHorizontalScrollOffset
   1932      * (RecyclerView.State)
   1933      */
   1934     @Override
   1935     public int computeHorizontalScrollOffset() {
   1936         if (mLayout == null) {
   1937             return 0;
   1938         }
   1939         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState) : 0;
   1940     }
   1941 
   1942     /**
   1943      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
   1944      * horizontal range. This value is used to compute the length of the thumb within the
   1945      * scrollbar's track.</p>
   1946      *
   1947      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1948      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
   1949      *
   1950      * <p>Default implementation returns 0.</p>
   1951      *
   1952      * <p>If you want to support scroll bars, override
   1953      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
   1954      * LayoutManager.</p>
   1955      *
   1956      * @return The horizontal extent of the scrollbar's thumb
   1957      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
   1958      */
   1959     @Override
   1960     public int computeHorizontalScrollExtent() {
   1961         if (mLayout == null) {
   1962             return 0;
   1963         }
   1964         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
   1965     }
   1966 
   1967     /**
   1968      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
   1969      *
   1970      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1971      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
   1972      *
   1973      * <p>Default implementation returns 0.</p>
   1974      *
   1975      * <p>If you want to support scroll bars, override
   1976      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
   1977      * LayoutManager.</p>
   1978      *
   1979      * @return The total horizontal range represented by the vertical scrollbar
   1980      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
   1981      */
   1982     @Override
   1983     public int computeHorizontalScrollRange() {
   1984         if (mLayout == null) {
   1985             return 0;
   1986         }
   1987         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
   1988     }
   1989 
   1990     /**
   1991      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
   1992      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
   1993      *
   1994      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1995      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
   1996      *
   1997      * <p>Default implementation returns 0.</p>
   1998      *
   1999      * <p>If you want to support scroll bars, override
   2000      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
   2001      * LayoutManager.</p>
   2002      *
   2003      * @return The vertical offset of the scrollbar's thumb
   2004      * @see RecyclerView.LayoutManager#computeVerticalScrollOffset
   2005      * (RecyclerView.State)
   2006      */
   2007     @Override
   2008     public int computeVerticalScrollOffset() {
   2009         if (mLayout == null) {
   2010             return 0;
   2011         }
   2012         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
   2013     }
   2014 
   2015     /**
   2016      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
   2017      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
   2018      *
   2019      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   2020      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
   2021      *
   2022      * <p>Default implementation returns 0.</p>
   2023      *
   2024      * <p>If you want to support scroll bars, override
   2025      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
   2026      * LayoutManager.</p>
   2027      *
   2028      * @return The vertical extent of the scrollbar's thumb
   2029      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
   2030      */
   2031     @Override
   2032     public int computeVerticalScrollExtent() {
   2033         if (mLayout == null) {
   2034             return 0;
   2035         }
   2036         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
   2037     }
   2038 
   2039     /**
   2040      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
   2041      *
   2042      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   2043      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
   2044      *
   2045      * <p>Default implementation returns 0.</p>
   2046      *
   2047      * <p>If you want to support scroll bars, override
   2048      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
   2049      * LayoutManager.</p>
   2050      *
   2051      * @return The total vertical range represented by the vertical scrollbar
   2052      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
   2053      */
   2054     @Override
   2055     public int computeVerticalScrollRange() {
   2056         if (mLayout == null) {
   2057             return 0;
   2058         }
   2059         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
   2060     }
   2061 
   2062     /**
   2063      * This method should be called before any code that may trigger a child view to cause a call to
   2064      * {@link RecyclerView#requestLayout()}.  Doing so enables {@link RecyclerView} to avoid
   2065      * reacting to additional redundant calls to {@link #requestLayout()}.
   2066      * <p>
   2067      * A call to this method must always be accompanied by a call to
   2068      * {@link #stopInterceptRequestLayout(boolean)} that follows the code that may trigger a
   2069      * child View to cause a call to {@link RecyclerView#requestLayout()}.
   2070      *
   2071      * @see #stopInterceptRequestLayout(boolean)
   2072      */
   2073     void startInterceptRequestLayout() {
   2074         mInterceptRequestLayoutDepth++;
   2075         if (mInterceptRequestLayoutDepth == 1 && !mLayoutFrozen) {
   2076             mLayoutWasDefered = false;
   2077         }
   2078     }
   2079 
   2080     /**
   2081      * This method should be called after any code that may trigger a child view to cause a call to
   2082      * {@link RecyclerView#requestLayout()}.
   2083      * <p>
   2084      * A call to this method must always be accompanied by a call to
   2085      * {@link #startInterceptRequestLayout()} that precedes the code that may trigger a child
   2086      * View to cause a call to {@link RecyclerView#requestLayout()}.
   2087      *
   2088      * @see #startInterceptRequestLayout()
   2089      */
   2090     void stopInterceptRequestLayout(boolean performLayoutChildren) {
   2091         if (mInterceptRequestLayoutDepth < 1) {
   2092             //noinspection PointlessBooleanExpression
   2093             if (DEBUG) {
   2094                 throw new IllegalStateException("stopInterceptRequestLayout was called more "
   2095                         + "times than startInterceptRequestLayout."
   2096                         + exceptionLabel());
   2097             }
   2098             mInterceptRequestLayoutDepth = 1;
   2099         }
   2100         if (!performLayoutChildren && !mLayoutFrozen) {
   2101             // Reset the layout request eaten counter.
   2102             // This is necessary since eatRequest calls can be nested in which case the other
   2103             // call will override the inner one.
   2104             // for instance:
   2105             // eat layout for process adapter updates
   2106             //   eat layout for dispatchLayout
   2107             //     a bunch of req layout calls arrive
   2108 
   2109             mLayoutWasDefered = false;
   2110         }
   2111         if (mInterceptRequestLayoutDepth == 1) {
   2112             // when layout is frozen we should delay dispatchLayout()
   2113             if (performLayoutChildren && mLayoutWasDefered && !mLayoutFrozen
   2114                     && mLayout != null && mAdapter != null) {
   2115                 dispatchLayout();
   2116             }
   2117             if (!mLayoutFrozen) {
   2118                 mLayoutWasDefered = false;
   2119             }
   2120         }
   2121         mInterceptRequestLayoutDepth--;
   2122     }
   2123 
   2124     /**
   2125      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
   2126      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
   2127      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
   2128      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
   2129      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
   2130      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
   2131      * called.
   2132      *
   2133      * <p>
   2134      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
   2135      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
   2136      * RecyclerView, State, int)}.
   2137      * <p>
   2138      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
   2139      * stop frozen.
   2140      * <p>
   2141      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
   2142      * responsibility to call ItemAnimator.end().
   2143      *
   2144      * @param frozen   true to freeze layout and scroll, false to re-enable.
   2145      */
   2146     public void setLayoutFrozen(boolean frozen) {
   2147         if (frozen != mLayoutFrozen) {
   2148             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
   2149             if (!frozen) {
   2150                 mLayoutFrozen = false;
   2151                 if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
   2152                     requestLayout();
   2153                 }
   2154                 mLayoutWasDefered = false;
   2155             } else {
   2156                 final long now = SystemClock.uptimeMillis();
   2157                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
   2158                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
   2159                 onTouchEvent(cancelEvent);
   2160                 mLayoutFrozen = true;
   2161                 mIgnoreMotionEventTillDown = true;
   2162                 stopScroll();
   2163             }
   2164         }
   2165     }
   2166 
   2167     /**
   2168      * Returns true if layout and scroll are frozen.
   2169      *
   2170      * @return true if layout and scroll are frozen
   2171      * @see #setLayoutFrozen(boolean)
   2172      */
   2173     public boolean isLayoutFrozen() {
   2174         return mLayoutFrozen;
   2175     }
   2176 
   2177     /**
   2178      * Animate a scroll by the given amount of pixels along either axis.
   2179      *
   2180      * @param dx Pixels to scroll horizontally
   2181      * @param dy Pixels to scroll vertically
   2182      */
   2183     public void smoothScrollBy(@Px int dx, @Px int dy) {
   2184         smoothScrollBy(dx, dy, null);
   2185     }
   2186 
   2187     /**
   2188      * Animate a scroll by the given amount of pixels along either axis.
   2189      *
   2190      * @param dx Pixels to scroll horizontally
   2191      * @param dy Pixels to scroll vertically
   2192      * @param interpolator {@link Interpolator} to be used for scrolling. If it is
   2193      *                     {@code null}, RecyclerView is going to use the default interpolator.
   2194      */
   2195     public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator) {
   2196         if (mLayout == null) {
   2197             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
   2198                     + "Call setLayoutManager with a non-null argument.");
   2199             return;
   2200         }
   2201         if (mLayoutFrozen) {
   2202             return;
   2203         }
   2204         if (!mLayout.canScrollHorizontally()) {
   2205             dx = 0;
   2206         }
   2207         if (!mLayout.canScrollVertically()) {
   2208             dy = 0;
   2209         }
   2210         if (dx != 0 || dy != 0) {
   2211             mViewFlinger.smoothScrollBy(dx, dy, interpolator);
   2212         }
   2213     }
   2214 
   2215     /**
   2216      * Begin a standard fling with an initial velocity along each axis in pixels per second.
   2217      * If the velocity given is below the system-defined minimum this method will return false
   2218      * and no fling will occur.
   2219      *
   2220      * @param velocityX Initial horizontal velocity in pixels per second
   2221      * @param velocityY Initial vertical velocity in pixels per second
   2222      * @return true if the fling was started, false if the velocity was too low to fling or
   2223      * LayoutManager does not support scrolling in the axis fling is issued.
   2224      *
   2225      * @see LayoutManager#canScrollVertically()
   2226      * @see LayoutManager#canScrollHorizontally()
   2227      */
   2228     public boolean fling(int velocityX, int velocityY) {
   2229         if (mLayout == null) {
   2230             Log.e(TAG, "Cannot fling without a LayoutManager set. "
   2231                     + "Call setLayoutManager with a non-null argument.");
   2232             return false;
   2233         }
   2234         if (mLayoutFrozen) {
   2235             return false;
   2236         }
   2237 
   2238         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   2239         final boolean canScrollVertical = mLayout.canScrollVertically();
   2240 
   2241         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
   2242             velocityX = 0;
   2243         }
   2244         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
   2245             velocityY = 0;
   2246         }
   2247         if (velocityX == 0 && velocityY == 0) {
   2248             // If we don't have any velocity, return false
   2249             return false;
   2250         }
   2251 
   2252         if (!dispatchNestedPreFling(velocityX, velocityY)) {
   2253             final boolean canScroll = canScrollHorizontal || canScrollVertical;
   2254             dispatchNestedFling(velocityX, velocityY, canScroll);
   2255 
   2256             if (mOnFlingListener != null && mOnFlingListener.onFling(velocityX, velocityY)) {
   2257                 return true;
   2258             }
   2259 
   2260             if (canScroll) {
   2261                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
   2262                 if (canScrollHorizontal) {
   2263                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
   2264                 }
   2265                 if (canScrollVertical) {
   2266                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
   2267                 }
   2268                 startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
   2269 
   2270                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
   2271                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
   2272                 mViewFlinger.fling(velocityX, velocityY);
   2273                 return true;
   2274             }
   2275         }
   2276         return false;
   2277     }
   2278 
   2279     /**
   2280      * Stop any current scroll in progress, such as one started by
   2281      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
   2282      */
   2283     public void stopScroll() {
   2284         setScrollState(SCROLL_STATE_IDLE);
   2285         stopScrollersInternal();
   2286     }
   2287 
   2288     /**
   2289      * Similar to {@link #stopScroll()} but does not set the state.
   2290      */
   2291     private void stopScrollersInternal() {
   2292         mViewFlinger.stop();
   2293         if (mLayout != null) {
   2294             mLayout.stopSmoothScroller();
   2295         }
   2296     }
   2297 
   2298     /**
   2299      * Returns the minimum velocity to start a fling.
   2300      *
   2301      * @return The minimum velocity to start a fling
   2302      */
   2303     public int getMinFlingVelocity() {
   2304         return mMinFlingVelocity;
   2305     }
   2306 
   2307 
   2308     /**
   2309      * Returns the maximum fling velocity used by this RecyclerView.
   2310      *
   2311      * @return The maximum fling velocity used by this RecyclerView.
   2312      */
   2313     public int getMaxFlingVelocity() {
   2314         return mMaxFlingVelocity;
   2315     }
   2316 
   2317     /**
   2318      * Apply a pull to relevant overscroll glow effects
   2319      */
   2320     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
   2321         boolean invalidate = false;
   2322         if (overscrollX < 0) {
   2323             ensureLeftGlow();
   2324             EdgeEffectCompat.onPull(mLeftGlow, -overscrollX / getWidth(), 1f - y  / getHeight());
   2325             invalidate = true;
   2326         } else if (overscrollX > 0) {
   2327             ensureRightGlow();
   2328             EdgeEffectCompat.onPull(mRightGlow, overscrollX / getWidth(), y / getHeight());
   2329             invalidate = true;
   2330         }
   2331 
   2332         if (overscrollY < 0) {
   2333             ensureTopGlow();
   2334             EdgeEffectCompat.onPull(mTopGlow, -overscrollY / getHeight(), x / getWidth());
   2335             invalidate = true;
   2336         } else if (overscrollY > 0) {
   2337             ensureBottomGlow();
   2338             EdgeEffectCompat.onPull(mBottomGlow, overscrollY / getHeight(), 1f - x / getWidth());
   2339             invalidate = true;
   2340         }
   2341 
   2342         if (invalidate || overscrollX != 0 || overscrollY != 0) {
   2343             ViewCompat.postInvalidateOnAnimation(this);
   2344         }
   2345     }
   2346 
   2347     private void releaseGlows() {
   2348         boolean needsInvalidate = false;
   2349         if (mLeftGlow != null) {
   2350             mLeftGlow.onRelease();
   2351             needsInvalidate = mLeftGlow.isFinished();
   2352         }
   2353         if (mTopGlow != null) {
   2354             mTopGlow.onRelease();
   2355             needsInvalidate |= mTopGlow.isFinished();
   2356         }
   2357         if (mRightGlow != null) {
   2358             mRightGlow.onRelease();
   2359             needsInvalidate |= mRightGlow.isFinished();
   2360         }
   2361         if (mBottomGlow != null) {
   2362             mBottomGlow.onRelease();
   2363             needsInvalidate |= mBottomGlow.isFinished();
   2364         }
   2365         if (needsInvalidate) {
   2366             ViewCompat.postInvalidateOnAnimation(this);
   2367         }
   2368     }
   2369 
   2370     void considerReleasingGlowsOnScroll(int dx, int dy) {
   2371         boolean needsInvalidate = false;
   2372         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
   2373             mLeftGlow.onRelease();
   2374             needsInvalidate = mLeftGlow.isFinished();
   2375         }
   2376         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
   2377             mRightGlow.onRelease();
   2378             needsInvalidate |= mRightGlow.isFinished();
   2379         }
   2380         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
   2381             mTopGlow.onRelease();
   2382             needsInvalidate |= mTopGlow.isFinished();
   2383         }
   2384         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
   2385             mBottomGlow.onRelease();
   2386             needsInvalidate |= mBottomGlow.isFinished();
   2387         }
   2388         if (needsInvalidate) {
   2389             ViewCompat.postInvalidateOnAnimation(this);
   2390         }
   2391     }
   2392 
   2393     void absorbGlows(int velocityX, int velocityY) {
   2394         if (velocityX < 0) {
   2395             ensureLeftGlow();
   2396             mLeftGlow.onAbsorb(-velocityX);
   2397         } else if (velocityX > 0) {
   2398             ensureRightGlow();
   2399             mRightGlow.onAbsorb(velocityX);
   2400         }
   2401 
   2402         if (velocityY < 0) {
   2403             ensureTopGlow();
   2404             mTopGlow.onAbsorb(-velocityY);
   2405         } else if (velocityY > 0) {
   2406             ensureBottomGlow();
   2407             mBottomGlow.onAbsorb(velocityY);
   2408         }
   2409 
   2410         if (velocityX != 0 || velocityY != 0) {
   2411             ViewCompat.postInvalidateOnAnimation(this);
   2412         }
   2413     }
   2414 
   2415     void ensureLeftGlow() {
   2416         if (mLeftGlow != null) {
   2417             return;
   2418         }
   2419         mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
   2420         if (mClipToPadding) {
   2421             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   2422                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   2423         } else {
   2424             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   2425         }
   2426     }
   2427 
   2428     void ensureRightGlow() {
   2429         if (mRightGlow != null) {
   2430             return;
   2431         }
   2432         mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
   2433         if (mClipToPadding) {
   2434             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   2435                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   2436         } else {
   2437             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   2438         }
   2439     }
   2440 
   2441     void ensureTopGlow() {
   2442         if (mTopGlow != null) {
   2443             return;
   2444         }
   2445         mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
   2446         if (mClipToPadding) {
   2447             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   2448                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   2449         } else {
   2450             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   2451         }
   2452 
   2453     }
   2454 
   2455     void ensureBottomGlow() {
   2456         if (mBottomGlow != null) {
   2457             return;
   2458         }
   2459         mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
   2460         if (mClipToPadding) {
   2461             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   2462                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   2463         } else {
   2464             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   2465         }
   2466     }
   2467 
   2468     void invalidateGlows() {
   2469         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
   2470     }
   2471 
   2472     /**
   2473      * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
   2474      * <p>
   2475      * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
   2476      * and new effects are created as needed using
   2477      * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
   2478      *
   2479      * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
   2480      */
   2481     public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
   2482         Preconditions.checkNotNull(edgeEffectFactory);
   2483         mEdgeEffectFactory = edgeEffectFactory;
   2484         invalidateGlows();
   2485     }
   2486 
   2487     /**
   2488      * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
   2489      * was set.
   2490      *
   2491      * @return The previously set {@link EdgeEffectFactory}
   2492      * @see #setEdgeEffectFactory(EdgeEffectFactory)
   2493      */
   2494     @NonNull
   2495     public EdgeEffectFactory getEdgeEffectFactory() {
   2496         return mEdgeEffectFactory;
   2497     }
   2498 
   2499     /**
   2500      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
   2501      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
   2502      * that differs from other ViewGroups.
   2503      * <p>
   2504      * It first does a focus search within the RecyclerView. If this search finds a View that is in
   2505      * the focus direction with respect to the currently focused View, RecyclerView returns that
   2506      * child as the next focus target. When it cannot find such child, it calls
   2507      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} to layout more Views
   2508      * in the focus search direction. If LayoutManager adds a View that matches the
   2509      * focus search criteria, it will be returned as the focus search result. Otherwise,
   2510      * RecyclerView will call parent to handle the focus search like a regular ViewGroup.
   2511      * <p>
   2512      * When the direction is {@link View#FOCUS_FORWARD} or {@link View#FOCUS_BACKWARD}, a View that
   2513      * is not in the focus direction is still valid focus target which may not be the desired
   2514      * behavior if the Adapter has more children in the focus direction. To handle this case,
   2515      * RecyclerView converts the focus direction to an absolute direction and makes a preliminary
   2516      * focus search in that direction. If there are no Views to gain focus, it will call
   2517      * {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} before running a
   2518      * focus search with the original (relative) direction. This allows RecyclerView to provide
   2519      * better candidates to the focus search while still allowing the view system to take focus from
   2520      * the RecyclerView and give it to a more suitable child if such child exists.
   2521      *
   2522      * @param focused The view that currently has focus
   2523      * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   2524      * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD},
   2525      * {@link View#FOCUS_BACKWARD} or 0 for not applicable.
   2526      *
   2527      * @return A new View that can be the next focus after the focused View
   2528      */
   2529     @Override
   2530     public View focusSearch(View focused, int direction) {
   2531         View result = mLayout.onInterceptFocusSearch(focused, direction);
   2532         if (result != null) {
   2533             return result;
   2534         }
   2535         final boolean canRunFocusFailure = mAdapter != null && mLayout != null
   2536                 && !isComputingLayout() && !mLayoutFrozen;
   2537 
   2538         final FocusFinder ff = FocusFinder.getInstance();
   2539         if (canRunFocusFailure
   2540                 && (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD)) {
   2541             // convert direction to absolute direction and see if we have a view there and if not
   2542             // tell LayoutManager to add if it can.
   2543             boolean needsFocusFailureLayout = false;
   2544             if (mLayout.canScrollVertically()) {
   2545                 final int absDir =
   2546                         direction == View.FOCUS_FORWARD ? View.FOCUS_DOWN : View.FOCUS_UP;
   2547                 final View found = ff.findNextFocus(this, focused, absDir);
   2548                 needsFocusFailureLayout = found == null;
   2549                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
   2550                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
   2551                     direction = absDir;
   2552                 }
   2553             }
   2554             if (!needsFocusFailureLayout && mLayout.canScrollHorizontally()) {
   2555                 boolean rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
   2556                 final int absDir = (direction == View.FOCUS_FORWARD) ^ rtl
   2557                         ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
   2558                 final View found = ff.findNextFocus(this, focused, absDir);
   2559                 needsFocusFailureLayout = found == null;
   2560                 if (FORCE_ABS_FOCUS_SEARCH_DIRECTION) {
   2561                     // Workaround for broken FOCUS_BACKWARD in API 15 and older devices.
   2562                     direction = absDir;
   2563                 }
   2564             }
   2565             if (needsFocusFailureLayout) {
   2566                 consumePendingUpdateOperations();
   2567                 final View focusedItemView = findContainingItemView(focused);
   2568                 if (focusedItemView == null) {
   2569                     // panic, focused view is not a child anymore, cannot call super.
   2570                     return null;
   2571                 }
   2572                 startInterceptRequestLayout();
   2573                 mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
   2574                 stopInterceptRequestLayout(false);
   2575             }
   2576             result = ff.findNextFocus(this, focused, direction);
   2577         } else {
   2578             result = ff.findNextFocus(this, focused, direction);
   2579             if (result == null && canRunFocusFailure) {
   2580                 consumePendingUpdateOperations();
   2581                 final View focusedItemView = findContainingItemView(focused);
   2582                 if (focusedItemView == null) {
   2583                     // panic, focused view is not a child anymore, cannot call super.
   2584                     return null;
   2585                 }
   2586                 startInterceptRequestLayout();
   2587                 result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
   2588                 stopInterceptRequestLayout(false);
   2589             }
   2590         }
   2591         if (result != null && !result.hasFocusable()) {
   2592             if (getFocusedChild() == null) {
   2593                 // Scrolling to this unfocusable view is not meaningful since there is no currently
   2594                 // focused view which RV needs to keep visible.
   2595                 return super.focusSearch(focused, direction);
   2596             }
   2597             // If the next view returned by onFocusSearchFailed in layout manager has no focusable
   2598             // views, we still scroll to that view in order to make it visible on the screen.
   2599             // If it's focusable, framework already calls RV's requestChildFocus which handles
   2600             // bringing this newly focused item onto the screen.
   2601             requestChildOnScreen(result, null);
   2602             return focused;
   2603         }
   2604         return isPreferredNextFocus(focused, result, direction)
   2605                 ? result : super.focusSearch(focused, direction);
   2606     }
   2607 
   2608     /**
   2609      * Checks if the new focus candidate is a good enough candidate such that RecyclerView will
   2610      * assign it as the next focus View instead of letting view hierarchy decide.
   2611      * A good candidate means a View that is aligned in the focus direction wrt the focused View
   2612      * and is not the RecyclerView itself.
   2613      * When this method returns false, RecyclerView will let the parent make the decision so the
   2614      * same View may still get the focus as a result of that search.
   2615      */
   2616     private boolean isPreferredNextFocus(View focused, View next, int direction) {
   2617         if (next == null || next == this) {
   2618             return false;
   2619         }
   2620         // panic, result view is not a child anymore, maybe workaround b/37864393
   2621         if (findContainingItemView(next) == null) {
   2622             return false;
   2623         }
   2624         if (focused == null) {
   2625             return true;
   2626         }
   2627         // panic, focused view is not a child anymore, maybe workaround b/37864393
   2628         if (findContainingItemView(focused) == null) {
   2629             return true;
   2630         }
   2631 
   2632         mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
   2633         mTempRect2.set(0, 0, next.getWidth(), next.getHeight());
   2634         offsetDescendantRectToMyCoords(focused, mTempRect);
   2635         offsetDescendantRectToMyCoords(next, mTempRect2);
   2636         final int rtl = mLayout.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL ? -1 : 1;
   2637         int rightness = 0;
   2638         if ((mTempRect.left < mTempRect2.left
   2639                 || mTempRect.right <= mTempRect2.left)
   2640                 && mTempRect.right < mTempRect2.right) {
   2641             rightness = 1;
   2642         } else if ((mTempRect.right > mTempRect2.right
   2643                 || mTempRect.left >= mTempRect2.right)
   2644                 && mTempRect.left > mTempRect2.left) {
   2645             rightness = -1;
   2646         }
   2647         int downness = 0;
   2648         if ((mTempRect.top < mTempRect2.top
   2649                 || mTempRect.bottom <= mTempRect2.top)
   2650                 && mTempRect.bottom < mTempRect2.bottom) {
   2651             downness = 1;
   2652         } else if ((mTempRect.bottom > mTempRect2.bottom
   2653                 || mTempRect.top >= mTempRect2.bottom)
   2654                 && mTempRect.top > mTempRect2.top) {
   2655             downness = -1;
   2656         }
   2657         switch (direction) {
   2658             case View.FOCUS_LEFT:
   2659                 return rightness < 0;
   2660             case View.FOCUS_RIGHT:
   2661                 return rightness > 0;
   2662             case View.FOCUS_UP:
   2663                 return downness < 0;
   2664             case View.FOCUS_DOWN:
   2665                 return downness > 0;
   2666             case View.FOCUS_FORWARD:
   2667                 return downness > 0 || (downness == 0 && rightness * rtl >= 0);
   2668             case View.FOCUS_BACKWARD:
   2669                 return downness < 0 || (downness == 0 && rightness * rtl <= 0);
   2670         }
   2671         throw new IllegalArgumentException("Invalid direction: " + direction + exceptionLabel());
   2672     }
   2673 
   2674     @Override
   2675     public void requestChildFocus(View child, View focused) {
   2676         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
   2677             requestChildOnScreen(child, focused);
   2678         }
   2679         super.requestChildFocus(child, focused);
   2680     }
   2681 
   2682     /**
   2683      * Requests that the given child of the RecyclerView be positioned onto the screen. This method
   2684      * can be called for both unfocusable and focusable child views. For unfocusable child views,
   2685      * the {@param focused} parameter passed is null, whereas for a focusable child, this parameter
   2686      * indicates the actual descendant view within this child view that holds the focus.
   2687      * @param child The child view of this RecyclerView that wants to come onto the screen.
   2688      * @param focused The descendant view that actually has the focus if child is focusable, null
   2689      *                otherwise.
   2690      */
   2691     private void requestChildOnScreen(@NonNull View child, @Nullable View focused) {
   2692         View rectView = (focused != null) ? focused : child;
   2693         mTempRect.set(0, 0, rectView.getWidth(), rectView.getHeight());
   2694 
   2695         // get item decor offsets w/o refreshing. If they are invalid, there will be another
   2696         // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
   2697         // View in viewport.
   2698         final ViewGroup.LayoutParams focusedLayoutParams = rectView.getLayoutParams();
   2699         if (focusedLayoutParams instanceof LayoutParams) {
   2700             // if focused child has item decors, use them. Otherwise, ignore.
   2701             final LayoutParams lp = (LayoutParams) focusedLayoutParams;
   2702             if (!lp.mInsetsDirty) {
   2703                 final Rect insets = lp.mDecorInsets;
   2704                 mTempRect.left -= insets.left;
   2705                 mTempRect.right += insets.right;
   2706                 mTempRect.top -= insets.top;
   2707                 mTempRect.bottom += insets.bottom;
   2708             }
   2709         }
   2710 
   2711         if (focused != null) {
   2712             offsetDescendantRectToMyCoords(focused, mTempRect);
   2713             offsetRectIntoDescendantCoords(child, mTempRect);
   2714         }
   2715         mLayout.requestChildRectangleOnScreen(this, child, mTempRect, !mFirstLayoutComplete,
   2716                 (focused == null));
   2717     }
   2718 
   2719     @Override
   2720     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
   2721         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
   2722     }
   2723 
   2724     @Override
   2725     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
   2726         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
   2727             super.addFocusables(views, direction, focusableMode);
   2728         }
   2729     }
   2730 
   2731     @Override
   2732     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
   2733         if (isComputingLayout()) {
   2734             // if we are in the middle of a layout calculation, don't let any child take focus.
   2735             // RV will handle it after layout calculation is finished.
   2736             return false;
   2737         }
   2738         return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
   2739     }
   2740 
   2741     @Override
   2742     protected void onAttachedToWindow() {
   2743         super.onAttachedToWindow();
   2744         mLayoutOrScrollCounter = 0;
   2745         mIsAttached = true;
   2746         mFirstLayoutComplete = mFirstLayoutComplete && !isLayoutRequested();
   2747         if (mLayout != null) {
   2748             mLayout.dispatchAttachedToWindow(this);
   2749         }
   2750         mPostedAnimatorRunner = false;
   2751 
   2752         if (ALLOW_THREAD_GAP_WORK) {
   2753             // Register with gap worker
   2754             mGapWorker = GapWorker.sGapWorker.get();
   2755             if (mGapWorker == null) {
   2756                 mGapWorker = new GapWorker();
   2757 
   2758                 // break 60 fps assumption if data from display appears valid
   2759                 // NOTE: we only do this query once, statically, because it's very expensive (> 1ms)
   2760                 Display display = ViewCompat.getDisplay(this);
   2761                 float refreshRate = 60.0f;
   2762                 if (!isInEditMode() && display != null) {
   2763                     float displayRefreshRate = display.getRefreshRate();
   2764                     if (displayRefreshRate >= 30.0f) {
   2765                         refreshRate = displayRefreshRate;
   2766                     }
   2767                 }
   2768                 mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
   2769                 GapWorker.sGapWorker.set(mGapWorker);
   2770             }
   2771             mGapWorker.add(this);
   2772         }
   2773     }
   2774 
   2775     @Override
   2776     protected void onDetachedFromWindow() {
   2777         super.onDetachedFromWindow();
   2778         if (mItemAnimator != null) {
   2779             mItemAnimator.endAnimations();
   2780         }
   2781         stopScroll();
   2782         mIsAttached = false;
   2783         if (mLayout != null) {
   2784             mLayout.dispatchDetachedFromWindow(this, mRecycler);
   2785         }
   2786         mPendingAccessibilityImportanceChange.clear();
   2787         removeCallbacks(mItemAnimatorRunner);
   2788         mViewInfoStore.onDetach();
   2789 
   2790         if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
   2791             // Unregister with gap worker
   2792             mGapWorker.remove(this);
   2793             mGapWorker = null;
   2794         }
   2795     }
   2796 
   2797     /**
   2798      * Returns true if RecyclerView is attached to window.
   2799      */
   2800     @Override
   2801     public boolean isAttachedToWindow() {
   2802         return mIsAttached;
   2803     }
   2804 
   2805     /**
   2806      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2807      * {@link IllegalStateException} if it <b>is not</b>.
   2808      *
   2809      * @param message The message for the exception. Can be null.
   2810      * @see #assertNotInLayoutOrScroll(String)
   2811      */
   2812     void assertInLayoutOrScroll(String message) {
   2813         if (!isComputingLayout()) {
   2814             if (message == null) {
   2815                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
   2816                         + "computing a layout or scrolling" + exceptionLabel());
   2817             }
   2818             throw new IllegalStateException(message + exceptionLabel());
   2819 
   2820         }
   2821     }
   2822 
   2823     /**
   2824      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2825      * {@link IllegalStateException} if it <b>is</b>.
   2826      *
   2827      * @param message The message for the exception. Can be null.
   2828      * @see #assertInLayoutOrScroll(String)
   2829      */
   2830     void assertNotInLayoutOrScroll(String message) {
   2831         if (isComputingLayout()) {
   2832             if (message == null) {
   2833                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
   2834                         + "computing a layout or scrolling" + exceptionLabel());
   2835             }
   2836             throw new IllegalStateException(message);
   2837         }
   2838         if (mDispatchScrollCounter > 0) {
   2839             Log.w(TAG, "Cannot call this method in a scroll callback. Scroll callbacks might"
   2840                             + "be run during a measure & layout pass where you cannot change the"
   2841                             + "RecyclerView data. Any method call that might change the structure"
   2842                             + "of the RecyclerView or the adapter contents should be postponed to"
   2843                             + "the next frame.",
   2844                     new IllegalStateException("" + exceptionLabel()));
   2845         }
   2846     }
   2847 
   2848     /**
   2849      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
   2850      * to child views or this view's standard scrolling behavior.
   2851      *
   2852      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
   2853      * returns true from
   2854      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
   2855      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
   2856      * for each incoming MotionEvent until the end of the gesture.</p>
   2857      *
   2858      * @param listener Listener to add
   2859      * @see SimpleOnItemTouchListener
   2860      */
   2861     public void addOnItemTouchListener(@NonNull OnItemTouchListener listener) {
   2862         mOnItemTouchListeners.add(listener);
   2863     }
   2864 
   2865     /**
   2866      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
   2867      *
   2868      * @param listener Listener to remove
   2869      */
   2870     public void removeOnItemTouchListener(@NonNull OnItemTouchListener listener) {
   2871         mOnItemTouchListeners.remove(listener);
   2872         if (mActiveOnItemTouchListener == listener) {
   2873             mActiveOnItemTouchListener = null;
   2874         }
   2875     }
   2876 
   2877     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
   2878         final int action = e.getAction();
   2879         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
   2880             mActiveOnItemTouchListener = null;
   2881         }
   2882 
   2883         final int listenerCount = mOnItemTouchListeners.size();
   2884         for (int i = 0; i < listenerCount; i++) {
   2885             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2886             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
   2887                 mActiveOnItemTouchListener = listener;
   2888                 return true;
   2889             }
   2890         }
   2891         return false;
   2892     }
   2893 
   2894     private boolean dispatchOnItemTouch(MotionEvent e) {
   2895         final int action = e.getAction();
   2896         if (mActiveOnItemTouchListener != null) {
   2897             if (action == MotionEvent.ACTION_DOWN) {
   2898                 // Stale state from a previous gesture, we're starting a new one. Clear it.
   2899                 mActiveOnItemTouchListener = null;
   2900             } else {
   2901                 mActiveOnItemTouchListener.onTouchEvent(this, e);
   2902                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
   2903                     // Clean up for the next gesture.
   2904                     mActiveOnItemTouchListener = null;
   2905                 }
   2906                 return true;
   2907             }
   2908         }
   2909 
   2910         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
   2911         // as called from onInterceptTouchEvent; skip it.
   2912         if (action != MotionEvent.ACTION_DOWN) {
   2913             final int listenerCount = mOnItemTouchListeners.size();
   2914             for (int i = 0; i < listenerCount; i++) {
   2915                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2916                 if (listener.onInterceptTouchEvent(this, e)) {
   2917                     mActiveOnItemTouchListener = listener;
   2918                     return true;
   2919                 }
   2920             }
   2921         }
   2922         return false;
   2923     }
   2924 
   2925     @Override
   2926     public boolean onInterceptTouchEvent(MotionEvent e) {
   2927         if (mLayoutFrozen) {
   2928             // When layout is frozen,  RV does not intercept the motion event.
   2929             // A child view e.g. a button may still get the click.
   2930             return false;
   2931         }
   2932         if (dispatchOnItemTouchIntercept(e)) {
   2933             cancelTouch();
   2934             return true;
   2935         }
   2936 
   2937         if (mLayout == null) {
   2938             return false;
   2939         }
   2940 
   2941         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   2942         final boolean canScrollVertically = mLayout.canScrollVertically();
   2943 
   2944         if (mVelocityTracker == null) {
   2945             mVelocityTracker = VelocityTracker.obtain();
   2946         }
   2947         mVelocityTracker.addMovement(e);
   2948 
   2949         final int action = e.getActionMasked();
   2950         final int actionIndex = e.getActionIndex();
   2951 
   2952         switch (action) {
   2953             case MotionEvent.ACTION_DOWN:
   2954                 if (mIgnoreMotionEventTillDown) {
   2955                     mIgnoreMotionEventTillDown = false;
   2956                 }
   2957                 mScrollPointerId = e.getPointerId(0);
   2958                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   2959                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   2960 
   2961                 if (mScrollState == SCROLL_STATE_SETTLING) {
   2962                     getParent().requestDisallowInterceptTouchEvent(true);
   2963                     setScrollState(SCROLL_STATE_DRAGGING);
   2964                 }
   2965 
   2966                 // Clear the nested offsets
   2967                 mNestedOffsets[0] = mNestedOffsets[1] = 0;
   2968 
   2969                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
   2970                 if (canScrollHorizontally) {
   2971                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
   2972                 }
   2973                 if (canScrollVertically) {
   2974                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
   2975                 }
   2976                 startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
   2977                 break;
   2978 
   2979             case MotionEvent.ACTION_POINTER_DOWN:
   2980                 mScrollPointerId = e.getPointerId(actionIndex);
   2981                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
   2982                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
   2983                 break;
   2984 
   2985             case MotionEvent.ACTION_MOVE: {
   2986                 final int index = e.findPointerIndex(mScrollPointerId);
   2987                 if (index < 0) {
   2988                     Log.e(TAG, "Error processing scroll; pointer index for id "
   2989                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   2990                     return false;
   2991                 }
   2992 
   2993                 final int x = (int) (e.getX(index) + 0.5f);
   2994                 final int y = (int) (e.getY(index) + 0.5f);
   2995                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   2996                     final int dx = x - mInitialTouchX;
   2997                     final int dy = y - mInitialTouchY;
   2998                     boolean startScroll = false;
   2999                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   3000                         mLastTouchX = x;
   3001                         startScroll = true;
   3002                     }
   3003                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   3004                         mLastTouchY = y;
   3005                         startScroll = true;
   3006                     }
   3007                     if (startScroll) {
   3008                         setScrollState(SCROLL_STATE_DRAGGING);
   3009                     }
   3010                 }
   3011             } break;
   3012 
   3013             case MotionEvent.ACTION_POINTER_UP: {
   3014                 onPointerUp(e);
   3015             } break;
   3016 
   3017             case MotionEvent.ACTION_UP: {
   3018                 mVelocityTracker.clear();
   3019                 stopNestedScroll(TYPE_TOUCH);
   3020             } break;
   3021 
   3022             case MotionEvent.ACTION_CANCEL: {
   3023                 cancelTouch();
   3024             }
   3025         }
   3026         return mScrollState == SCROLL_STATE_DRAGGING;
   3027     }
   3028 
   3029     @Override
   3030     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   3031         final int listenerCount = mOnItemTouchListeners.size();
   3032         for (int i = 0; i < listenerCount; i++) {
   3033             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   3034             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
   3035         }
   3036         super.requestDisallowInterceptTouchEvent(disallowIntercept);
   3037     }
   3038 
   3039     @Override
   3040     public boolean onTouchEvent(MotionEvent e) {
   3041         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
   3042             return false;
   3043         }
   3044         if (dispatchOnItemTouch(e)) {
   3045             cancelTouch();
   3046             return true;
   3047         }
   3048 
   3049         if (mLayout == null) {
   3050             return false;
   3051         }
   3052 
   3053         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   3054         final boolean canScrollVertically = mLayout.canScrollVertically();
   3055 
   3056         if (mVelocityTracker == null) {
   3057             mVelocityTracker = VelocityTracker.obtain();
   3058         }
   3059         boolean eventAddedToVelocityTracker = false;
   3060 
   3061         final MotionEvent vtev = MotionEvent.obtain(e);
   3062         final int action = e.getActionMasked();
   3063         final int actionIndex = e.getActionIndex();
   3064 
   3065         if (action == MotionEvent.ACTION_DOWN) {
   3066             mNestedOffsets[0] = mNestedOffsets[1] = 0;
   3067         }
   3068         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
   3069 
   3070         switch (action) {
   3071             case MotionEvent.ACTION_DOWN: {
   3072                 mScrollPointerId = e.getPointerId(0);
   3073                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   3074                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   3075 
   3076                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
   3077                 if (canScrollHorizontally) {
   3078                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
   3079                 }
   3080                 if (canScrollVertically) {
   3081                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
   3082                 }
   3083                 startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
   3084             } break;
   3085 
   3086             case MotionEvent.ACTION_POINTER_DOWN: {
   3087                 mScrollPointerId = e.getPointerId(actionIndex);
   3088                 mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
   3089                 mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
   3090             } break;
   3091 
   3092             case MotionEvent.ACTION_MOVE: {
   3093                 final int index = e.findPointerIndex(mScrollPointerId);
   3094                 if (index < 0) {
   3095                     Log.e(TAG, "Error processing scroll; pointer index for id "
   3096                             + mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   3097                     return false;
   3098                 }
   3099 
   3100                 final int x = (int) (e.getX(index) + 0.5f);
   3101                 final int y = (int) (e.getY(index) + 0.5f);
   3102                 int dx = mLastTouchX - x;
   3103                 int dy = mLastTouchY - y;
   3104 
   3105                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
   3106                     dx -= mScrollConsumed[0];
   3107                     dy -= mScrollConsumed[1];
   3108                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   3109                     // Updated the nested offsets
   3110                     mNestedOffsets[0] += mScrollOffset[0];
   3111                     mNestedOffsets[1] += mScrollOffset[1];
   3112                 }
   3113 
   3114                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   3115                     boolean startScroll = false;
   3116                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   3117                         if (dx > 0) {
   3118                             dx -= mTouchSlop;
   3119                         } else {
   3120                             dx += mTouchSlop;
   3121                         }
   3122                         startScroll = true;
   3123                     }
   3124                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   3125                         if (dy > 0) {
   3126                             dy -= mTouchSlop;
   3127                         } else {
   3128                             dy += mTouchSlop;
   3129                         }
   3130                         startScroll = true;
   3131                     }
   3132                     if (startScroll) {
   3133                         setScrollState(SCROLL_STATE_DRAGGING);
   3134                     }
   3135                 }
   3136 
   3137                 if (mScrollState == SCROLL_STATE_DRAGGING) {
   3138                     mLastTouchX = x - mScrollOffset[0];
   3139                     mLastTouchY = y - mScrollOffset[1];
   3140 
   3141                     if (scrollByInternal(
   3142                             canScrollHorizontally ? dx : 0,
   3143                             canScrollVertically ? dy : 0,
   3144                             vtev)) {
   3145                         getParent().requestDisallowInterceptTouchEvent(true);
   3146                     }
   3147                     if (mGapWorker != null && (dx != 0 || dy != 0)) {
   3148                         mGapWorker.postFromTraversal(this, dx, dy);
   3149                     }
   3150                 }
   3151             } break;
   3152 
   3153             case MotionEvent.ACTION_POINTER_UP: {
   3154                 onPointerUp(e);
   3155             } break;
   3156 
   3157             case MotionEvent.ACTION_UP: {
   3158                 mVelocityTracker.addMovement(vtev);
   3159                 eventAddedToVelocityTracker = true;
   3160                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
   3161                 final float xvel = canScrollHorizontally
   3162                         ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
   3163                 final float yvel = canScrollVertically
   3164                         ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
   3165                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
   3166                     setScrollState(SCROLL_STATE_IDLE);
   3167                 }
   3168                 resetTouch();
   3169             } break;
   3170 
   3171             case MotionEvent.ACTION_CANCEL: {
   3172                 cancelTouch();
   3173             } break;
   3174         }
   3175 
   3176         if (!eventAddedToVelocityTracker) {
   3177             mVelocityTracker.addMovement(vtev);
   3178         }
   3179         vtev.recycle();
   3180 
   3181         return true;
   3182     }
   3183 
   3184     private void resetTouch() {
   3185         if (mVelocityTracker != null) {
   3186             mVelocityTracker.clear();
   3187         }
   3188         stopNestedScroll(TYPE_TOUCH);
   3189         releaseGlows();
   3190     }
   3191 
   3192     private void cancelTouch() {
   3193         resetTouch();
   3194         setScrollState(SCROLL_STATE_IDLE);
   3195     }
   3196 
   3197     private void onPointerUp(MotionEvent e) {
   3198         final int actionIndex = e.getActionIndex();
   3199         if (e.getPointerId(actionIndex) == mScrollPointerId) {
   3200             // Pick a new pointer to pick up the slack.
   3201             final int newIndex = actionIndex == 0 ? 1 : 0;
   3202             mScrollPointerId = e.getPointerId(newIndex);
   3203             mInitialTouchX = mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
   3204             mInitialTouchY = mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
   3205         }
   3206     }
   3207 
   3208     @Override
   3209     public boolean onGenericMotionEvent(MotionEvent event) {
   3210         if (mLayout == null) {
   3211             return false;
   3212         }
   3213         if (mLayoutFrozen) {
   3214             return false;
   3215         }
   3216         if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
   3217             final float vScroll, hScroll;
   3218             if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
   3219                 if (mLayout.canScrollVertically()) {
   3220                     // Inverse the sign of the vertical scroll to align the scroll orientation
   3221                     // with AbsListView.
   3222                     vScroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
   3223                 } else {
   3224                     vScroll = 0f;
   3225                 }
   3226                 if (mLayout.canScrollHorizontally()) {
   3227                     hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
   3228                 } else {
   3229                     hScroll = 0f;
   3230                 }
   3231             } else if ((event.getSource() & InputDeviceCompat.SOURCE_ROTARY_ENCODER) != 0) {
   3232                 final float axisScroll = event.getAxisValue(MotionEventCompat.AXIS_SCROLL);
   3233                 if (mLayout.canScrollVertically()) {
   3234                     // Invert the sign of the vertical scroll to align the scroll orientation
   3235                     // with AbsListView.
   3236                     vScroll = -axisScroll;
   3237                     hScroll = 0f;
   3238                 } else if (mLayout.canScrollHorizontally()) {
   3239                     vScroll = 0f;
   3240                     hScroll = axisScroll;
   3241                 } else {
   3242                     vScroll = 0f;
   3243                     hScroll = 0f;
   3244                 }
   3245             } else {
   3246                 vScroll = 0f;
   3247                 hScroll = 0f;
   3248             }
   3249 
   3250             if (vScroll != 0 || hScroll != 0) {
   3251                 scrollByInternal((int) (hScroll * mScaledHorizontalScrollFactor),
   3252                         (int) (vScroll * mScaledVerticalScrollFactor), event);
   3253             }
   3254         }
   3255         return false;
   3256     }
   3257 
   3258     @Override
   3259     protected void onMeasure(int widthSpec, int heightSpec) {
   3260         if (mLayout == null) {
   3261             defaultOnMeasure(widthSpec, heightSpec);
   3262             return;
   3263         }
   3264         if (mLayout.isAutoMeasureEnabled()) {
   3265             final int widthMode = MeasureSpec.getMode(widthSpec);
   3266             final int heightMode = MeasureSpec.getMode(heightSpec);
   3267 
   3268             /**
   3269              * This specific call should be considered deprecated and replaced with
   3270              * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
   3271              * break existing third party code but all documentation directs developers to not
   3272              * override {@link LayoutManager#onMeasure(int, int)} when
   3273              * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
   3274              */
   3275             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   3276 
   3277             final boolean measureSpecModeIsExactly =
   3278                     widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
   3279             if (measureSpecModeIsExactly || mAdapter == null) {
   3280                 return;
   3281             }
   3282 
   3283             if (mState.mLayoutStep == State.STEP_START) {
   3284                 dispatchLayoutStep1();
   3285             }
   3286             // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
   3287             // consistency
   3288             mLayout.setMeasureSpecs(widthSpec, heightSpec);
   3289             mState.mIsMeasuring = true;
   3290             dispatchLayoutStep2();
   3291 
   3292             // now we can get the width and height from the children.
   3293             mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
   3294 
   3295             // if RecyclerView has non-exact width and height and if there is at least one child
   3296             // which also has non-exact width & height, we have to re-measure.
   3297             if (mLayout.shouldMeasureTwice()) {
   3298                 mLayout.setMeasureSpecs(
   3299                         MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
   3300                         MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
   3301                 mState.mIsMeasuring = true;
   3302                 dispatchLayoutStep2();
   3303                 // now we can get the width and height from the children.
   3304                 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
   3305             }
   3306         } else {
   3307             if (mHasFixedSize) {
   3308                 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   3309                 return;
   3310             }
   3311             // custom onMeasure
   3312             if (mAdapterUpdateDuringMeasure) {
   3313                 startInterceptRequestLayout();
   3314                 onEnterLayoutOrScroll();
   3315                 processAdapterUpdatesAndSetAnimationFlags();
   3316                 onExitLayoutOrScroll();
   3317 
   3318                 if (mState.mRunPredictiveAnimations) {
   3319                     mState.mInPreLayout = true;
   3320                 } else {
   3321                     // consume remaining updates to provide a consistent state with the layout pass.
   3322                     mAdapterHelper.consumeUpdatesInOnePass();
   3323                     mState.mInPreLayout = false;
   3324                 }
   3325                 mAdapterUpdateDuringMeasure = false;
   3326                 stopInterceptRequestLayout(false);
   3327             } else if (mState.mRunPredictiveAnimations) {
   3328                 // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
   3329                 // this means there is already an onMeasure() call performed to handle the pending
   3330                 // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
   3331                 // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
   3332                 // because getViewForPosition() will crash when LM uses a child to measure.
   3333                 setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
   3334                 return;
   3335             }
   3336 
   3337             if (mAdapter != null) {
   3338                 mState.mItemCount = mAdapter.getItemCount();
   3339             } else {
   3340                 mState.mItemCount = 0;
   3341             }
   3342             startInterceptRequestLayout();
   3343             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   3344             stopInterceptRequestLayout(false);
   3345             mState.mInPreLayout = false; // clear
   3346         }
   3347     }
   3348 
   3349     /**
   3350      * An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
   3351      * where this RecyclerView is otherwise lacking better information.
   3352      */
   3353     void defaultOnMeasure(int widthSpec, int heightSpec) {
   3354         // calling LayoutManager here is not pretty but that API is already public and it is better
   3355         // than creating another method since this is internal.
   3356         final int width = LayoutManager.chooseSize(widthSpec,
   3357                 getPaddingLeft() + getPaddingRight(),
   3358                 ViewCompat.getMinimumWidth(this));
   3359         final int height = LayoutManager.chooseSize(heightSpec,
   3360                 getPaddingTop() + getPaddingBottom(),
   3361                 ViewCompat.getMinimumHeight(this));
   3362 
   3363         setMeasuredDimension(width, height);
   3364     }
   3365 
   3366     @Override
   3367     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   3368         super.onSizeChanged(w, h, oldw, oldh);
   3369         if (w != oldw || h != oldh) {
   3370             invalidateGlows();
   3371             // layout's w/h are updated during measure/layout steps.
   3372         }
   3373     }
   3374 
   3375     /**
   3376      * Sets the {@link ItemAnimator} that will handle animations involving changes
   3377      * to the items in this RecyclerView. By default, RecyclerView instantiates and
   3378      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
   3379      * enabled for the RecyclerView depends on the ItemAnimator and whether
   3380      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
   3381      * supports item animations}.
   3382      *
   3383      * @param animator The ItemAnimator being set. If null, no animations will occur
   3384      * when changes occur to the items in this RecyclerView.
   3385      */
   3386     public void setItemAnimator(@Nullable ItemAnimator animator) {
   3387         if (mItemAnimator != null) {
   3388             mItemAnimator.endAnimations();
   3389             mItemAnimator.setListener(null);
   3390         }
   3391         mItemAnimator = animator;
   3392         if (mItemAnimator != null) {
   3393             mItemAnimator.setListener(mItemAnimatorListener);
   3394         }
   3395     }
   3396 
   3397     void onEnterLayoutOrScroll() {
   3398         mLayoutOrScrollCounter++;
   3399     }
   3400 
   3401     void onExitLayoutOrScroll() {
   3402         onExitLayoutOrScroll(true);
   3403     }
   3404 
   3405     void onExitLayoutOrScroll(boolean enableChangeEvents) {
   3406         mLayoutOrScrollCounter--;
   3407         if (mLayoutOrScrollCounter < 1) {
   3408             if (DEBUG && mLayoutOrScrollCounter < 0) {
   3409                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
   3410                         + "Some calls are not matching" + exceptionLabel());
   3411             }
   3412             mLayoutOrScrollCounter = 0;
   3413             if (enableChangeEvents) {
   3414                 dispatchContentChangedIfNecessary();
   3415                 dispatchPendingImportantForAccessibilityChanges();
   3416             }
   3417         }
   3418     }
   3419 
   3420     boolean isAccessibilityEnabled() {
   3421         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
   3422     }
   3423 
   3424     private void dispatchContentChangedIfNecessary() {
   3425         final int flags = mEatenAccessibilityChangeFlags;
   3426         mEatenAccessibilityChangeFlags = 0;
   3427         if (flags != 0 && isAccessibilityEnabled()) {
   3428             final AccessibilityEvent event = AccessibilityEvent.obtain();
   3429             event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
   3430             AccessibilityEventCompat.setContentChangeTypes(event, flags);
   3431             sendAccessibilityEventUnchecked(event);
   3432         }
   3433     }
   3434 
   3435     /**
   3436      * Returns whether RecyclerView is currently computing a layout.
   3437      * <p>
   3438      * If this method returns true, it means that RecyclerView is in a lockdown state and any
   3439      * attempt to update adapter contents will result in an exception because adapter contents
   3440      * cannot be changed while RecyclerView is trying to compute the layout.
   3441      * <p>
   3442      * It is very unlikely that your code will be running during this state as it is
   3443      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
   3444      * in response to system events (touch, accessibility etc).
   3445      * <p>
   3446      * This case may happen if you have some custom logic to change adapter contents in
   3447      * response to a View callback (e.g. focus change callback) which might be triggered during a
   3448      * layout calculation. In these cases, you should just postpone the change using a Handler or a
   3449      * similar mechanism.
   3450      *
   3451      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
   3452      *         otherwise
   3453      */
   3454     public boolean isComputingLayout() {
   3455         return mLayoutOrScrollCounter > 0;
   3456     }
   3457 
   3458     /**
   3459      * Returns true if an accessibility event should not be dispatched now. This happens when an
   3460      * accessibility request arrives while RecyclerView does not have a stable state which is very
   3461      * hard to handle for a LayoutManager. Instead, this method records necessary information about
   3462      * the event and dispatches a window change event after the critical section is finished.
   3463      *
   3464      * @return True if the accessibility event should be postponed.
   3465      */
   3466     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
   3467         if (isComputingLayout()) {
   3468             int type = 0;
   3469             if (event != null) {
   3470                 type = AccessibilityEventCompat.getContentChangeTypes(event);
   3471             }
   3472             if (type == 0) {
   3473                 type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
   3474             }
   3475             mEatenAccessibilityChangeFlags |= type;
   3476             return true;
   3477         }
   3478         return false;
   3479     }
   3480 
   3481     @Override
   3482     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
   3483         if (shouldDeferAccessibilityEvent(event)) {
   3484             return;
   3485         }
   3486         super.sendAccessibilityEventUnchecked(event);
   3487     }
   3488 
   3489     /**
   3490      * Gets the current ItemAnimator for this RecyclerView. A null return value
   3491      * indicates that there is no animator and that item changes will happen without
   3492      * any animations. By default, RecyclerView instantiates and
   3493      * uses an instance of {@link DefaultItemAnimator}.
   3494      *
   3495      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
   3496      * when changes occur to the items in this RecyclerView.
   3497      */
   3498     @Nullable
   3499     public ItemAnimator getItemAnimator() {
   3500         return mItemAnimator;
   3501     }
   3502 
   3503     /**
   3504      * Post a runnable to the next frame to run pending item animations. Only the first such
   3505      * request will be posted, governed by the mPostedAnimatorRunner flag.
   3506      */
   3507     void postAnimationRunner() {
   3508         if (!mPostedAnimatorRunner && mIsAttached) {
   3509             ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
   3510             mPostedAnimatorRunner = true;
   3511         }
   3512     }
   3513 
   3514     private boolean predictiveItemAnimationsEnabled() {
   3515         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
   3516     }
   3517 
   3518     /**
   3519      * Consumes adapter updates and calculates which type of animations we want to run.
   3520      * Called in onMeasure and dispatchLayout.
   3521      * <p>
   3522      * This method may process only the pre-layout state of updates or all of them.
   3523      */
   3524     private void processAdapterUpdatesAndSetAnimationFlags() {
   3525         if (mDataSetHasChangedAfterLayout) {
   3526             // Processing these items have no value since data set changed unexpectedly.
   3527             // Instead, we just reset it.
   3528             mAdapterHelper.reset();
   3529             if (mDispatchItemsChangedEvent) {
   3530                 mLayout.onItemsChanged(this);
   3531             }
   3532         }
   3533         // simple animations are a subset of advanced animations (which will cause a
   3534         // pre-layout step)
   3535         // If layout supports predictive animations, pre-process to decide if we want to run them
   3536         if (predictiveItemAnimationsEnabled()) {
   3537             mAdapterHelper.preProcess();
   3538         } else {
   3539             mAdapterHelper.consumeUpdatesInOnePass();
   3540         }
   3541         boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
   3542         mState.mRunSimpleAnimations = mFirstLayoutComplete
   3543                 && mItemAnimator != null
   3544                 && (mDataSetHasChangedAfterLayout
   3545                 || animationTypeSupported
   3546                 || mLayout.mRequestedSimpleAnimations)
   3547                 && (!mDataSetHasChangedAfterLayout
   3548                 || mAdapter.hasStableIds());
   3549         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
   3550                 && animationTypeSupported
   3551                 && !mDataSetHasChangedAfterLayout
   3552                 && predictiveItemAnimationsEnabled();
   3553     }
   3554 
   3555     /**
   3556      * Wrapper around layoutChildren() that handles animating changes caused by layout.
   3557      * Animations work on the assumption that there are five different kinds of items
   3558      * in play:
   3559      * PERSISTENT: items are visible before and after layout
   3560      * REMOVED: items were visible before layout and were removed by the app
   3561      * ADDED: items did not exist before layout and were added by the app
   3562      * DISAPPEARING: items exist in the data set before/after, but changed from
   3563      * visible to non-visible in the process of layout (they were moved off
   3564      * screen as a side-effect of other changes)
   3565      * APPEARING: items exist in the data set before/after, but changed from
   3566      * non-visible to visible in the process of layout (they were moved on
   3567      * screen as a side-effect of other changes)
   3568      * The overall approach figures out what items exist before/after layout and
   3569      * infers one of the five above states for each of the items. Then the animations
   3570      * are set up accordingly:
   3571      * PERSISTENT views are animated via
   3572      * {@link ItemAnimator#animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3573      * DISAPPEARING views are animated via
   3574      * {@link ItemAnimator#animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3575      * APPEARING views are animated via
   3576      * {@link ItemAnimator#animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)}
   3577      * and changed views are animated via
   3578      * {@link ItemAnimator#animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)}.
   3579      */
   3580     void dispatchLayout() {
   3581         if (mAdapter == null) {
   3582             Log.e(TAG, "No adapter attached; skipping layout");
   3583             // leave the state in START
   3584             return;
   3585         }
   3586         if (mLayout == null) {
   3587             Log.e(TAG, "No layout manager attached; skipping layout");
   3588             // leave the state in START
   3589             return;
   3590         }
   3591         mState.mIsMeasuring = false;
   3592         if (mState.mLayoutStep == State.STEP_START) {
   3593             dispatchLayoutStep1();
   3594             mLayout.setExactMeasureSpecsFrom(this);
   3595             dispatchLayoutStep2();
   3596         } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
   3597                 || mLayout.getHeight() != getHeight()) {
   3598             // First 2 steps are done in onMeasure but looks like we have to run again due to
   3599             // changed size.
   3600             mLayout.setExactMeasureSpecsFrom(this);
   3601             dispatchLayoutStep2();
   3602         } else {
   3603             // always make sure we sync them (to ensure mode is exact)
   3604             mLayout.setExactMeasureSpecsFrom(this);
   3605         }
   3606         dispatchLayoutStep3();
   3607     }
   3608 
   3609     private void saveFocusInfo() {
   3610         View child = null;
   3611         if (mPreserveFocusAfterLayout && hasFocus() && mAdapter != null) {
   3612             child = getFocusedChild();
   3613         }
   3614 
   3615         final ViewHolder focusedVh = child == null ? null : findContainingViewHolder(child);
   3616         if (focusedVh == null) {
   3617             resetFocusInfo();
   3618         } else {
   3619             mState.mFocusedItemId = mAdapter.hasStableIds() ? focusedVh.getItemId() : NO_ID;
   3620             // mFocusedItemPosition should hold the current adapter position of the previously
   3621             // focused item. If the item is removed, we store the previous adapter position of the
   3622             // removed item.
   3623             mState.mFocusedItemPosition = mDataSetHasChangedAfterLayout ? NO_POSITION
   3624                     : (focusedVh.isRemoved() ? focusedVh.mOldPosition
   3625                             : focusedVh.getAdapterPosition());
   3626             mState.mFocusedSubChildId = getDeepestFocusedViewWithId(focusedVh.itemView);
   3627         }
   3628     }
   3629 
   3630     private void resetFocusInfo() {
   3631         mState.mFocusedItemId = NO_ID;
   3632         mState.mFocusedItemPosition = NO_POSITION;
   3633         mState.mFocusedSubChildId = View.NO_ID;
   3634     }
   3635 
   3636     /**
   3637      * Finds the best view candidate to request focus on using mFocusedItemPosition index of the
   3638      * previously focused item. It first traverses the adapter forward to find a focusable candidate
   3639      * and if no such candidate is found, it reverses the focus search direction for the items
   3640      * before the mFocusedItemPosition'th index;
   3641      * @return The best candidate to request focus on, or null if no such candidate exists. Null
   3642      * indicates all the existing adapter items are unfocusable.
   3643      */
   3644     @Nullable
   3645     private View findNextViewToFocus() {
   3646         int startFocusSearchIndex = mState.mFocusedItemPosition != -1 ? mState.mFocusedItemPosition
   3647                 : 0;
   3648         ViewHolder nextFocus;
   3649         final int itemCount = mState.getItemCount();
   3650         for (int i = startFocusSearchIndex; i < itemCount; i++) {
   3651             nextFocus = findViewHolderForAdapterPosition(i);
   3652             if (nextFocus == null) {
   3653                 break;
   3654             }
   3655             if (nextFocus.itemView.hasFocusable()) {
   3656                 return nextFocus.itemView;
   3657             }
   3658         }
   3659         final int limit = Math.min(itemCount, startFocusSearchIndex);
   3660         for (int i = limit - 1; i >= 0; i--) {
   3661             nextFocus = findViewHolderForAdapterPosition(i);
   3662             if (nextFocus == null) {
   3663                 return null;
   3664             }
   3665             if (nextFocus.itemView.hasFocusable()) {
   3666                 return nextFocus.itemView;
   3667             }
   3668         }
   3669         return null;
   3670     }
   3671 
   3672     private void recoverFocusFromState() {
   3673         if (!mPreserveFocusAfterLayout || mAdapter == null || !hasFocus()
   3674                 || getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS
   3675                 || (getDescendantFocusability() == FOCUS_BEFORE_DESCENDANTS && isFocused())) {
   3676             // No-op if either of these cases happens:
   3677             // 1. RV has no focus, or 2. RV blocks focus to its children, or 3. RV takes focus
   3678             // before its children and is focused (i.e. it already stole the focus away from its
   3679             // descendants).
   3680             return;
   3681         }
   3682         // only recover focus if RV itself has the focus or the focused view is hidden
   3683         if (!isFocused()) {
   3684             final View focusedChild = getFocusedChild();
   3685             if (IGNORE_DETACHED_FOCUSED_CHILD
   3686                     && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
   3687                 // Special handling of API 15-. A focused child can be invalid because mFocus is not
   3688                 // cleared when the child is detached (mParent = null),
   3689                 // This happens because clearFocus on API 15- does not invalidate mFocus of its
   3690                 // parent when this child is detached.
   3691                 // For API 16+, this is not an issue because requestFocus takes care of clearing the
   3692                 // prior detached focused child. For API 15- the problem happens in 2 cases because
   3693                 // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
   3694                 // for the current focused item which calls clearChild or 2. when the prior focused
   3695                 // child is removed, removeDetachedView called in layout step 3 which calls
   3696                 // clearChild. We should ignore this invalid focused child in all our calculations
   3697                 // for the next view to receive focus, and apply the focus recovery logic instead.
   3698                 if (mChildHelper.getChildCount() == 0) {
   3699                     // No children left. Request focus on the RV itself since one of its children
   3700                     // was holding focus previously.
   3701                     requestFocus();
   3702                     return;
   3703                 }
   3704             } else if (!mChildHelper.isHidden(focusedChild)) {
   3705                 // If the currently focused child is hidden, apply the focus recovery logic.
   3706                 // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
   3707                 return;
   3708             }
   3709         }
   3710         ViewHolder focusTarget = null;
   3711         // RV first attempts to locate the previously focused item to request focus on using
   3712         // mFocusedItemId. If such an item no longer exists, it then makes a best-effort attempt to
   3713         // find the next best candidate to request focus on based on mFocusedItemPosition.
   3714         if (mState.mFocusedItemId != NO_ID && mAdapter.hasStableIds()) {
   3715             focusTarget = findViewHolderForItemId(mState.mFocusedItemId);
   3716         }
   3717         View viewToFocus = null;
   3718         if (focusTarget == null || mChildHelper.isHidden(focusTarget.itemView)
   3719                 || !focusTarget.itemView.hasFocusable()) {
   3720             if (mChildHelper.getChildCount() > 0) {
   3721                 // At this point, RV has focus and either of these conditions are true:
   3722                 // 1. There's no previously focused item either because RV received focused before
   3723                 // layout, or the previously focused item was removed, or RV doesn't have stable IDs
   3724                 // 2. Previous focus child is hidden, or 3. Previous focused child is no longer
   3725                 // focusable. In either of these cases, we make sure that RV still passes down the
   3726                 // focus to one of its focusable children using a best-effort algorithm.
   3727                 viewToFocus = findNextViewToFocus();
   3728             }
   3729         } else {
   3730             // looks like the focused item has been replaced with another view that represents the
   3731             // same item in the adapter. Request focus on that.
   3732             viewToFocus = focusTarget.itemView;
   3733         }
   3734 
   3735         if (viewToFocus != null) {
   3736             if (mState.mFocusedSubChildId != NO_ID) {
   3737                 View child = viewToFocus.findViewById(mState.mFocusedSubChildId);
   3738                 if (child != null && child.isFocusable()) {
   3739                     viewToFocus = child;
   3740                 }
   3741             }
   3742             viewToFocus.requestFocus();
   3743         }
   3744     }
   3745 
   3746     private int getDeepestFocusedViewWithId(View view) {
   3747         int lastKnownId = view.getId();
   3748         while (!view.isFocused() && view instanceof ViewGroup && view.hasFocus()) {
   3749             view = ((ViewGroup) view).getFocusedChild();
   3750             final int id = view.getId();
   3751             if (id != View.NO_ID) {
   3752                 lastKnownId = view.getId();
   3753             }
   3754         }
   3755         return lastKnownId;
   3756     }
   3757 
   3758     final void fillRemainingScrollValues(State state) {
   3759         if (getScrollState() == SCROLL_STATE_SETTLING) {
   3760             final OverScroller scroller = mViewFlinger.mScroller;
   3761             state.mRemainingScrollHorizontal = scroller.getFinalX() - scroller.getCurrX();
   3762             state.mRemainingScrollVertical = scroller.getFinalY() - scroller.getCurrY();
   3763         } else {
   3764             state.mRemainingScrollHorizontal = 0;
   3765             state.mRemainingScrollVertical = 0;
   3766         }
   3767     }
   3768 
   3769     /**
   3770      * The first step of a layout where we;
   3771      * - process adapter updates
   3772      * - decide which animation should run
   3773      * - save information about current views
   3774      * - If necessary, run predictive layout and save its information
   3775      */
   3776     private void dispatchLayoutStep1() {
   3777         mState.assertLayoutStep(State.STEP_START);
   3778         fillRemainingScrollValues(mState);
   3779         mState.mIsMeasuring = false;
   3780         startInterceptRequestLayout();
   3781         mViewInfoStore.clear();
   3782         onEnterLayoutOrScroll();
   3783         processAdapterUpdatesAndSetAnimationFlags();
   3784         saveFocusInfo();
   3785         mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
   3786         mItemsAddedOrRemoved = mItemsChanged = false;
   3787         mState.mInPreLayout = mState.mRunPredictiveAnimations;
   3788         mState.mItemCount = mAdapter.getItemCount();
   3789         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   3790 
   3791         if (mState.mRunSimpleAnimations) {
   3792             // Step 0: Find out where all non-removed items are, pre-layout
   3793             int count = mChildHelper.getChildCount();
   3794             for (int i = 0; i < count; ++i) {
   3795                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3796                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
   3797                     continue;
   3798                 }
   3799                 final ItemHolderInfo animationInfo = mItemAnimator
   3800                         .recordPreLayoutInformation(mState, holder,
   3801                                 ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
   3802                                 holder.getUnmodifiedPayloads());
   3803                 mViewInfoStore.addToPreLayout(holder, animationInfo);
   3804                 if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
   3805                         && !holder.shouldIgnore() && !holder.isInvalid()) {
   3806                     long key = getChangedHolderKey(holder);
   3807                     // This is NOT the only place where a ViewHolder is added to old change holders
   3808                     // list. There is another case where:
   3809                     //    * A VH is currently hidden but not deleted
   3810                     //    * The hidden item is changed in the adapter
   3811                     //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
   3812                     // When this case is detected, RV will un-hide that view and add to the old
   3813                     // change holders list.
   3814                     mViewInfoStore.addToOldChangeHolders(key, holder);
   3815                 }
   3816             }
   3817         }
   3818         if (mState.mRunPredictiveAnimations) {
   3819             // Step 1: run prelayout: This will use the old positions of items. The layout manager
   3820             // is expected to layout everything, even removed items (though not to add removed
   3821             // items back to the container). This gives the pre-layout position of APPEARING views
   3822             // which come into existence as part of the real layout.
   3823 
   3824             // Save old positions so that LayoutManager can run its mapping logic.
   3825             saveOldPositions();
   3826             final boolean didStructureChange = mState.mStructureChanged;
   3827             mState.mStructureChanged = false;
   3828             // temporarily disable flag because we are asking for previous layout
   3829             mLayout.onLayoutChildren(mRecycler, mState);
   3830             mState.mStructureChanged = didStructureChange;
   3831 
   3832             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
   3833                 final View child = mChildHelper.getChildAt(i);
   3834                 final ViewHolder viewHolder = getChildViewHolderInt(child);
   3835                 if (viewHolder.shouldIgnore()) {
   3836                     continue;
   3837                 }
   3838                 if (!mViewInfoStore.isInPreLayout(viewHolder)) {
   3839                     int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
   3840                     boolean wasHidden = viewHolder
   3841                             .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   3842                     if (!wasHidden) {
   3843                         flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
   3844                     }
   3845                     final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
   3846                             mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
   3847                     if (wasHidden) {
   3848                         recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
   3849                     } else {
   3850                         mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
   3851                     }
   3852                 }
   3853             }
   3854             // we don't process disappearing list because they may re-appear in post layout pass.
   3855             clearOldPositions();
   3856         } else {
   3857             clearOldPositions();
   3858         }
   3859         onExitLayoutOrScroll();
   3860         stopInterceptRequestLayout(false);
   3861         mState.mLayoutStep = State.STEP_LAYOUT;
   3862     }
   3863 
   3864     /**
   3865      * The second layout step where we do the actual layout of the views for the final state.
   3866      * This step might be run multiple times if necessary (e.g. measure).
   3867      */
   3868     private void dispatchLayoutStep2() {
   3869         startInterceptRequestLayout();
   3870         onEnterLayoutOrScroll();
   3871         mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
   3872         mAdapterHelper.consumeUpdatesInOnePass();
   3873         mState.mItemCount = mAdapter.getItemCount();
   3874         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
   3875 
   3876         // Step 2: Run layout
   3877         mState.mInPreLayout = false;
   3878         mLayout.onLayoutChildren(mRecycler, mState);
   3879 
   3880         mState.mStructureChanged = false;
   3881         mPendingSavedState = null;
   3882 
   3883         // onLayoutChildren may have caused client code to disable item animations; re-check
   3884         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
   3885         mState.mLayoutStep = State.STEP_ANIMATIONS;
   3886         onExitLayoutOrScroll();
   3887         stopInterceptRequestLayout(false);
   3888     }
   3889 
   3890     /**
   3891      * The final step of the layout where we save the information about views for animations,
   3892      * trigger animations and do any necessary cleanup.
   3893      */
   3894     private void dispatchLayoutStep3() {
   3895         mState.assertLayoutStep(State.STEP_ANIMATIONS);
   3896         startInterceptRequestLayout();
   3897         onEnterLayoutOrScroll();
   3898         mState.mLayoutStep = State.STEP_START;
   3899         if (mState.mRunSimpleAnimations) {
   3900             // Step 3: Find out where things are now, and process change animations.
   3901             // traverse list in reverse because we may call animateChange in the loop which may
   3902             // remove the target view holder.
   3903             for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
   3904                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3905                 if (holder.shouldIgnore()) {
   3906                     continue;
   3907                 }
   3908                 long key = getChangedHolderKey(holder);
   3909                 final ItemHolderInfo animationInfo = mItemAnimator
   3910                         .recordPostLayoutInformation(mState, holder);
   3911                 ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
   3912                 if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
   3913                     // run a change animation
   3914 
   3915                     // If an Item is CHANGED but the updated version is disappearing, it creates
   3916                     // a conflicting case.
   3917                     // Since a view that is marked as disappearing is likely to be going out of
   3918                     // bounds, we run a change animation. Both views will be cleaned automatically
   3919                     // once their animations finish.
   3920                     // On the other hand, if it is the same view holder instance, we run a
   3921                     // disappearing animation instead because we are not going to rebind the updated
   3922                     // VH unless it is enforced by the layout manager.
   3923                     final boolean oldDisappearing = mViewInfoStore.isDisappearing(
   3924                             oldChangeViewHolder);
   3925                     final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
   3926                     if (oldDisappearing && oldChangeViewHolder == holder) {
   3927                         // run disappear animation instead of change
   3928                         mViewInfoStore.addToPostLayout(holder, animationInfo);
   3929                     } else {
   3930                         final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
   3931                                 oldChangeViewHolder);
   3932                         // we add and remove so that any post info is merged.
   3933                         mViewInfoStore.addToPostLayout(holder, animationInfo);
   3934                         ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
   3935                         if (preInfo == null) {
   3936                             handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
   3937                         } else {
   3938                             animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
   3939                                     oldDisappearing, newDisappearing);
   3940                         }
   3941                     }
   3942                 } else {
   3943                     mViewInfoStore.addToPostLayout(holder, animationInfo);
   3944                 }
   3945             }
   3946 
   3947             // Step 4: Process view info lists and trigger animations
   3948             mViewInfoStore.process(mViewInfoProcessCallback);
   3949         }
   3950 
   3951         mLayout.removeAndRecycleScrapInt(mRecycler);
   3952         mState.mPreviousLayoutItemCount = mState.mItemCount;
   3953         mDataSetHasChangedAfterLayout = false;
   3954         mDispatchItemsChangedEvent = false;
   3955         mState.mRunSimpleAnimations = false;
   3956 
   3957         mState.mRunPredictiveAnimations = false;
   3958         mLayout.mRequestedSimpleAnimations = false;
   3959         if (mRecycler.mChangedScrap != null) {
   3960             mRecycler.mChangedScrap.clear();
   3961         }
   3962         if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
   3963             // Initial prefetch has expanded cache, so reset until next prefetch.
   3964             // This prevents initial prefetches from expanding the cache permanently.
   3965             mLayout.mPrefetchMaxCountObserved = 0;
   3966             mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
   3967             mRecycler.updateViewCacheSize();
   3968         }
   3969 
   3970         mLayout.onLayoutCompleted(mState);
   3971         onExitLayoutOrScroll();
   3972         stopInterceptRequestLayout(false);
   3973         mViewInfoStore.clear();
   3974         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
   3975             dispatchOnScrolled(0, 0);
   3976         }
   3977         recoverFocusFromState();
   3978         resetFocusInfo();
   3979     }
   3980 
   3981     /**
   3982      * This handles the case where there is an unexpected VH missing in the pre-layout map.
   3983      * <p>
   3984      * We might be able to detect the error in the application which will help the developer to
   3985      * resolve the issue.
   3986      * <p>
   3987      * If it is not an expected error, we at least print an error to notify the developer and ignore
   3988      * the animation.
   3989      *
   3990      * https://code.google.com/p/android/issues/detail?id=193958
   3991      *
   3992      * @param key The change key
   3993      * @param holder Current ViewHolder
   3994      * @param oldChangeViewHolder Changed ViewHolder
   3995      */
   3996     private void handleMissingPreInfoForChangeError(long key,
   3997             ViewHolder holder, ViewHolder oldChangeViewHolder) {
   3998         // check if two VH have the same key, if so, print that as an error
   3999         final int childCount = mChildHelper.getChildCount();
   4000         for (int i = 0; i < childCount; i++) {
   4001             View view = mChildHelper.getChildAt(i);
   4002             ViewHolder other = getChildViewHolderInt(view);
   4003             if (other == holder) {
   4004                 continue;
   4005             }
   4006             final long otherKey = getChangedHolderKey(other);
   4007             if (otherKey == key) {
   4008                 if (mAdapter != null && mAdapter.hasStableIds()) {
   4009                     throw new IllegalStateException("Two different ViewHolders have the same stable"
   4010                             + " ID. Stable IDs in your adapter MUST BE unique and SHOULD NOT"
   4011                             + " change.\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
   4012                             + exceptionLabel());
   4013                 } else {
   4014                     throw new IllegalStateException("Two different ViewHolders have the same change"
   4015                             + " ID. This might happen due to inconsistent Adapter update events or"
   4016                             + " if the LayoutManager lays out the same View multiple times."
   4017                             + "\n ViewHolder 1:" + other + " \n View Holder 2:" + holder
   4018                             + exceptionLabel());
   4019                 }
   4020             }
   4021         }
   4022         // Very unlikely to happen but if it does, notify the developer.
   4023         Log.e(TAG, "Problem while matching changed view holders with the new"
   4024                 + "ones. The pre-layout information for the change holder " + oldChangeViewHolder
   4025                 + " cannot be found but it is necessary for " + holder + exceptionLabel());
   4026     }
   4027 
   4028     /**
   4029      * Records the animation information for a view holder that was bounced from hidden list. It
   4030      * also clears the bounce back flag.
   4031      */
   4032     void recordAnimationInfoIfBouncedHiddenView(ViewHolder viewHolder,
   4033             ItemHolderInfo animationInfo) {
   4034         // looks like this view bounced back from hidden list!
   4035         viewHolder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   4036         if (mState.mTrackOldChangeHolders && viewHolder.isUpdated()
   4037                 && !viewHolder.isRemoved() && !viewHolder.shouldIgnore()) {
   4038             long key = getChangedHolderKey(viewHolder);
   4039             mViewInfoStore.addToOldChangeHolders(key, viewHolder);
   4040         }
   4041         mViewInfoStore.addToPreLayout(viewHolder, animationInfo);
   4042     }
   4043 
   4044     private void findMinMaxChildLayoutPositions(int[] into) {
   4045         final int count = mChildHelper.getChildCount();
   4046         if (count == 0) {
   4047             into[0] = NO_POSITION;
   4048             into[1] = NO_POSITION;
   4049             return;
   4050         }
   4051         int minPositionPreLayout = Integer.MAX_VALUE;
   4052         int maxPositionPreLayout = Integer.MIN_VALUE;
   4053         for (int i = 0; i < count; ++i) {
   4054             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   4055             if (holder.shouldIgnore()) {
   4056                 continue;
   4057             }
   4058             final int pos = holder.getLayoutPosition();
   4059             if (pos < minPositionPreLayout) {
   4060                 minPositionPreLayout = pos;
   4061             }
   4062             if (pos > maxPositionPreLayout) {
   4063                 maxPositionPreLayout = pos;
   4064             }
   4065         }
   4066         into[0] = minPositionPreLayout;
   4067         into[1] = maxPositionPreLayout;
   4068     }
   4069 
   4070     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
   4071         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   4072         return mMinMaxLayoutPositions[0] != minPositionPreLayout
   4073                 || mMinMaxLayoutPositions[1] != maxPositionPreLayout;
   4074     }
   4075 
   4076     @Override
   4077     protected void removeDetachedView(View child, boolean animate) {
   4078         ViewHolder vh = getChildViewHolderInt(child);
   4079         if (vh != null) {
   4080             if (vh.isTmpDetached()) {
   4081                 vh.clearTmpDetachFlag();
   4082             } else if (!vh.shouldIgnore()) {
   4083                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
   4084                         + " is not flagged as tmp detached." + vh + exceptionLabel());
   4085             }
   4086         }
   4087 
   4088         // Clear any android.view.animation.Animation that may prevent the item from
   4089         // detaching when being removed. If a child is re-added before the
   4090         // lazy detach occurs, it will receive invalid attach/detach sequencing.
   4091         child.clearAnimation();
   4092 
   4093         dispatchChildDetached(child);
   4094         super.removeDetachedView(child, animate);
   4095     }
   4096 
   4097     /**
   4098      * Returns a unique key to be used while handling change animations.
   4099      * It might be child's position or stable id depending on the adapter type.
   4100      */
   4101     long getChangedHolderKey(ViewHolder holder) {
   4102         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
   4103     }
   4104 
   4105     void animateAppearance(@NonNull ViewHolder itemHolder,
   4106             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
   4107         itemHolder.setIsRecyclable(false);
   4108         if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
   4109             postAnimationRunner();
   4110         }
   4111     }
   4112 
   4113     void animateDisappearance(@NonNull ViewHolder holder,
   4114             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
   4115         addAnimatingView(holder);
   4116         holder.setIsRecyclable(false);
   4117         if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
   4118             postAnimationRunner();
   4119         }
   4120     }
   4121 
   4122     private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
   4123             @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
   4124             boolean oldHolderDisappearing, boolean newHolderDisappearing) {
   4125         oldHolder.setIsRecyclable(false);
   4126         if (oldHolderDisappearing) {
   4127             addAnimatingView(oldHolder);
   4128         }
   4129         if (oldHolder != newHolder) {
   4130             if (newHolderDisappearing) {
   4131                 addAnimatingView(newHolder);
   4132             }
   4133             oldHolder.mShadowedHolder = newHolder;
   4134             // old holder should disappear after animation ends
   4135             addAnimatingView(oldHolder);
   4136             mRecycler.unscrapView(oldHolder);
   4137             newHolder.setIsRecyclable(false);
   4138             newHolder.mShadowingHolder = oldHolder;
   4139         }
   4140         if (mItemAnimator.animateChange(oldHolder, newHolder, preInfo, postInfo)) {
   4141             postAnimationRunner();
   4142         }
   4143     }
   4144 
   4145     @Override
   4146     protected void onLayout(boolean changed, int l, int t, int r, int b) {
   4147         TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
   4148         dispatchLayout();
   4149         TraceCompat.endSection();
   4150         mFirstLayoutComplete = true;
   4151     }
   4152 
   4153     @Override
   4154     public void requestLayout() {
   4155         if (mInterceptRequestLayoutDepth == 0 && !mLayoutFrozen) {
   4156             super.requestLayout();
   4157         } else {
   4158             mLayoutWasDefered = true;
   4159         }
   4160     }
   4161 
   4162     void markItemDecorInsetsDirty() {
   4163         final int childCount = mChildHelper.getUnfilteredChildCount();
   4164         for (int i = 0; i < childCount; i++) {
   4165             final View child = mChildHelper.getUnfilteredChildAt(i);
   4166             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   4167         }
   4168         mRecycler.markItemDecorInsetsDirty();
   4169     }
   4170 
   4171     @Override
   4172     public void draw(Canvas c) {
   4173         super.draw(c);
   4174 
   4175         final int count = mItemDecorations.size();
   4176         for (int i = 0; i < count; i++) {
   4177             mItemDecorations.get(i).onDrawOver(c, this, mState);
   4178         }
   4179         // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
   4180         // need find children closest to edges. Not sure if it is worth the effort.
   4181         boolean needsInvalidate = false;
   4182         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
   4183             final int restore = c.save();
   4184             final int padding = mClipToPadding ? getPaddingBottom() : 0;
   4185             c.rotate(270);
   4186             c.translate(-getHeight() + padding, 0);
   4187             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
   4188             c.restoreToCount(restore);
   4189         }
   4190         if (mTopGlow != null && !mTopGlow.isFinished()) {
   4191             final int restore = c.save();
   4192             if (mClipToPadding) {
   4193                 c.translate(getPaddingLeft(), getPaddingTop());
   4194             }
   4195             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
   4196             c.restoreToCount(restore);
   4197         }
   4198         if (mRightGlow != null && !mRightGlow.isFinished()) {
   4199             final int restore = c.save();
   4200             final int width = getWidth();
   4201             final int padding = mClipToPadding ? getPaddingTop() : 0;
   4202             c.rotate(90);
   4203             c.translate(-padding, -width);
   4204             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
   4205             c.restoreToCount(restore);
   4206         }
   4207         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
   4208             final int restore = c.save();
   4209             c.rotate(180);
   4210             if (mClipToPadding) {
   4211                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
   4212             } else {
   4213                 c.translate(-getWidth(), -getHeight());
   4214             }
   4215             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
   4216             c.restoreToCount(restore);
   4217         }
   4218 
   4219         // If some views are animating, ItemDecorators are likely to move/change with them.
   4220         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
   4221         // display lists are not invalidated.
   4222         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
   4223                 && mItemAnimator.isRunning()) {
   4224             needsInvalidate = true;
   4225         }
   4226 
   4227         if (needsInvalidate) {
   4228             ViewCompat.postInvalidateOnAnimation(this);
   4229         }
   4230     }
   4231 
   4232     @Override
   4233     public void onDraw(Canvas c) {
   4234         super.onDraw(c);
   4235 
   4236         final int count = mItemDecorations.size();
   4237         for (int i = 0; i < count; i++) {
   4238             mItemDecorations.get(i).onDraw(c, this, mState);
   4239         }
   4240     }
   4241 
   4242     @Override
   4243     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
   4244         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
   4245     }
   4246 
   4247     @Override
   4248     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
   4249         if (mLayout == null) {
   4250             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
   4251         }
   4252         return mLayout.generateDefaultLayoutParams();
   4253     }
   4254 
   4255     @Override
   4256     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
   4257         if (mLayout == null) {
   4258             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
   4259         }
   4260         return mLayout.generateLayoutParams(getContext(), attrs);
   4261     }
   4262 
   4263     @Override
   4264     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
   4265         if (mLayout == null) {
   4266             throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
   4267         }
   4268         return mLayout.generateLayoutParams(p);
   4269     }
   4270 
   4271     /**
   4272      * Returns true if RecyclerView is currently running some animations.
   4273      * <p>
   4274      * If you want to be notified when animations are finished, use
   4275      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
   4276      *
   4277      * @return True if there are some item animations currently running or waiting to be started.
   4278      */
   4279     public boolean isAnimating() {
   4280         return mItemAnimator != null && mItemAnimator.isRunning();
   4281     }
   4282 
   4283     void saveOldPositions() {
   4284         final int childCount = mChildHelper.getUnfilteredChildCount();
   4285         for (int i = 0; i < childCount; i++) {
   4286             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4287             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
   4288                 throw new IllegalStateException("view holder cannot have position -1 unless it"
   4289                         + " is removed" + exceptionLabel());
   4290             }
   4291             if (!holder.shouldIgnore()) {
   4292                 holder.saveOldPosition();
   4293             }
   4294         }
   4295     }
   4296 
   4297     void clearOldPositions() {
   4298         final int childCount = mChildHelper.getUnfilteredChildCount();
   4299         for (int i = 0; i < childCount; i++) {
   4300             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4301             if (!holder.shouldIgnore()) {
   4302                 holder.clearOldPosition();
   4303             }
   4304         }
   4305         mRecycler.clearOldPositions();
   4306     }
   4307 
   4308     void offsetPositionRecordsForMove(int from, int to) {
   4309         final int childCount = mChildHelper.getUnfilteredChildCount();
   4310         final int start, end, inBetweenOffset;
   4311         if (from < to) {
   4312             start = from;
   4313             end = to;
   4314             inBetweenOffset = -1;
   4315         } else {
   4316             start = to;
   4317             end = from;
   4318             inBetweenOffset = 1;
   4319         }
   4320 
   4321         for (int i = 0; i < childCount; i++) {
   4322             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4323             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   4324                 continue;
   4325             }
   4326             if (DEBUG) {
   4327                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder "
   4328                         + holder);
   4329             }
   4330             if (holder.mPosition == from) {
   4331                 holder.offsetPosition(to - from, false);
   4332             } else {
   4333                 holder.offsetPosition(inBetweenOffset, false);
   4334             }
   4335 
   4336             mState.mStructureChanged = true;
   4337         }
   4338         mRecycler.offsetPositionRecordsForMove(from, to);
   4339         requestLayout();
   4340     }
   4341 
   4342     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
   4343         final int childCount = mChildHelper.getUnfilteredChildCount();
   4344         for (int i = 0; i < childCount; i++) {
   4345             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4346             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
   4347                 if (DEBUG) {
   4348                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder "
   4349                             + holder + " now at position " + (holder.mPosition + itemCount));
   4350                 }
   4351                 holder.offsetPosition(itemCount, false);
   4352                 mState.mStructureChanged = true;
   4353             }
   4354         }
   4355         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
   4356         requestLayout();
   4357     }
   4358 
   4359     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
   4360             boolean applyToPreLayout) {
   4361         final int positionEnd = positionStart + itemCount;
   4362         final int childCount = mChildHelper.getUnfilteredChildCount();
   4363         for (int i = 0; i < childCount; i++) {
   4364             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4365             if (holder != null && !holder.shouldIgnore()) {
   4366                 if (holder.mPosition >= positionEnd) {
   4367                     if (DEBUG) {
   4368                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
   4369                                 + " holder " + holder + " now at position "
   4370                                 + (holder.mPosition - itemCount));
   4371                     }
   4372                     holder.offsetPosition(-itemCount, applyToPreLayout);
   4373                     mState.mStructureChanged = true;
   4374                 } else if (holder.mPosition >= positionStart) {
   4375                     if (DEBUG) {
   4376                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i
   4377                                 + " holder " + holder + " now REMOVED");
   4378                     }
   4379                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
   4380                             applyToPreLayout);
   4381                     mState.mStructureChanged = true;
   4382                 }
   4383             }
   4384         }
   4385         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
   4386         requestLayout();
   4387     }
   4388 
   4389     /**
   4390      * Rebind existing views for the given range, or create as needed.
   4391      *
   4392      * @param positionStart Adapter position to start at
   4393      * @param itemCount Number of views that must explicitly be rebound
   4394      */
   4395     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
   4396         final int childCount = mChildHelper.getUnfilteredChildCount();
   4397         final int positionEnd = positionStart + itemCount;
   4398 
   4399         for (int i = 0; i < childCount; i++) {
   4400             final View child = mChildHelper.getUnfilteredChildAt(i);
   4401             final ViewHolder holder = getChildViewHolderInt(child);
   4402             if (holder == null || holder.shouldIgnore()) {
   4403                 continue;
   4404             }
   4405             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
   4406                 // We re-bind these view holders after pre-processing is complete so that
   4407                 // ViewHolders have their final positions assigned.
   4408                 holder.addFlags(ViewHolder.FLAG_UPDATE);
   4409                 holder.addChangePayload(payload);
   4410                 // lp cannot be null since we get ViewHolder from it.
   4411                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   4412             }
   4413         }
   4414         mRecycler.viewRangeUpdate(positionStart, itemCount);
   4415     }
   4416 
   4417     boolean canReuseUpdatedViewHolder(ViewHolder viewHolder) {
   4418         return mItemAnimator == null || mItemAnimator.canReuseUpdatedViewHolder(viewHolder,
   4419                 viewHolder.getUnmodifiedPayloads());
   4420     }
   4421 
   4422     /**
   4423      * Processes the fact that, as far as we can tell, the data set has completely changed.
   4424      *
   4425      * <ul>
   4426      *   <li>Once layout occurs, all attached items should be discarded or animated.
   4427      *   <li>Attached items are labeled as invalid.
   4428      *   <li>Because items may still be prefetched between a "data set completely changed"
   4429      *       event and a layout event, all cached items are discarded.
   4430      * </ul>
   4431      *
   4432      * @param dispatchItemsChanged Whether to call
   4433      * {@link LayoutManager#onItemsChanged(RecyclerView)} during measure/layout.
   4434      */
   4435     void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
   4436         mDispatchItemsChangedEvent |= dispatchItemsChanged;
   4437         mDataSetHasChangedAfterLayout = true;
   4438         markKnownViewsInvalid();
   4439     }
   4440 
   4441     /**
   4442      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
   4443      * data change event.
   4444      */
   4445     void markKnownViewsInvalid() {
   4446         final int childCount = mChildHelper.getUnfilteredChildCount();
   4447         for (int i = 0; i < childCount; i++) {
   4448             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4449             if (holder != null && !holder.shouldIgnore()) {
   4450                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   4451             }
   4452         }
   4453         markItemDecorInsetsDirty();
   4454         mRecycler.markKnownViewsInvalid();
   4455     }
   4456 
   4457     /**
   4458      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
   4459      * will trigger a {@link #requestLayout()} call.
   4460      */
   4461     public void invalidateItemDecorations() {
   4462         if (mItemDecorations.size() == 0) {
   4463             return;
   4464         }
   4465         if (mLayout != null) {
   4466             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
   4467                     + " or layout");
   4468         }
   4469         markItemDecorInsetsDirty();
   4470         requestLayout();
   4471     }
   4472 
   4473     /**
   4474      * Returns true if the RecyclerView should attempt to preserve currently focused Adapter Item's
   4475      * focus even if the View representing the Item is replaced during a layout calculation.
   4476      * <p>
   4477      * By default, this value is {@code true}.
   4478      *
   4479      * @return True if the RecyclerView will try to preserve focused Item after a layout if it loses
   4480      * focus.
   4481      *
   4482      * @see #setPreserveFocusAfterLayout(boolean)
   4483      */
   4484     public boolean getPreserveFocusAfterLayout() {
   4485         return mPreserveFocusAfterLayout;
   4486     }
   4487 
   4488     /**
   4489      * Set whether the RecyclerView should try to keep the same Item focused after a layout
   4490      * calculation or not.
   4491      * <p>
   4492      * Usually, LayoutManagers keep focused views visible before and after layout but sometimes,
   4493      * views may lose focus during a layout calculation as their state changes or they are replaced
   4494      * with another view due to type change or animation. In these cases, RecyclerView can request
   4495      * focus on the new view automatically.
   4496      *
   4497      * @param preserveFocusAfterLayout Whether RecyclerView should preserve focused Item during a
   4498      *                                 layout calculations. Defaults to true.
   4499      *
   4500      * @see #getPreserveFocusAfterLayout()
   4501      */
   4502     public void setPreserveFocusAfterLayout(boolean preserveFocusAfterLayout) {
   4503         mPreserveFocusAfterLayout = preserveFocusAfterLayout;
   4504     }
   4505 
   4506     /**
   4507      * Retrieve the {@link ViewHolder} for the given child view.
   4508      *
   4509      * @param child Child of this RecyclerView to query for its ViewHolder
   4510      * @return The child view's ViewHolder
   4511      */
   4512     public ViewHolder getChildViewHolder(@NonNull View child) {
   4513         final ViewParent parent = child.getParent();
   4514         if (parent != null && parent != this) {
   4515             throw new IllegalArgumentException("View " + child + " is not a direct child of "
   4516                     + this);
   4517         }
   4518         return getChildViewHolderInt(child);
   4519     }
   4520 
   4521     /**
   4522      * Traverses the ancestors of the given view and returns the item view that contains it and
   4523      * also a direct child of the RecyclerView. This returned view can be used to get the
   4524      * ViewHolder by calling {@link #getChildViewHolder(View)}.
   4525      *
   4526      * @param view The view that is a descendant of the RecyclerView.
   4527      *
   4528      * @return The direct child of the RecyclerView which contains the given view or null if the
   4529      * provided view is not a descendant of this RecyclerView.
   4530      *
   4531      * @see #getChildViewHolder(View)
   4532      * @see #findContainingViewHolder(View)
   4533      */
   4534     @Nullable
   4535     public View findContainingItemView(@NonNull View view) {
   4536         ViewParent parent = view.getParent();
   4537         while (parent != null && parent != this && parent instanceof View) {
   4538             view = (View) parent;
   4539             parent = view.getParent();
   4540         }
   4541         return parent == this ? view : null;
   4542     }
   4543 
   4544     /**
   4545      * Returns the ViewHolder that contains the given view.
   4546      *
   4547      * @param view The view that is a descendant of the RecyclerView.
   4548      *
   4549      * @return The ViewHolder that contains the given view or null if the provided view is not a
   4550      * descendant of this RecyclerView.
   4551      */
   4552     @Nullable
   4553     public ViewHolder findContainingViewHolder(@NonNull View view) {
   4554         View itemView = findContainingItemView(view);
   4555         return itemView == null ? null : getChildViewHolder(itemView);
   4556     }
   4557 
   4558 
   4559     static ViewHolder getChildViewHolderInt(View child) {
   4560         if (child == null) {
   4561             return null;
   4562         }
   4563         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
   4564     }
   4565 
   4566     /**
   4567      * @deprecated use {@link #getChildAdapterPosition(View)} or
   4568      * {@link #getChildLayoutPosition(View)}.
   4569      */
   4570     @Deprecated
   4571     public int getChildPosition(@NonNull View child) {
   4572         return getChildAdapterPosition(child);
   4573     }
   4574 
   4575     /**
   4576      * Return the adapter position that the given child view corresponds to.
   4577      *
   4578      * @param child Child View to query
   4579      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
   4580      */
   4581     public int getChildAdapterPosition(@NonNull View child) {
   4582         final ViewHolder holder = getChildViewHolderInt(child);
   4583         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
   4584     }
   4585 
   4586     /**
   4587      * Return the adapter position of the given child view as of the latest completed layout pass.
   4588      * <p>
   4589      * This position may not be equal to Item's adapter position if there are pending changes
   4590      * in the adapter which have not been reflected to the layout yet.
   4591      *
   4592      * @param child Child View to query
   4593      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
   4594      * the View is representing a removed item.
   4595      */
   4596     public int getChildLayoutPosition(@NonNull View child) {
   4597         final ViewHolder holder = getChildViewHolderInt(child);
   4598         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
   4599     }
   4600 
   4601     /**
   4602      * Return the stable item id that the given child view corresponds to.
   4603      *
   4604      * @param child Child View to query
   4605      * @return Item id corresponding to the given view or {@link #NO_ID}
   4606      */
   4607     public long getChildItemId(@NonNull View child) {
   4608         if (mAdapter == null || !mAdapter.hasStableIds()) {
   4609             return NO_ID;
   4610         }
   4611         final ViewHolder holder = getChildViewHolderInt(child);
   4612         return holder != null ? holder.getItemId() : NO_ID;
   4613     }
   4614 
   4615     /**
   4616      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
   4617      * {@link #findViewHolderForAdapterPosition(int)}
   4618      */
   4619     @Deprecated
   4620     @Nullable
   4621     public ViewHolder findViewHolderForPosition(int position) {
   4622         return findViewHolderForPosition(position, false);
   4623     }
   4624 
   4625     /**
   4626      * Return the ViewHolder for the item in the given position of the data set as of the latest
   4627      * layout pass.
   4628      * <p>
   4629      * This method checks only the children of RecyclerView. If the item at the given
   4630      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   4631      * <p>
   4632      * Note that when Adapter contents change, ViewHolder positions are not updated until the
   4633      * next layout calculation. If there are pending adapter updates, the return value of this
   4634      * method may not match your adapter contents. You can use
   4635      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
   4636      * <p>
   4637      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
   4638      * with the same layout position representing the same Item. In this case, the updated
   4639      * ViewHolder will be returned.
   4640      *
   4641      * @param position The position of the item in the data set of the adapter
   4642      * @return The ViewHolder at <code>position</code> or null if there is no such item
   4643      */
   4644     @Nullable
   4645     public ViewHolder findViewHolderForLayoutPosition(int position) {
   4646         return findViewHolderForPosition(position, false);
   4647     }
   4648 
   4649     /**
   4650      * Return the ViewHolder for the item in the given position of the data set. Unlike
   4651      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
   4652      * adapter changes that may not be reflected to the layout yet. On the other hand, if
   4653      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
   4654      * calculated yet, this method will return <code>null</code> since the new positions of views
   4655      * are unknown until the layout is calculated.
   4656      * <p>
   4657      * This method checks only the children of RecyclerView. If the item at the given
   4658      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   4659      * <p>
   4660      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders
   4661      * representing the same Item. In this case, the updated ViewHolder will be returned.
   4662      *
   4663      * @param position The position of the item in the data set of the adapter
   4664      * @return The ViewHolder at <code>position</code> or null if there is no such item
   4665      */
   4666     @Nullable
   4667     public ViewHolder findViewHolderForAdapterPosition(int position) {
   4668         if (mDataSetHasChangedAfterLayout) {
   4669             return null;
   4670         }
   4671         final int childCount = mChildHelper.getUnfilteredChildCount();
   4672         // hidden VHs are not preferred but if that is the only one we find, we rather return it
   4673         ViewHolder hidden = null;
   4674         for (int i = 0; i < childCount; i++) {
   4675             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4676             if (holder != null && !holder.isRemoved()
   4677                     && getAdapterPositionFor(holder) == position) {
   4678                 if (mChildHelper.isHidden(holder.itemView)) {
   4679                     hidden = holder;
   4680                 } else {
   4681                     return holder;
   4682                 }
   4683             }
   4684         }
   4685         return hidden;
   4686     }
   4687 
   4688     @Nullable
   4689     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
   4690         final int childCount = mChildHelper.getUnfilteredChildCount();
   4691         ViewHolder hidden = null;
   4692         for (int i = 0; i < childCount; i++) {
   4693             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4694             if (holder != null && !holder.isRemoved()) {
   4695                 if (checkNewPosition) {
   4696                     if (holder.mPosition != position) {
   4697                         continue;
   4698                     }
   4699                 } else if (holder.getLayoutPosition() != position) {
   4700                     continue;
   4701                 }
   4702                 if (mChildHelper.isHidden(holder.itemView)) {
   4703                     hidden = holder;
   4704                 } else {
   4705                     return holder;
   4706                 }
   4707             }
   4708         }
   4709         // This method should not query cached views. It creates a problem during adapter updates
   4710         // when we are dealing with already laid out views. Also, for the public method, it is more
   4711         // reasonable to return null if position is not laid out.
   4712         return hidden;
   4713     }
   4714 
   4715     /**
   4716      * Return the ViewHolder for the item with the given id. The RecyclerView must
   4717      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
   4718      * return a non-null value.
   4719      * <p>
   4720      * This method checks only the children of RecyclerView. If the item with the given
   4721      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
   4722      *
   4723      * When the ItemAnimator is running a change animation, there might be 2 ViewHolders with the
   4724      * same id. In this case, the updated ViewHolder will be returned.
   4725      *
   4726      * @param id The id for the requested item
   4727      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
   4728      */
   4729     public ViewHolder findViewHolderForItemId(long id) {
   4730         if (mAdapter == null || !mAdapter.hasStableIds()) {
   4731             return null;
   4732         }
   4733         final int childCount = mChildHelper.getUnfilteredChildCount();
   4734         ViewHolder hidden = null;
   4735         for (int i = 0; i < childCount; i++) {
   4736             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   4737             if (holder != null && !holder.isRemoved() && holder.getItemId() == id) {
   4738                 if (mChildHelper.isHidden(holder.itemView)) {
   4739                     hidden = holder;
   4740                 } else {
   4741                     return holder;
   4742                 }
   4743             }
   4744         }
   4745         return hidden;
   4746     }
   4747 
   4748     /**
   4749      * Find the topmost view under the given point.
   4750      *
   4751      * @param x Horizontal position in pixels to search
   4752      * @param y Vertical position in pixels to search
   4753      * @return The child view under (x, y) or null if no matching child is found
   4754      */
   4755     @Nullable
   4756     public View findChildViewUnder(float x, float y) {
   4757         final int count = mChildHelper.getChildCount();
   4758         for (int i = count - 1; i >= 0; i--) {
   4759             final View child = mChildHelper.getChildAt(i);
   4760             final float translationX = child.getTranslationX();
   4761             final float translationY = child.getTranslationY();
   4762             if (x >= child.getLeft() + translationX
   4763                     && x <= child.getRight() + translationX
   4764                     && y >= child.getTop() + translationY
   4765                     && y <= child.getBottom() + translationY) {
   4766                 return child;
   4767             }
   4768         }
   4769         return null;
   4770     }
   4771 
   4772     @Override
   4773     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
   4774         return super.drawChild(canvas, child, drawingTime);
   4775     }
   4776 
   4777     /**
   4778      * Offset the bounds of all child views by <code>dy</code> pixels.
   4779      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   4780      *
   4781      * @param dy Vertical pixel offset to apply to the bounds of all child views
   4782      */
   4783     public void offsetChildrenVertical(@Px int dy) {
   4784         final int childCount = mChildHelper.getChildCount();
   4785         for (int i = 0; i < childCount; i++) {
   4786             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
   4787         }
   4788     }
   4789 
   4790     /**
   4791      * Called when an item view is attached to this RecyclerView.
   4792      *
   4793      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   4794      * of child views as they become attached. This will be called before a
   4795      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
   4796      * changes.</p>
   4797      *
   4798      * @param child Child view that is now attached to this RecyclerView and its associated window
   4799      */
   4800     public void onChildAttachedToWindow(@NonNull View child) {
   4801     }
   4802 
   4803     /**
   4804      * Called when an item view is detached from this RecyclerView.
   4805      *
   4806      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   4807      * of child views as they become detached. This will be called as a
   4808      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
   4809      *
   4810      * @param child Child view that is now detached from this RecyclerView and its associated window
   4811      */
   4812     public void onChildDetachedFromWindow(@NonNull View child) {
   4813     }
   4814 
   4815     /**
   4816      * Offset the bounds of all child views by <code>dx</code> pixels.
   4817      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   4818      *
   4819      * @param dx Horizontal pixel offset to apply to the bounds of all child views
   4820      */
   4821     public void offsetChildrenHorizontal(@Px int dx) {
   4822         final int childCount = mChildHelper.getChildCount();
   4823         for (int i = 0; i < childCount; i++) {
   4824             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
   4825         }
   4826     }
   4827 
   4828     /**
   4829      * Returns the bounds of the view including its decoration and margins.
   4830      *
   4831      * @param view The view element to check
   4832      * @param outBounds A rect that will receive the bounds of the element including its
   4833      *                  decoration and margins.
   4834      */
   4835     public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
   4836         getDecoratedBoundsWithMarginsInt(view, outBounds);
   4837     }
   4838 
   4839     static void getDecoratedBoundsWithMarginsInt(View view, Rect outBounds) {
   4840         final LayoutParams lp = (LayoutParams) view.getLayoutParams();
   4841         final Rect insets = lp.mDecorInsets;
   4842         outBounds.set(view.getLeft() - insets.left - lp.leftMargin,
   4843                 view.getTop() - insets.top - lp.topMargin,
   4844                 view.getRight() + insets.right + lp.rightMargin,
   4845                 view.getBottom() + insets.bottom + lp.bottomMargin);
   4846     }
   4847 
   4848     Rect getItemDecorInsetsForChild(View child) {
   4849         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   4850         if (!lp.mInsetsDirty) {
   4851             return lp.mDecorInsets;
   4852         }
   4853 
   4854         if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
   4855             // changed/invalid items should not be updated until they are rebound.
   4856             return lp.mDecorInsets;
   4857         }
   4858         final Rect insets = lp.mDecorInsets;
   4859         insets.set(0, 0, 0, 0);
   4860         final int decorCount = mItemDecorations.size();
   4861         for (int i = 0; i < decorCount; i++) {
   4862             mTempRect.set(0, 0, 0, 0);
   4863             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
   4864             insets.left += mTempRect.left;
   4865             insets.top += mTempRect.top;
   4866             insets.right += mTempRect.right;
   4867             insets.bottom += mTempRect.bottom;
   4868         }
   4869         lp.mInsetsDirty = false;
   4870         return insets;
   4871     }
   4872 
   4873     /**
   4874      * Called when the scroll position of this RecyclerView changes. Subclasses should use
   4875      * this method to respond to scrolling within the adapter's data set instead of an explicit
   4876      * listener.
   4877      *
   4878      * <p>This method will always be invoked before listeners. If a subclass needs to perform
   4879      * any additional upkeep or bookkeeping after scrolling but before listeners run,
   4880      * this is a good place to do so.</p>
   4881      *
   4882      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
   4883      * the distance scrolled in either direction within the adapter's data set instead of absolute
   4884      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
   4885      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
   4886      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
   4887      * do not correspond to the data set scroll position. However, some subclasses may choose
   4888      * to use these fields as special offsets.</p>
   4889      *
   4890      * @param dx horizontal distance scrolled in pixels
   4891      * @param dy vertical distance scrolled in pixels
   4892      */
   4893     public void onScrolled(@Px int dx, @Px int dy) {
   4894         // Do nothing
   4895     }
   4896 
   4897     void dispatchOnScrolled(int hresult, int vresult) {
   4898         mDispatchScrollCounter++;
   4899         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
   4900         // but some general-purpose code may choose to respond to changes this way.
   4901         final int scrollX = getScrollX();
   4902         final int scrollY = getScrollY();
   4903         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
   4904 
   4905         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
   4906         onScrolled(hresult, vresult);
   4907 
   4908         // Invoke listeners last. Subclassed view methods always handle the event first.
   4909         // All internal state is consistent by the time listeners are invoked.
   4910         if (mScrollListener != null) {
   4911             mScrollListener.onScrolled(this, hresult, vresult);
   4912         }
   4913         if (mScrollListeners != null) {
   4914             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   4915                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
   4916             }
   4917         }
   4918         mDispatchScrollCounter--;
   4919     }
   4920 
   4921     /**
   4922      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
   4923      * method to respond to state changes instead of an explicit listener.
   4924      *
   4925      * <p>This method will always be invoked before listeners, but after the LayoutManager
   4926      * responds to the scroll state change.</p>
   4927      *
   4928      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
   4929      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
   4930      */
   4931     public void onScrollStateChanged(int state) {
   4932         // Do nothing
   4933     }
   4934 
   4935     void dispatchOnScrollStateChanged(int state) {
   4936         // Let the LayoutManager go first; this allows it to bring any properties into
   4937         // a consistent state before the RecyclerView subclass responds.
   4938         if (mLayout != null) {
   4939             mLayout.onScrollStateChanged(state);
   4940         }
   4941 
   4942         // Let the RecyclerView subclass handle this event next; any LayoutManager property
   4943         // changes will be reflected by this time.
   4944         onScrollStateChanged(state);
   4945 
   4946         // Listeners go last. All other internal state is consistent by this point.
   4947         if (mScrollListener != null) {
   4948             mScrollListener.onScrollStateChanged(this, state);
   4949         }
   4950         if (mScrollListeners != null) {
   4951             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   4952                 mScrollListeners.get(i).onScrollStateChanged(this, state);
   4953             }
   4954         }
   4955     }
   4956 
   4957     /**
   4958      * Returns whether there are pending adapter updates which are not yet applied to the layout.
   4959      * <p>
   4960      * If this method returns <code>true</code>, it means that what user is currently seeing may not
   4961      * reflect them adapter contents (depending on what has changed).
   4962      * You may use this information to defer or cancel some operations.
   4963      * <p>
   4964      * This method returns true if RecyclerView has not yet calculated the first layout after it is
   4965      * attached to the Window or the Adapter has been replaced.
   4966      *
   4967      * @return True if there are some adapter updates which are not yet reflected to layout or false
   4968      * if layout is up to date.
   4969      */
   4970     public boolean hasPendingAdapterUpdates() {
   4971         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
   4972                 || mAdapterHelper.hasPendingUpdates();
   4973     }
   4974 
   4975     class ViewFlinger implements Runnable {
   4976         private int mLastFlingX;
   4977         private int mLastFlingY;
   4978         private OverScroller mScroller;
   4979         Interpolator mInterpolator = sQuinticInterpolator;
   4980 
   4981         // When set to true, postOnAnimation callbacks are delayed until the run method completes
   4982         private boolean mEatRunOnAnimationRequest = false;
   4983 
   4984         // Tracks if postAnimationCallback should be re-attached when it is done
   4985         private boolean mReSchedulePostAnimationCallback = false;
   4986 
   4987         ViewFlinger() {
   4988             mScroller = new OverScroller(getContext(), sQuinticInterpolator);
   4989         }
   4990 
   4991         @Override
   4992         public void run() {
   4993             if (mLayout == null) {
   4994                 stop();
   4995                 return; // no layout, cannot scroll.
   4996             }
   4997             disableRunOnAnimationRequests();
   4998             consumePendingUpdateOperations();
   4999             // keep a local reference so that if it is changed during onAnimation method, it won't
   5000             // cause unexpected behaviors
   5001             final OverScroller scroller = mScroller;
   5002             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
   5003             if (scroller.computeScrollOffset()) {
   5004                 final int[] scrollConsumed = mScrollConsumed;
   5005                 final int x = scroller.getCurrX();
   5006                 final int y = scroller.getCurrY();
   5007                 int dx = x - mLastFlingX;
   5008                 int dy = y - mLastFlingY;
   5009                 int hresult = 0;
   5010                 int vresult = 0;
   5011                 mLastFlingX = x;
   5012                 mLastFlingY = y;
   5013                 int overscrollX = 0, overscrollY = 0;
   5014 
   5015                 if (dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {
   5016                     dx -= scrollConsumed[0];
   5017                     dy -= scrollConsumed[1];
   5018                 }
   5019 
   5020                 if (mAdapter != null) {
   5021                     scrollStep(dx, dy, mScrollStepConsumed);
   5022                     hresult = mScrollStepConsumed[0];
   5023                     vresult = mScrollStepConsumed[1];
   5024                     overscrollX = dx - hresult;
   5025                     overscrollY = dy - vresult;
   5026 
   5027                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun()
   5028                             && smoothScroller.isRunning()) {
   5029                         final int adapterSize = mState.getItemCount();
   5030                         if (adapterSize == 0) {
   5031                             smoothScroller.stop();
   5032                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
   5033                             smoothScroller.setTargetPosition(adapterSize - 1);
   5034                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   5035                         } else {
   5036                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   5037                         }
   5038                     }
   5039                 }
   5040                 if (!mItemDecorations.isEmpty()) {
   5041                     invalidate();
   5042                 }
   5043                 if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   5044                     considerReleasingGlowsOnScroll(dx, dy);
   5045                 }
   5046 
   5047                 if (!dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null,
   5048                         TYPE_NON_TOUCH)
   5049                         && (overscrollX != 0 || overscrollY != 0)) {
   5050                     final int vel = (int) scroller.getCurrVelocity();
   5051 
   5052                     int velX = 0;
   5053                     if (overscrollX != x) {
   5054                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
   5055                     }
   5056 
   5057                     int velY = 0;
   5058                     if (overscrollY != y) {
   5059                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
   5060                     }
   5061 
   5062                     if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
   5063                         absorbGlows(velX, velY);
   5064                     }
   5065                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0)
   5066                             && (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
   5067                         scroller.abortAnimation();
   5068                     }
   5069                 }
   5070                 if (hresult != 0 || vresult != 0) {
   5071                     dispatchOnScrolled(hresult, vresult);
   5072                 }
   5073 
   5074                 if (!awakenScrollBars()) {
   5075                     invalidate();
   5076                 }
   5077 
   5078                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
   5079                         && vresult == dy;
   5080                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
   5081                         && hresult == dx;
   5082                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
   5083                         || fullyConsumedVertical;
   5084 
   5085                 if (scroller.isFinished() || (!fullyConsumedAny
   5086                         && !hasNestedScrollingParent(TYPE_NON_TOUCH))) {
   5087                     // setting state to idle will stop this.
   5088                     setScrollState(SCROLL_STATE_IDLE);
   5089                     if (ALLOW_THREAD_GAP_WORK) {
   5090                         mPrefetchRegistry.clearPrefetchPositions();
   5091                     }
   5092                     stopNestedScroll(TYPE_NON_TOUCH);
   5093                 } else {
   5094                     postOnAnimation();
   5095                     if (mGapWorker != null) {
   5096                         mGapWorker.postFromTraversal(RecyclerView.this, dx, dy);
   5097                     }
   5098                 }
   5099             }
   5100             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
   5101             if (smoothScroller != null) {
   5102                 if (smoothScroller.isPendingInitialRun()) {
   5103                     smoothScroller.onAnimation(0, 0);
   5104                 }
   5105                 if (!mReSchedulePostAnimationCallback) {
   5106                     smoothScroller.stop(); //stop if it does not trigger any scroll
   5107                 }
   5108             }
   5109             enableRunOnAnimationRequests();
   5110         }
   5111 
   5112         private void disableRunOnAnimationRequests() {
   5113             mReSchedulePostAnimationCallback = false;
   5114             mEatRunOnAnimationRequest = true;
   5115         }
   5116 
   5117         private void enableRunOnAnimationRequests() {
   5118             mEatRunOnAnimationRequest = false;
   5119             if (mReSchedulePostAnimationCallback) {
   5120                 postOnAnimation();
   5121             }
   5122         }
   5123 
   5124         void postOnAnimation() {
   5125             if (mEatRunOnAnimationRequest) {
   5126                 mReSchedulePostAnimationCallback = true;
   5127             } else {
   5128                 removeCallbacks(this);
   5129                 ViewCompat.postOnAnimation(RecyclerView.this, this);
   5130             }
   5131         }
   5132 
   5133         public void fling(int velocityX, int velocityY) {
   5134             setScrollState(SCROLL_STATE_SETTLING);
   5135             mLastFlingX = mLastFlingY = 0;
   5136             mScroller.fling(0, 0, velocityX, velocityY,
   5137                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
   5138             postOnAnimation();
   5139         }
   5140 
   5141         public void smoothScrollBy(int dx, int dy) {
   5142             smoothScrollBy(dx, dy, 0, 0);
   5143         }
   5144 
   5145         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
   5146             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
   5147         }
   5148 
   5149         private float distanceInfluenceForSnapDuration(float f) {
   5150             f -= 0.5f; // center the values about 0.
   5151             f *= 0.3f * (float) Math.PI / 2.0f;
   5152             return (float) Math.sin(f);
   5153         }
   5154 
   5155         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
   5156             final int absDx = Math.abs(dx);
   5157             final int absDy = Math.abs(dy);
   5158             final boolean horizontal = absDx > absDy;
   5159             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
   5160             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
   5161             final int containerSize = horizontal ? getWidth() : getHeight();
   5162             final int halfContainerSize = containerSize / 2;
   5163             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
   5164             final float distance = halfContainerSize + halfContainerSize
   5165                     * distanceInfluenceForSnapDuration(distanceRatio);
   5166 
   5167             final int duration;
   5168             if (velocity > 0) {
   5169                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
   5170             } else {
   5171                 float absDelta = (float) (horizontal ? absDx : absDy);
   5172                 duration = (int) (((absDelta / containerSize) + 1) * 300);
   5173             }
   5174             return Math.min(duration, MAX_SCROLL_DURATION);
   5175         }
   5176 
   5177         public void smoothScrollBy(int dx, int dy, int duration) {
   5178             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
   5179         }
   5180 
   5181         public void smoothScrollBy(int dx, int dy, Interpolator interpolator) {
   5182             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, 0, 0),
   5183                     interpolator == null ? sQuinticInterpolator : interpolator);
   5184         }
   5185 
   5186         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
   5187             if (mInterpolator != interpolator) {
   5188                 mInterpolator = interpolator;
   5189                 mScroller = new OverScroller(getContext(), interpolator);
   5190             }
   5191             setScrollState(SCROLL_STATE_SETTLING);
   5192             mLastFlingX = mLastFlingY = 0;
   5193             mScroller.startScroll(0, 0, dx, dy, duration);
   5194             if (Build.VERSION.SDK_INT < 23) {
   5195                 // b/64931938 before API 23, startScroll() does not reset getCurX()/getCurY()
   5196                 // to start values, which causes fillRemainingScrollValues() put in obsolete values
   5197                 // for LayoutManager.onLayoutChildren().
   5198                 mScroller.computeScrollOffset();
   5199             }
   5200             postOnAnimation();
   5201         }
   5202 
   5203         public void stop() {
   5204             removeCallbacks(this);
   5205             mScroller.abortAnimation();
   5206         }
   5207 
   5208     }
   5209 
   5210     void repositionShadowingViews() {
   5211         // Fix up shadow views used by change animations
   5212         int count = mChildHelper.getChildCount();
   5213         for (int i = 0; i < count; i++) {
   5214             View view = mChildHelper.getChildAt(i);
   5215             ViewHolder holder = getChildViewHolder(view);
   5216             if (holder != null && holder.mShadowingHolder != null) {
   5217                 View shadowingView = holder.mShadowingHolder.itemView;
   5218                 int left = view.getLeft();
   5219                 int top = view.getTop();
   5220                 if (left != shadowingView.getLeft() ||  top != shadowingView.getTop()) {
   5221                     shadowingView.layout(left, top,
   5222                             left + shadowingView.getWidth(),
   5223                             top + shadowingView.getHeight());
   5224                 }
   5225             }
   5226         }
   5227     }
   5228 
   5229     private class RecyclerViewDataObserver extends AdapterDataObserver {
   5230         RecyclerViewDataObserver() {
   5231         }
   5232 
   5233         @Override
   5234         public void onChanged() {
   5235             assertNotInLayoutOrScroll(null);
   5236             mState.mStructureChanged = true;
   5237 
   5238             processDataSetCompletelyChanged(true);
   5239             if (!mAdapterHelper.hasPendingUpdates()) {
   5240                 requestLayout();
   5241             }
   5242         }
   5243 
   5244         @Override
   5245         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   5246             assertNotInLayoutOrScroll(null);
   5247             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
   5248                 triggerUpdateProcessor();
   5249             }
   5250         }
   5251 
   5252         @Override
   5253         public void onItemRangeInserted(int positionStart, int itemCount) {
   5254             assertNotInLayoutOrScroll(null);
   5255             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
   5256                 triggerUpdateProcessor();
   5257             }
   5258         }
   5259 
   5260         @Override
   5261         public void onItemRangeRemoved(int positionStart, int itemCount) {
   5262             assertNotInLayoutOrScroll(null);
   5263             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
   5264                 triggerUpdateProcessor();
   5265             }
   5266         }
   5267 
   5268         @Override
   5269         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   5270             assertNotInLayoutOrScroll(null);
   5271             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
   5272                 triggerUpdateProcessor();
   5273             }
   5274         }
   5275 
   5276         void triggerUpdateProcessor() {
   5277             if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
   5278                 ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
   5279             } else {
   5280                 mAdapterUpdateDuringMeasure = true;
   5281                 requestLayout();
   5282             }
   5283         }
   5284     }
   5285 
   5286     /**
   5287      * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
   5288      *
   5289      * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
   5290      */
   5291     public static class EdgeEffectFactory {
   5292 
   5293         @Retention(RetentionPolicy.SOURCE)
   5294         @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
   5295         public @interface EdgeDirection {}
   5296 
   5297         /**
   5298          * Direction constant for the left edge
   5299          */
   5300         public static final int DIRECTION_LEFT = 0;
   5301 
   5302         /**
   5303          * Direction constant for the top edge
   5304          */
   5305         public static final int DIRECTION_TOP = 1;
   5306 
   5307         /**
   5308          * Direction constant for the right edge
   5309          */
   5310         public static final int DIRECTION_RIGHT = 2;
   5311 
   5312         /**
   5313          * Direction constant for the bottom edge
   5314          */
   5315         public static final int DIRECTION_BOTTOM = 3;
   5316 
   5317         /**
   5318          * Create a new EdgeEffect for the provided direction.
   5319          */
   5320         protected @NonNull EdgeEffect createEdgeEffect(@NonNull RecyclerView view,
   5321                 @EdgeDirection int direction) {
   5322             return new EdgeEffect(view.getContext());
   5323         }
   5324     }
   5325 
   5326     /**
   5327      * RecycledViewPool lets you share Views between multiple RecyclerViews.
   5328      * <p>
   5329      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
   5330      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
   5331      * <p>
   5332      * RecyclerView automatically creates a pool for itself if you don't provide one.
   5333      */
   5334     public static class RecycledViewPool {
   5335         private static final int DEFAULT_MAX_SCRAP = 5;
   5336 
   5337         /**
   5338          * Tracks both pooled holders, as well as create/bind timing metadata for the given type.
   5339          *
   5340          * Note that this tracks running averages of create/bind time across all RecyclerViews
   5341          * (and, indirectly, Adapters) that use this pool.
   5342          *
   5343          * 1) This enables us to track average create and bind times across multiple adapters. Even
   5344          * though create (and especially bind) may behave differently for different Adapter
   5345          * subclasses, sharing the pool is a strong signal that they'll perform similarly, per type.
   5346          *
   5347          * 2) If {@link #willBindInTime(int, long, long)} returns false for one view, it will return
   5348          * false for all other views of its type for the same deadline. This prevents items
   5349          * constructed by {@link GapWorker} prefetch from being bound to a lower priority prefetch.
   5350          */
   5351         static class ScrapData {
   5352             final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
   5353             int mMaxScrap = DEFAULT_MAX_SCRAP;
   5354             long mCreateRunningAverageNs = 0;
   5355             long mBindRunningAverageNs = 0;
   5356         }
   5357         SparseArray<ScrapData> mScrap = new SparseArray<>();
   5358 
   5359         private int mAttachCount = 0;
   5360 
   5361         /**
   5362          * Discard all ViewHolders.
   5363          */
   5364         public void clear() {
   5365             for (int i = 0; i < mScrap.size(); i++) {
   5366                 ScrapData data = mScrap.valueAt(i);
   5367                 data.mScrapHeap.clear();
   5368             }
   5369         }
   5370 
   5371         /**
   5372          * Sets the maximum number of ViewHolders to hold in the pool before discarding.
   5373          *
   5374          * @param viewType ViewHolder Type
   5375          * @param max Maximum number
   5376          */
   5377         public void setMaxRecycledViews(int viewType, int max) {
   5378             ScrapData scrapData = getScrapDataForType(viewType);
   5379             scrapData.mMaxScrap = max;
   5380             final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
   5381             while (scrapHeap.size() > max) {
   5382                 scrapHeap.remove(scrapHeap.size() - 1);
   5383             }
   5384         }
   5385 
   5386         /**
   5387          * Returns the current number of Views held by the RecycledViewPool of the given view type.
   5388          */
   5389         public int getRecycledViewCount(int viewType) {
   5390             return getScrapDataForType(viewType).mScrapHeap.size();
   5391         }
   5392 
   5393         /**
   5394          * Acquire a ViewHolder of the specified type from the pool, or {@code null} if none are
   5395          * present.
   5396          *
   5397          * @param viewType ViewHolder type.
   5398          * @return ViewHolder of the specified type acquired from the pool, or {@code null} if none
   5399          * are present.
   5400          */
   5401         @Nullable
   5402         public ViewHolder getRecycledView(int viewType) {
   5403             final ScrapData scrapData = mScrap.get(viewType);
   5404             if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
   5405                 final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
   5406                 return scrapHeap.remove(scrapHeap.size() - 1);
   5407             }
   5408             return null;
   5409         }
   5410 
   5411         /**
   5412          * Total number of ViewHolders held by the pool.
   5413          *
   5414          * @return Number of ViewHolders held by the pool.
   5415          */
   5416         int size() {
   5417             int count = 0;
   5418             for (int i = 0; i < mScrap.size(); i++) {
   5419                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
   5420                 if (viewHolders != null) {
   5421                     count += viewHolders.size();
   5422                 }
   5423             }
   5424             return count;
   5425         }
   5426 
   5427         /**
   5428          * Add a scrap ViewHolder to the pool.
   5429          * <p>
   5430          * If the pool is already full for that ViewHolder's type, it will be immediately discarded.
   5431          *
   5432          * @param scrap ViewHolder to be added to the pool.
   5433          */
   5434         public void putRecycledView(ViewHolder scrap) {
   5435             final int viewType = scrap.getItemViewType();
   5436             final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
   5437             if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
   5438                 return;
   5439             }
   5440             if (DEBUG && scrapHeap.contains(scrap)) {
   5441                 throw new IllegalArgumentException("this scrap item already exists");
   5442             }
   5443             scrap.resetInternal();
   5444             scrapHeap.add(scrap);
   5445         }
   5446 
   5447         long runningAverage(long oldAverage, long newValue) {
   5448             if (oldAverage == 0) {
   5449                 return newValue;
   5450             }
   5451             return (oldAverage / 4 * 3) + (newValue / 4);
   5452         }
   5453 
   5454         void factorInCreateTime(int viewType, long createTimeNs) {
   5455             ScrapData scrapData = getScrapDataForType(viewType);
   5456             scrapData.mCreateRunningAverageNs = runningAverage(
   5457                     scrapData.mCreateRunningAverageNs, createTimeNs);
   5458         }
   5459 
   5460         void factorInBindTime(int viewType, long bindTimeNs) {
   5461             ScrapData scrapData = getScrapDataForType(viewType);
   5462             scrapData.mBindRunningAverageNs = runningAverage(
   5463                     scrapData.mBindRunningAverageNs, bindTimeNs);
   5464         }
   5465 
   5466         boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
   5467             long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
   5468             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
   5469         }
   5470 
   5471         boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
   5472             long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
   5473             return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
   5474         }
   5475 
   5476         void attach() {
   5477             mAttachCount++;
   5478         }
   5479 
   5480         void detach() {
   5481             mAttachCount--;
   5482         }
   5483 
   5484 
   5485         /**
   5486          * Detaches the old adapter and attaches the new one.
   5487          * <p>
   5488          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
   5489          * adapter uses a different ViewHolder than the oldAdapter.
   5490          *
   5491          * @param oldAdapter The previous adapter instance. Will be detached.
   5492          * @param newAdapter The new adapter instance. Will be attached.
   5493          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
   5494          *                               ViewHolder and view types.
   5495          */
   5496         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   5497                 boolean compatibleWithPrevious) {
   5498             if (oldAdapter != null) {
   5499                 detach();
   5500             }
   5501             if (!compatibleWithPrevious && mAttachCount == 0) {
   5502                 clear();
   5503             }
   5504             if (newAdapter != null) {
   5505                 attach();
   5506             }
   5507         }
   5508 
   5509         private ScrapData getScrapDataForType(int viewType) {
   5510             ScrapData scrapData = mScrap.get(viewType);
   5511             if (scrapData == null) {
   5512                 scrapData = new ScrapData();
   5513                 mScrap.put(viewType, scrapData);
   5514             }
   5515             return scrapData;
   5516         }
   5517     }
   5518 
   5519     /**
   5520      * Utility method for finding an internal RecyclerView, if present
   5521      */
   5522     @Nullable
   5523     static RecyclerView findNestedRecyclerView(@NonNull View view) {
   5524         if (!(view instanceof ViewGroup)) {
   5525             return null;
   5526         }
   5527         if (view instanceof RecyclerView) {
   5528             return (RecyclerView) view;
   5529         }
   5530         final ViewGroup parent = (ViewGroup) view;
   5531         final int count = parent.getChildCount();
   5532         for (int i = 0; i < count; i++) {
   5533             final View child = parent.getChildAt(i);
   5534             final RecyclerView descendant = findNestedRecyclerView(child);
   5535             if (descendant != null) {
   5536                 return descendant;
   5537             }
   5538         }
   5539         return null;
   5540     }
   5541 
   5542     /**
   5543      * Utility method for clearing holder's internal RecyclerView, if present
   5544      */
   5545     static void clearNestedRecyclerViewIfNotNested(@NonNull ViewHolder holder) {
   5546         if (holder.mNestedRecyclerView != null) {
   5547             View item = holder.mNestedRecyclerView.get();
   5548             while (item != null) {
   5549                 if (item == holder.itemView) {
   5550                     return; // match found, don't need to clear
   5551                 }
   5552 
   5553                 ViewParent parent = item.getParent();
   5554                 if (parent instanceof View) {
   5555                     item = (View) parent;
   5556                 } else {
   5557                     item = null;
   5558                 }
   5559             }
   5560             holder.mNestedRecyclerView = null; // not nested
   5561         }
   5562     }
   5563 
   5564     /**
   5565      * Time base for deadline-aware work scheduling. Overridable for testing.
   5566      *
   5567      * Will return 0 to avoid cost of System.nanoTime where deadline-aware work scheduling
   5568      * isn't relevant.
   5569      */
   5570     long getNanoTime() {
   5571         if (ALLOW_THREAD_GAP_WORK) {
   5572             return System.nanoTime();
   5573         } else {
   5574             return 0;
   5575         }
   5576     }
   5577 
   5578     /**
   5579      * A Recycler is responsible for managing scrapped or detached item views for reuse.
   5580      *
   5581      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
   5582      * that has been marked for removal or reuse.</p>
   5583      *
   5584      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
   5585      * an adapter's data set representing the data at a given position or item ID.
   5586      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
   5587      * If not, the view can be quickly reused by the LayoutManager with no further work.
   5588      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
   5589      * may be repositioned by a LayoutManager without remeasurement.</p>
   5590      */
   5591     public final class Recycler {
   5592         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
   5593         ArrayList<ViewHolder> mChangedScrap = null;
   5594 
   5595         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
   5596 
   5597         private final List<ViewHolder>
   5598                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
   5599 
   5600         private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
   5601         int mViewCacheMax = DEFAULT_CACHE_SIZE;
   5602 
   5603         RecycledViewPool mRecyclerPool;
   5604 
   5605         private ViewCacheExtension mViewCacheExtension;
   5606 
   5607         static final int DEFAULT_CACHE_SIZE = 2;
   5608 
   5609         /**
   5610          * Clear scrap views out of this recycler. Detached views contained within a
   5611          * recycled view pool will remain.
   5612          */
   5613         public void clear() {
   5614             mAttachedScrap.clear();
   5615             recycleAndClearCachedViews();
   5616         }
   5617 
   5618         /**
   5619          * Set the maximum number of detached, valid views we should retain for later use.
   5620          *
   5621          * @param viewCount Number of views to keep before sending views to the shared pool
   5622          */
   5623         public void setViewCacheSize(int viewCount) {
   5624             mRequestedCacheMax = viewCount;
   5625             updateViewCacheSize();
   5626         }
   5627 
   5628         void updateViewCacheSize() {
   5629             int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
   5630             mViewCacheMax = mRequestedCacheMax + extraCache;
   5631 
   5632             // first, try the views that can be recycled
   5633             for (int i = mCachedViews.size() - 1;
   5634                     i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
   5635                 recycleCachedViewAt(i);
   5636             }
   5637         }
   5638 
   5639         /**
   5640          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
   5641          *
   5642          * @return List of ViewHolders in the scrap list.
   5643          */
   5644         @NonNull
   5645         public List<ViewHolder> getScrapList() {
   5646             return mUnmodifiableAttachedScrap;
   5647         }
   5648 
   5649         /**
   5650          * Helper method for getViewForPosition.
   5651          * <p>
   5652          * Checks whether a given view holder can be used for the provided position.
   5653          *
   5654          * @param holder ViewHolder
   5655          * @return true if ViewHolder matches the provided position, false otherwise
   5656          */
   5657         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
   5658             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
   5659             // if it is not removed, verify the type and id.
   5660             if (holder.isRemoved()) {
   5661                 if (DEBUG && !mState.isPreLayout()) {
   5662                     throw new IllegalStateException("should not receive a removed view unless it"
   5663                             + " is pre layout" + exceptionLabel());
   5664                 }
   5665                 return mState.isPreLayout();
   5666             }
   5667             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
   5668                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
   5669                         + "adapter position" + holder + exceptionLabel());
   5670             }
   5671             if (!mState.isPreLayout()) {
   5672                 // don't check type if it is pre-layout.
   5673                 final int type = mAdapter.getItemViewType(holder.mPosition);
   5674                 if (type != holder.getItemViewType()) {
   5675                     return false;
   5676                 }
   5677             }
   5678             if (mAdapter.hasStableIds()) {
   5679                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
   5680             }
   5681             return true;
   5682         }
   5683 
   5684         /**
   5685          * Attempts to bind view, and account for relevant timing information. If
   5686          * deadlineNs != FOREVER_NS, this method may fail to bind, and return false.
   5687          *
   5688          * @param holder Holder to be bound.
   5689          * @param offsetPosition Position of item to be bound.
   5690          * @param position Pre-layout position of item to be bound.
   5691          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
   5692          *                   complete. If FOREVER_NS is passed, this method will not fail to
   5693          *                   bind the holder.
   5694          * @return
   5695          */
   5696         private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
   5697                 int position, long deadlineNs) {
   5698             holder.mOwnerRecyclerView = RecyclerView.this;
   5699             final int viewType = holder.getItemViewType();
   5700             long startBindNs = getNanoTime();
   5701             if (deadlineNs != FOREVER_NS
   5702                     && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
   5703                 // abort - we have a deadline we can't meet
   5704                 return false;
   5705             }
   5706             mAdapter.bindViewHolder(holder, offsetPosition);
   5707             long endBindNs = getNanoTime();
   5708             mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
   5709             attachAccessibilityDelegateOnBind(holder);
   5710             if (mState.isPreLayout()) {
   5711                 holder.mPreLayoutPosition = position;
   5712             }
   5713             return true;
   5714         }
   5715 
   5716         /**
   5717          * Binds the given View to the position. The View can be a View previously retrieved via
   5718          * {@link #getViewForPosition(int)} or created by
   5719          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
   5720          * <p>
   5721          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
   5722          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
   5723          * wants to handle its own recycling logic.
   5724          * <p>
   5725          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
   5726          * you don't need to call this method unless you want to bind this View to another position.
   5727          *
   5728          * @param view The view to update.
   5729          * @param position The position of the item to bind to this View.
   5730          */
   5731         public void bindViewToPosition(@NonNull View view, int position) {
   5732             ViewHolder holder = getChildViewHolderInt(view);
   5733             if (holder == null) {
   5734                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
   5735                         + " pass arbitrary views to this method, they should be created by the "
   5736                         + "Adapter" + exceptionLabel());
   5737             }
   5738             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5739             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   5740                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   5741                         + "position " + position + "(offset:" + offsetPosition + ")."
   5742                         + "state:" + mState.getItemCount() + exceptionLabel());
   5743             }
   5744             tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);
   5745 
   5746             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   5747             final LayoutParams rvLayoutParams;
   5748             if (lp == null) {
   5749                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   5750                 holder.itemView.setLayoutParams(rvLayoutParams);
   5751             } else if (!checkLayoutParams(lp)) {
   5752                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   5753                 holder.itemView.setLayoutParams(rvLayoutParams);
   5754             } else {
   5755                 rvLayoutParams = (LayoutParams) lp;
   5756             }
   5757 
   5758             rvLayoutParams.mInsetsDirty = true;
   5759             rvLayoutParams.mViewHolder = holder;
   5760             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
   5761         }
   5762 
   5763         /**
   5764          * RecyclerView provides artificial position range (item count) in pre-layout state and
   5765          * automatically maps these positions to {@link Adapter} positions when
   5766          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
   5767          * <p>
   5768          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
   5769          * LayoutManager may need to call some custom component with item positions in which
   5770          * case you need the actual adapter position instead of the pre layout position. You
   5771          * can use this method to convert a pre-layout position to adapter (post layout) position.
   5772          * <p>
   5773          * Note that if the provided position belongs to a deleted ViewHolder, this method will
   5774          * return -1.
   5775          * <p>
   5776          * Calling this method in post-layout state returns the same value back.
   5777          *
   5778          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
   5779          *                 less than {@link State#getItemCount()}.
   5780          */
   5781         public int convertPreLayoutPositionToPostLayout(int position) {
   5782             if (position < 0 || position >= mState.getItemCount()) {
   5783                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
   5784                         + "item count is " + mState.getItemCount() + exceptionLabel());
   5785             }
   5786             if (!mState.isPreLayout()) {
   5787                 return position;
   5788             }
   5789             return mAdapterHelper.findPositionOffset(position);
   5790         }
   5791 
   5792         /**
   5793          * Obtain a view initialized for the given position.
   5794          *
   5795          * This method should be used by {@link LayoutManager} implementations to obtain
   5796          * views to represent data from an {@link Adapter}.
   5797          * <p>
   5798          * The Recycler may reuse a scrap or detached view from a shared pool if one is
   5799          * available for the correct view type. If the adapter has not indicated that the
   5800          * data at the given position has changed, the Recycler will attempt to hand back
   5801          * a scrap view that was previously initialized for that data without rebinding.
   5802          *
   5803          * @param position Position to obtain a view for
   5804          * @return A view representing the data at <code>position</code> from <code>adapter</code>
   5805          */
   5806         @NonNull
   5807         public View getViewForPosition(int position) {
   5808             return getViewForPosition(position, false);
   5809         }
   5810 
   5811         View getViewForPosition(int position, boolean dryRun) {
   5812             return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
   5813         }
   5814 
   5815         /**
   5816          * Attempts to get the ViewHolder for the given position, either from the Recycler scrap,
   5817          * cache, the RecycledViewPool, or creating it directly.
   5818          * <p>
   5819          * If a deadlineNs other than {@link #FOREVER_NS} is passed, this method early return
   5820          * rather than constructing or binding a ViewHolder if it doesn't think it has time.
   5821          * If a ViewHolder must be constructed and not enough time remains, null is returned. If a
   5822          * ViewHolder is aquired and must be bound but not enough time remains, an unbound holder is
   5823          * returned. Use {@link ViewHolder#isBound()} on the returned object to check for this.
   5824          *
   5825          * @param position Position of ViewHolder to be returned.
   5826          * @param dryRun True if the ViewHolder should not be removed from scrap/cache/
   5827          * @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should
   5828          *                   complete. If FOREVER_NS is passed, this method will not fail to
   5829          *                   create/bind the holder if needed.
   5830          *
   5831          * @return ViewHolder for requested position
   5832          */
   5833         @Nullable
   5834         ViewHolder tryGetViewHolderForPositionByDeadline(int position,
   5835                 boolean dryRun, long deadlineNs) {
   5836             if (position < 0 || position >= mState.getItemCount()) {
   5837                 throw new IndexOutOfBoundsException("Invalid item position " + position
   5838                         + "(" + position + "). Item count:" + mState.getItemCount()
   5839                         + exceptionLabel());
   5840             }
   5841             boolean fromScrapOrHiddenOrCache = false;
   5842             ViewHolder holder = null;
   5843             // 0) If there is a changed scrap, try to find from there
   5844             if (mState.isPreLayout()) {
   5845                 holder = getChangedScrapViewForPosition(position);
   5846                 fromScrapOrHiddenOrCache = holder != null;
   5847             }
   5848             // 1) Find by position from scrap/hidden list/cache
   5849             if (holder == null) {
   5850                 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
   5851                 if (holder != null) {
   5852                     if (!validateViewHolderForOffsetPosition(holder)) {
   5853                         // recycle holder (and unscrap if relevant) since it can't be used
   5854                         if (!dryRun) {
   5855                             // we would like to recycle this but need to make sure it is not used by
   5856                             // animation logic etc.
   5857                             holder.addFlags(ViewHolder.FLAG_INVALID);
   5858                             if (holder.isScrap()) {
   5859                                 removeDetachedView(holder.itemView, false);
   5860                                 holder.unScrap();
   5861                             } else if (holder.wasReturnedFromScrap()) {
   5862                                 holder.clearReturnedFromScrapFlag();
   5863                             }
   5864                             recycleViewHolderInternal(holder);
   5865                         }
   5866                         holder = null;
   5867                     } else {
   5868                         fromScrapOrHiddenOrCache = true;
   5869                     }
   5870                 }
   5871             }
   5872             if (holder == null) {
   5873                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5874                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   5875                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   5876                             + "position " + position + "(offset:" + offsetPosition + ")."
   5877                             + "state:" + mState.getItemCount() + exceptionLabel());
   5878                 }
   5879 
   5880                 final int type = mAdapter.getItemViewType(offsetPosition);
   5881                 // 2) Find from scrap/cache via stable ids, if exists
   5882                 if (mAdapter.hasStableIds()) {
   5883                     holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
   5884                             type, dryRun);
   5885                     if (holder != null) {
   5886                         // update position
   5887                         holder.mPosition = offsetPosition;
   5888                         fromScrapOrHiddenOrCache = true;
   5889                     }
   5890                 }
   5891                 if (holder == null && mViewCacheExtension != null) {
   5892                     // We are NOT sending the offsetPosition because LayoutManager does not
   5893                     // know it.
   5894                     final View view = mViewCacheExtension
   5895                             .getViewForPositionAndType(this, position, type);
   5896                     if (view != null) {
   5897                         holder = getChildViewHolder(view);
   5898                         if (holder == null) {
   5899                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   5900                                     + " a view which does not have a ViewHolder"
   5901                                     + exceptionLabel());
   5902                         } else if (holder.shouldIgnore()) {
   5903                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   5904                                     + " a view that is ignored. You must call stopIgnoring before"
   5905                                     + " returning this view." + exceptionLabel());
   5906                         }
   5907                     }
   5908                 }
   5909                 if (holder == null) { // fallback to pool
   5910                     if (DEBUG) {
   5911                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
   5912                                 + position + ") fetching from shared pool");
   5913                     }
   5914                     holder = getRecycledViewPool().getRecycledView(type);
   5915                     if (holder != null) {
   5916                         holder.resetInternal();
   5917                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
   5918                             invalidateDisplayListInt(holder);
   5919                         }
   5920                     }
   5921                 }
   5922                 if (holder == null) {
   5923                     long start = getNanoTime();
   5924                     if (deadlineNs != FOREVER_NS
   5925                             && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
   5926                         // abort - we have a deadline we can't meet
   5927                         return null;
   5928                     }
   5929                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
   5930                     if (ALLOW_THREAD_GAP_WORK) {
   5931                         // only bother finding nested RV if prefetching
   5932                         RecyclerView innerView = findNestedRecyclerView(holder.itemView);
   5933                         if (innerView != null) {
   5934                             holder.mNestedRecyclerView = new WeakReference<>(innerView);
   5935                         }
   5936                     }
   5937 
   5938                     long end = getNanoTime();
   5939                     mRecyclerPool.factorInCreateTime(type, end - start);
   5940                     if (DEBUG) {
   5941                         Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
   5942                     }
   5943                 }
   5944             }
   5945 
   5946             // This is very ugly but the only place we can grab this information
   5947             // before the View is rebound and returned to the LayoutManager for post layout ops.
   5948             // We don't need this in pre-layout since the VH is not updated by the LM.
   5949             if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
   5950                     .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
   5951                 holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   5952                 if (mState.mRunSimpleAnimations) {
   5953                     int changeFlags = ItemAnimator
   5954                             .buildAdapterChangeFlagsForAnimations(holder);
   5955                     changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
   5956                     final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState,
   5957                             holder, changeFlags, holder.getUnmodifiedPayloads());
   5958                     recordAnimationInfoIfBouncedHiddenView(holder, info);
   5959                 }
   5960             }
   5961 
   5962             boolean bound = false;
   5963             if (mState.isPreLayout() && holder.isBound()) {
   5964                 // do not update unless we absolutely have to.
   5965                 holder.mPreLayoutPosition = position;
   5966             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
   5967                 if (DEBUG && holder.isRemoved()) {
   5968                     throw new IllegalStateException("Removed holder should be bound and it should"
   5969                             + " come here only in pre-layout. Holder: " + holder
   5970                             + exceptionLabel());
   5971                 }
   5972                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   5973                 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
   5974             }
   5975 
   5976             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   5977             final LayoutParams rvLayoutParams;
   5978             if (lp == null) {
   5979                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   5980                 holder.itemView.setLayoutParams(rvLayoutParams);
   5981             } else if (!checkLayoutParams(lp)) {
   5982                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   5983                 holder.itemView.setLayoutParams(rvLayoutParams);
   5984             } else {
   5985                 rvLayoutParams = (LayoutParams) lp;
   5986             }
   5987             rvLayoutParams.mViewHolder = holder;
   5988             rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
   5989             return holder;
   5990         }
   5991 
   5992         private void attachAccessibilityDelegateOnBind(ViewHolder holder) {
   5993             if (isAccessibilityEnabled()) {
   5994                 final View itemView = holder.itemView;
   5995                 if (ViewCompat.getImportantForAccessibility(itemView)
   5996                         == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
   5997                     ViewCompat.setImportantForAccessibility(itemView,
   5998                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
   5999                 }
   6000                 if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
   6001                     holder.addFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
   6002                     ViewCompat.setAccessibilityDelegate(itemView,
   6003                             mAccessibilityDelegate.getItemDelegate());
   6004                 }
   6005             }
   6006         }
   6007 
   6008         private void invalidateDisplayListInt(ViewHolder holder) {
   6009             if (holder.itemView instanceof ViewGroup) {
   6010                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
   6011             }
   6012         }
   6013 
   6014         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
   6015             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
   6016                 final View view = viewGroup.getChildAt(i);
   6017                 if (view instanceof ViewGroup) {
   6018                     invalidateDisplayListInt((ViewGroup) view, true);
   6019                 }
   6020             }
   6021             if (!invalidateThis) {
   6022                 return;
   6023             }
   6024             // we need to force it to become invisible
   6025             if (viewGroup.getVisibility() == View.INVISIBLE) {
   6026                 viewGroup.setVisibility(View.VISIBLE);
   6027                 viewGroup.setVisibility(View.INVISIBLE);
   6028             } else {
   6029                 final int visibility = viewGroup.getVisibility();
   6030                 viewGroup.setVisibility(View.INVISIBLE);
   6031                 viewGroup.setVisibility(visibility);
   6032             }
   6033         }
   6034 
   6035         /**
   6036          * Recycle a detached view. The specified view will be added to a pool of views
   6037          * for later rebinding and reuse.
   6038          *
   6039          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
   6040          * View is scrapped, it will be removed from scrap list.</p>
   6041          *
   6042          * @param view Removed view for recycling
   6043          * @see LayoutManager#removeAndRecycleView(View, Recycler)
   6044          */
   6045         public void recycleView(@NonNull View view) {
   6046             // This public recycle method tries to make view recycle-able since layout manager
   6047             // intended to recycle this view (e.g. even if it is in scrap or change cache)
   6048             ViewHolder holder = getChildViewHolderInt(view);
   6049             if (holder.isTmpDetached()) {
   6050                 removeDetachedView(view, false);
   6051             }
   6052             if (holder.isScrap()) {
   6053                 holder.unScrap();
   6054             } else if (holder.wasReturnedFromScrap()) {
   6055                 holder.clearReturnedFromScrapFlag();
   6056             }
   6057             recycleViewHolderInternal(holder);
   6058         }
   6059 
   6060         /**
   6061          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
   6062          * catch potential bugs.
   6063          * @param view
   6064          */
   6065         void recycleViewInternal(View view) {
   6066             recycleViewHolderInternal(getChildViewHolderInt(view));
   6067         }
   6068 
   6069         void recycleAndClearCachedViews() {
   6070             final int count = mCachedViews.size();
   6071             for (int i = count - 1; i >= 0; i--) {
   6072                 recycleCachedViewAt(i);
   6073             }
   6074             mCachedViews.clear();
   6075             if (ALLOW_THREAD_GAP_WORK) {
   6076                 mPrefetchRegistry.clearPrefetchPositions();
   6077             }
   6078         }
   6079 
   6080         /**
   6081          * Recycles a cached view and removes the view from the list. Views are added to cache
   6082          * if and only if they are recyclable, so this method does not check it again.
   6083          * <p>
   6084          * A small exception to this rule is when the view does not have an animator reference
   6085          * but transient state is true (due to animations created outside ItemAnimator). In that
   6086          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
   6087          * still recyclable since Adapter wants to do so.
   6088          *
   6089          * @param cachedViewIndex The index of the view in cached views list
   6090          */
   6091         void recycleCachedViewAt(int cachedViewIndex) {
   6092             if (DEBUG) {
   6093                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
   6094             }
   6095             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
   6096             if (DEBUG) {
   6097                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
   6098             }
   6099             addViewHolderToRecycledViewPool(viewHolder, true);
   6100             mCachedViews.remove(cachedViewIndex);
   6101         }
   6102 
   6103         /**
   6104          * internal implementation checks if view is scrapped or attached and throws an exception
   6105          * if so.
   6106          * Public version un-scraps before calling recycle.
   6107          */
   6108         void recycleViewHolderInternal(ViewHolder holder) {
   6109             if (holder.isScrap() || holder.itemView.getParent() != null) {
   6110                 throw new IllegalArgumentException(
   6111                         "Scrapped or attached views may not be recycled. isScrap:"
   6112                                 + holder.isScrap() + " isAttached:"
   6113                                 + (holder.itemView.getParent() != null) + exceptionLabel());
   6114             }
   6115 
   6116             if (holder.isTmpDetached()) {
   6117                 throw new IllegalArgumentException("Tmp detached view should be removed "
   6118                         + "from RecyclerView before it can be recycled: " + holder
   6119                         + exceptionLabel());
   6120             }
   6121 
   6122             if (holder.shouldIgnore()) {
   6123                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
   6124                         + " should first call stopIgnoringView(view) before calling recycle."
   6125                         + exceptionLabel());
   6126             }
   6127             //noinspection unchecked
   6128             final boolean transientStatePreventsRecycling = holder
   6129                     .doesTransientStatePreventRecycling();
   6130             final boolean forceRecycle = mAdapter != null
   6131                     && transientStatePreventsRecycling
   6132                     && mAdapter.onFailedToRecycleView(holder);
   6133             boolean cached = false;
   6134             boolean recycled = false;
   6135             if (DEBUG && mCachedViews.contains(holder)) {
   6136                 throw new IllegalArgumentException("cached view received recycle internal? "
   6137                         + holder + exceptionLabel());
   6138             }
   6139             if (forceRecycle || holder.isRecyclable()) {
   6140                 if (mViewCacheMax > 0
   6141                         && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
   6142                         | ViewHolder.FLAG_REMOVED
   6143                         | ViewHolder.FLAG_UPDATE
   6144                         | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
   6145                     // Retire oldest cached view
   6146                     int cachedViewSize = mCachedViews.size();
   6147                     if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
   6148                         recycleCachedViewAt(0);
   6149                         cachedViewSize--;
   6150                     }
   6151 
   6152                     int targetCacheIndex = cachedViewSize;
   6153                     if (ALLOW_THREAD_GAP_WORK
   6154                             && cachedViewSize > 0
   6155                             && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
   6156                         // when adding the view, skip past most recently prefetched views
   6157                         int cacheIndex = cachedViewSize - 1;
   6158                         while (cacheIndex >= 0) {
   6159                             int cachedPos = mCachedViews.get(cacheIndex).mPosition;
   6160                             if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
   6161                                 break;
   6162                             }
   6163                             cacheIndex--;
   6164                         }
   6165                         targetCacheIndex = cacheIndex + 1;
   6166                     }
   6167                     mCachedViews.add(targetCacheIndex, holder);
   6168                     cached = true;
   6169                 }
   6170                 if (!cached) {
   6171                     addViewHolderToRecycledViewPool(holder, true);
   6172                     recycled = true;
   6173                 }
   6174             } else {
   6175                 // NOTE: A view can fail to be recycled when it is scrolled off while an animation
   6176                 // runs. In this case, the item is eventually recycled by
   6177                 // ItemAnimatorRestoreListener#onAnimationFinished.
   6178 
   6179                 // TODO: consider cancelling an animation when an item is removed scrollBy,
   6180                 // to return it to the pool faster
   6181                 if (DEBUG) {
   6182                     Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
   6183                             + "re-visit here. We are still removing it from animation lists"
   6184                             + exceptionLabel());
   6185                 }
   6186             }
   6187             // even if the holder is not removed, we still call this method so that it is removed
   6188             // from view holder lists.
   6189             mViewInfoStore.removeViewHolder(holder);
   6190             if (!cached && !recycled && transientStatePreventsRecycling) {
   6191                 holder.mOwnerRecyclerView = null;
   6192             }
   6193         }
   6194 
   6195         /**
   6196          * Prepares the ViewHolder to be removed/recycled, and inserts it into the RecycledViewPool.
   6197          *
   6198          * Pass false to dispatchRecycled for views that have not been bound.
   6199          *
   6200          * @param holder Holder to be added to the pool.
   6201          * @param dispatchRecycled True to dispatch View recycled callbacks.
   6202          */
   6203         void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
   6204             clearNestedRecyclerViewIfNotNested(holder);
   6205             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
   6206                 holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
   6207                 ViewCompat.setAccessibilityDelegate(holder.itemView, null);
   6208             }
   6209             if (dispatchRecycled) {
   6210                 dispatchViewRecycled(holder);
   6211             }
   6212             holder.mOwnerRecyclerView = null;
   6213             getRecycledViewPool().putRecycledView(holder);
   6214         }
   6215 
   6216         /**
   6217          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
   6218          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
   6219          * internal bookkeeping.
   6220          */
   6221         void quickRecycleScrapView(View view) {
   6222             final ViewHolder holder = getChildViewHolderInt(view);
   6223             holder.mScrapContainer = null;
   6224             holder.mInChangeScrap = false;
   6225             holder.clearReturnedFromScrapFlag();
   6226             recycleViewHolderInternal(holder);
   6227         }
   6228 
   6229         /**
   6230          * Mark an attached view as scrap.
   6231          *
   6232          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
   6233          * for rebinding and reuse. Requests for a view for a given position may return a
   6234          * reused or rebound scrap view instance.</p>
   6235          *
   6236          * @param view View to scrap
   6237          */
   6238         void scrapView(View view) {
   6239             final ViewHolder holder = getChildViewHolderInt(view);
   6240             if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
   6241                     || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
   6242                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
   6243                     throw new IllegalArgumentException("Called scrap view with an invalid view."
   6244                             + " Invalid views cannot be reused from scrap, they should rebound from"
   6245                             + " recycler pool." + exceptionLabel());
   6246                 }
   6247                 holder.setScrapContainer(this, false);
   6248                 mAttachedScrap.add(holder);
   6249             } else {
   6250                 if (mChangedScrap == null) {
   6251                     mChangedScrap = new ArrayList<ViewHolder>();
   6252                 }
   6253                 holder.setScrapContainer(this, true);
   6254                 mChangedScrap.add(holder);
   6255             }
   6256         }
   6257 
   6258         /**
   6259          * Remove a previously scrapped view from the pool of eligible scrap.
   6260          *
   6261          * <p>This view will no longer be eligible for reuse until re-scrapped or
   6262          * until it is explicitly removed and recycled.</p>
   6263          */
   6264         void unscrapView(ViewHolder holder) {
   6265             if (holder.mInChangeScrap) {
   6266                 mChangedScrap.remove(holder);
   6267             } else {
   6268                 mAttachedScrap.remove(holder);
   6269             }
   6270             holder.mScrapContainer = null;
   6271             holder.mInChangeScrap = false;
   6272             holder.clearReturnedFromScrapFlag();
   6273         }
   6274 
   6275         int getScrapCount() {
   6276             return mAttachedScrap.size();
   6277         }
   6278 
   6279         View getScrapViewAt(int index) {
   6280             return mAttachedScrap.get(index).itemView;
   6281         }
   6282 
   6283         void clearScrap() {
   6284             mAttachedScrap.clear();
   6285             if (mChangedScrap != null) {
   6286                 mChangedScrap.clear();
   6287             }
   6288         }
   6289 
   6290         ViewHolder getChangedScrapViewForPosition(int position) {
   6291             // If pre-layout, check the changed scrap for an exact match.
   6292             final int changedScrapSize;
   6293             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
   6294                 return null;
   6295             }
   6296             // find by position
   6297             for (int i = 0; i < changedScrapSize; i++) {
   6298                 final ViewHolder holder = mChangedScrap.get(i);
   6299                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
   6300                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   6301                     return holder;
   6302                 }
   6303             }
   6304             // find by id
   6305             if (mAdapter.hasStableIds()) {
   6306                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   6307                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
   6308                     final long id = mAdapter.getItemId(offsetPosition);
   6309                     for (int i = 0; i < changedScrapSize; i++) {
   6310                         final ViewHolder holder = mChangedScrap.get(i);
   6311                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
   6312                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   6313                             return holder;
   6314                         }
   6315                     }
   6316                 }
   6317             }
   6318             return null;
   6319         }
   6320 
   6321         /**
   6322          * Returns a view for the position either from attach scrap, hidden children, or cache.
   6323          *
   6324          * @param position Item position
   6325          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
   6326          * @return a ViewHolder that can be re-used for this position.
   6327          */
   6328         ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
   6329             final int scrapCount = mAttachedScrap.size();
   6330 
   6331             // Try first for an exact, non-invalid match from scrap.
   6332             for (int i = 0; i < scrapCount; i++) {
   6333                 final ViewHolder holder = mAttachedScrap.get(i);
   6334                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
   6335                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
   6336                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   6337                     return holder;
   6338                 }
   6339             }
   6340 
   6341             if (!dryRun) {
   6342                 View view = mChildHelper.findHiddenNonRemovedView(position);
   6343                 if (view != null) {
   6344                     // This View is good to be used. We just need to unhide, detach and move to the
   6345                     // scrap list.
   6346                     final ViewHolder vh = getChildViewHolderInt(view);
   6347                     mChildHelper.unhide(view);
   6348                     int layoutIndex = mChildHelper.indexOfChild(view);
   6349                     if (layoutIndex == RecyclerView.NO_POSITION) {
   6350                         throw new IllegalStateException("layout index should not be -1 after "
   6351                                 + "unhiding a view:" + vh + exceptionLabel());
   6352                     }
   6353                     mChildHelper.detachViewFromParent(layoutIndex);
   6354                     scrapView(view);
   6355                     vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP
   6356                             | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
   6357                     return vh;
   6358                 }
   6359             }
   6360 
   6361             // Search in our first-level recycled view cache.
   6362             final int cacheSize = mCachedViews.size();
   6363             for (int i = 0; i < cacheSize; i++) {
   6364                 final ViewHolder holder = mCachedViews.get(i);
   6365                 // invalid view holders may be in cache if adapter has stable ids as they can be
   6366                 // retrieved via getScrapOrCachedViewForId
   6367                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
   6368                     if (!dryRun) {
   6369                         mCachedViews.remove(i);
   6370                     }
   6371                     if (DEBUG) {
   6372                         Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position
   6373                                 + ") found match in cache: " + holder);
   6374                     }
   6375                     return holder;
   6376                 }
   6377             }
   6378             return null;
   6379         }
   6380 
   6381         ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
   6382             // Look in our attached views first
   6383             final int count = mAttachedScrap.size();
   6384             for (int i = count - 1; i >= 0; i--) {
   6385                 final ViewHolder holder = mAttachedScrap.get(i);
   6386                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
   6387                     if (type == holder.getItemViewType()) {
   6388                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   6389                         if (holder.isRemoved()) {
   6390                             // this might be valid in two cases:
   6391                             // > item is removed but we are in pre-layout pass
   6392                             // >> do nothing. return as is. make sure we don't rebind
   6393                             // > item is removed then added to another position and we are in
   6394                             // post layout.
   6395                             // >> remove removed and invalid flags, add update flag to rebind
   6396                             // because item was invisible to us and we don't know what happened in
   6397                             // between.
   6398                             if (!mState.isPreLayout()) {
   6399                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE
   6400                                         | ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
   6401                             }
   6402                         }
   6403                         return holder;
   6404                     } else if (!dryRun) {
   6405                         // if we are running animations, it is actually better to keep it in scrap
   6406                         // but this would force layout manager to lay it out which would be bad.
   6407                         // Recycle this scrap. Type mismatch.
   6408                         mAttachedScrap.remove(i);
   6409                         removeDetachedView(holder.itemView, false);
   6410                         quickRecycleScrapView(holder.itemView);
   6411                     }
   6412                 }
   6413             }
   6414 
   6415             // Search the first-level cache
   6416             final int cacheSize = mCachedViews.size();
   6417             for (int i = cacheSize - 1; i >= 0; i--) {
   6418                 final ViewHolder holder = mCachedViews.get(i);
   6419                 if (holder.getItemId() == id) {
   6420                     if (type == holder.getItemViewType()) {
   6421                         if (!dryRun) {
   6422                             mCachedViews.remove(i);
   6423                         }
   6424                         return holder;
   6425                     } else if (!dryRun) {
   6426                         recycleCachedViewAt(i);
   6427                         return null;
   6428                     }
   6429                 }
   6430             }
   6431             return null;
   6432         }
   6433 
   6434         void dispatchViewRecycled(@NonNull ViewHolder holder) {
   6435             if (mRecyclerListener != null) {
   6436                 mRecyclerListener.onViewRecycled(holder);
   6437             }
   6438             if (mAdapter != null) {
   6439                 mAdapter.onViewRecycled(holder);
   6440             }
   6441             if (mState != null) {
   6442                 mViewInfoStore.removeViewHolder(holder);
   6443             }
   6444             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
   6445         }
   6446 
   6447         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   6448                 boolean compatibleWithPrevious) {
   6449             clear();
   6450             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
   6451         }
   6452 
   6453         void offsetPositionRecordsForMove(int from, int to) {
   6454             final int start, end, inBetweenOffset;
   6455             if (from < to) {
   6456                 start = from;
   6457                 end = to;
   6458                 inBetweenOffset = -1;
   6459             } else {
   6460                 start = to;
   6461                 end = from;
   6462                 inBetweenOffset = 1;
   6463             }
   6464             final int cachedCount = mCachedViews.size();
   6465             for (int i = 0; i < cachedCount; i++) {
   6466                 final ViewHolder holder = mCachedViews.get(i);
   6467                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   6468                     continue;
   6469                 }
   6470                 if (holder.mPosition == from) {
   6471                     holder.offsetPosition(to - from, false);
   6472                 } else {
   6473                     holder.offsetPosition(inBetweenOffset, false);
   6474                 }
   6475                 if (DEBUG) {
   6476                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder "
   6477                             + holder);
   6478                 }
   6479             }
   6480         }
   6481 
   6482         void offsetPositionRecordsForInsert(int insertedAt, int count) {
   6483             final int cachedCount = mCachedViews.size();
   6484             for (int i = 0; i < cachedCount; i++) {
   6485                 final ViewHolder holder = mCachedViews.get(i);
   6486                 if (holder != null && holder.mPosition >= insertedAt) {
   6487                     if (DEBUG) {
   6488                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder "
   6489                                 + holder + " now at position " + (holder.mPosition + count));
   6490                     }
   6491                     holder.offsetPosition(count, true);
   6492                 }
   6493             }
   6494         }
   6495 
   6496         /**
   6497          * @param removedFrom Remove start index
   6498          * @param count Remove count
   6499          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
   6500          *                         false, they'll be applied before the second layout pass
   6501          */
   6502         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
   6503             final int removedEnd = removedFrom + count;
   6504             final int cachedCount = mCachedViews.size();
   6505             for (int i = cachedCount - 1; i >= 0; i--) {
   6506                 final ViewHolder holder = mCachedViews.get(i);
   6507                 if (holder != null) {
   6508                     if (holder.mPosition >= removedEnd) {
   6509                         if (DEBUG) {
   6510                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i
   6511                                     + " holder " + holder + " now at position "
   6512                                     + (holder.mPosition - count));
   6513                         }
   6514                         holder.offsetPosition(-count, applyToPreLayout);
   6515                     } else if (holder.mPosition >= removedFrom) {
   6516                         // Item for this view was removed. Dump it from the cache.
   6517                         holder.addFlags(ViewHolder.FLAG_REMOVED);
   6518                         recycleCachedViewAt(i);
   6519                     }
   6520                 }
   6521             }
   6522         }
   6523 
   6524         void setViewCacheExtension(ViewCacheExtension extension) {
   6525             mViewCacheExtension = extension;
   6526         }
   6527 
   6528         void setRecycledViewPool(RecycledViewPool pool) {
   6529             if (mRecyclerPool != null) {
   6530                 mRecyclerPool.detach();
   6531             }
   6532             mRecyclerPool = pool;
   6533             if (mRecyclerPool != null && getAdapter() != null) {
   6534                 mRecyclerPool.attach();
   6535             }
   6536         }
   6537 
   6538         RecycledViewPool getRecycledViewPool() {
   6539             if (mRecyclerPool == null) {
   6540                 mRecyclerPool = new RecycledViewPool();
   6541             }
   6542             return mRecyclerPool;
   6543         }
   6544 
   6545         void viewRangeUpdate(int positionStart, int itemCount) {
   6546             final int positionEnd = positionStart + itemCount;
   6547             final int cachedCount = mCachedViews.size();
   6548             for (int i = cachedCount - 1; i >= 0; i--) {
   6549                 final ViewHolder holder = mCachedViews.get(i);
   6550                 if (holder == null) {
   6551                     continue;
   6552                 }
   6553 
   6554                 final int pos = holder.mPosition;
   6555                 if (pos >= positionStart && pos < positionEnd) {
   6556                     holder.addFlags(ViewHolder.FLAG_UPDATE);
   6557                     recycleCachedViewAt(i);
   6558                     // cached views should not be flagged as changed because this will cause them
   6559                     // to animate when they are returned from cache.
   6560                 }
   6561             }
   6562         }
   6563 
   6564         void markKnownViewsInvalid() {
   6565             final int cachedCount = mCachedViews.size();
   6566             for (int i = 0; i < cachedCount; i++) {
   6567                 final ViewHolder holder = mCachedViews.get(i);
   6568                 if (holder != null) {
   6569                     holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   6570                     holder.addChangePayload(null);
   6571                 }
   6572             }
   6573 
   6574             if (mAdapter == null || !mAdapter.hasStableIds()) {
   6575                 // we cannot re-use cached views in this case. Recycle them all
   6576                 recycleAndClearCachedViews();
   6577             }
   6578         }
   6579 
   6580         void clearOldPositions() {
   6581             final int cachedCount = mCachedViews.size();
   6582             for (int i = 0; i < cachedCount; i++) {
   6583                 final ViewHolder holder = mCachedViews.get(i);
   6584                 holder.clearOldPosition();
   6585             }
   6586             final int scrapCount = mAttachedScrap.size();
   6587             for (int i = 0; i < scrapCount; i++) {
   6588                 mAttachedScrap.get(i).clearOldPosition();
   6589             }
   6590             if (mChangedScrap != null) {
   6591                 final int changedScrapCount = mChangedScrap.size();
   6592                 for (int i = 0; i < changedScrapCount; i++) {
   6593                     mChangedScrap.get(i).clearOldPosition();
   6594                 }
   6595             }
   6596         }
   6597 
   6598         void markItemDecorInsetsDirty() {
   6599             final int cachedCount = mCachedViews.size();
   6600             for (int i = 0; i < cachedCount; i++) {
   6601                 final ViewHolder holder = mCachedViews.get(i);
   6602                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
   6603                 if (layoutParams != null) {
   6604                     layoutParams.mInsetsDirty = true;
   6605                 }
   6606             }
   6607         }
   6608     }
   6609 
   6610     /**
   6611      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
   6612      * be controlled by the developer.
   6613      * <p>
   6614      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
   6615      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
   6616      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
   6617      * {@link RecycledViewPool}.
   6618      * <p>
   6619      * Note that, Recycler never sends Views to this method to be cached. It is developers
   6620      * responsibility to decide whether they want to keep their Views in this custom cache or let
   6621      * the default recycling policy handle it.
   6622      */
   6623     public abstract static class ViewCacheExtension {
   6624 
   6625         /**
   6626          * Returns a View that can be binded to the given Adapter position.
   6627          * <p>
   6628          * This method should <b>not</b> create a new View. Instead, it is expected to return
   6629          * an already created View that can be re-used for the given type and position.
   6630          * If the View is marked as ignored, it should first call
   6631          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
   6632          * <p>
   6633          * RecyclerView will re-bind the returned View to the position if necessary.
   6634          *
   6635          * @param recycler The Recycler that can be used to bind the View
   6636          * @param position The adapter position
   6637          * @param type     The type of the View, defined by adapter
   6638          * @return A View that is bound to the given position or NULL if there is no View to re-use
   6639          * @see LayoutManager#ignoreView(View)
   6640          */
   6641         @Nullable
   6642         public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
   6643                 int type);
   6644     }
   6645 
   6646     /**
   6647      * Base class for an Adapter
   6648      *
   6649      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
   6650      * within a {@link RecyclerView}.</p>
   6651      *
   6652      * @param  A class that extends ViewHolder that will be used by the adapter.
   6653      */
   6654     public abstract static class Adapter<VH extends ViewHolder> {
   6655         private final AdapterDataObservable mObservable = new AdapterDataObservable();
   6656         private boolean mHasStableIds = false;
   6657 
   6658         /**
   6659          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
   6660          * an item.
   6661          * <p>
   6662          * This new ViewHolder should be constructed with a new View that can represent the items
   6663          * of the given type. You can either create a new View manually or inflate it from an XML
   6664          * layout file.
   6665          * <p>
   6666          * The new ViewHolder will be used to display items of the adapter using
   6667          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
   6668          * different items in the data set, it is a good idea to cache references to sub views of
   6669          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
   6670          *
   6671          * @param parent The ViewGroup into which the new View will be added after it is bound to
   6672          *               an adapter position.
   6673          * @param viewType The view type of the new View.
   6674          *
   6675          * @return A new ViewHolder that holds a View of the given view type.
   6676          * @see #getItemViewType(int)
   6677          * @see #onBindViewHolder(ViewHolder, int)
   6678          */
   6679         @NonNull
   6680         public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);
   6681 
   6682         /**
   6683          * Called by RecyclerView to display the data at the specified position. This method should
   6684          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
   6685          * position.
   6686          * <p>
   6687          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   6688          * again if the position of the item changes in the data set unless the item itself is
   6689          * invalidated or the new position cannot be determined. For this reason, you should only
   6690          * use the <code>position</code> parameter while acquiring the related data item inside
   6691          * this method and should not keep a copy of it. If you need the position of an item later
   6692          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   6693          * have the updated adapter position.
   6694          *
   6695          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
   6696          * handle efficient partial bind.
   6697          *
   6698          * @param holder The ViewHolder which should be updated to represent the contents of the
   6699          *        item at the given position in the data set.
   6700          * @param position The position of the item within the adapter's data set.
   6701          */
   6702         public abstract void onBindViewHolder(@NonNull VH holder, int position);
   6703 
   6704         /**
   6705          * Called by RecyclerView to display the data at the specified position. This method
   6706          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
   6707          * the given position.
   6708          * <p>
   6709          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   6710          * again if the position of the item changes in the data set unless the item itself is
   6711          * invalidated or the new position cannot be determined. For this reason, you should only
   6712          * use the <code>position</code> parameter while acquiring the related data item inside
   6713          * this method and should not keep a copy of it. If you need the position of an item later
   6714          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   6715          * have the updated adapter position.
   6716          * <p>
   6717          * Partial bind vs full bind:
   6718          * <p>
   6719          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
   6720          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
   6721          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
   6722          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
   6723          * Adapter should not assume that the payload passed in notify methods will be received by
   6724          * onBindViewHolder().  For example when the view is not attached to the screen, the
   6725          * payload in notifyItemChange() will be simply dropped.
   6726          *
   6727          * @param holder The ViewHolder which should be updated to represent the contents of the
   6728          *               item at the given position in the data set.
   6729          * @param position The position of the item within the adapter's data set.
   6730          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
   6731          *                 update.
   6732          */
   6733         public void onBindViewHolder(@NonNull VH holder, int position,
   6734                 @NonNull List<Object> payloads) {
   6735             onBindViewHolder(holder, position);
   6736         }
   6737 
   6738         /**
   6739          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
   6740          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
   6741          *
   6742          * @see #onCreateViewHolder(ViewGroup, int)
   6743          */
   6744         @NonNull
   6745         public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
   6746             try {
   6747                 TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
   6748                 final VH holder = onCreateViewHolder(parent, viewType);
   6749                 if (holder.itemView.getParent() != null) {
   6750                     throw new IllegalStateException("ViewHolder views must not be attached when"
   6751                             + " created. Ensure that you are not passing 'true' to the attachToRoot"
   6752                             + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
   6753                 }
   6754                 holder.mItemViewType = viewType;
   6755                 return holder;
   6756             } finally {
   6757                 TraceCompat.endSection();
   6758             }
   6759         }
   6760 
   6761         /**
   6762          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
   6763          * {@link ViewHolder} contents with the item at the given position and also sets up some
   6764          * private fields to be used by RecyclerView.
   6765          *
   6766          * @see #onBindViewHolder(ViewHolder, int)
   6767          */
   6768         public final void bindViewHolder(@NonNull VH holder, int position) {
   6769             holder.mPosition = position;
   6770             if (hasStableIds()) {
   6771                 holder.mItemId = getItemId(position);
   6772             }
   6773             holder.setFlags(ViewHolder.FLAG_BOUND,
   6774                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
   6775                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   6776             TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
   6777             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
   6778             holder.clearPayload();
   6779             final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
   6780             if (layoutParams instanceof RecyclerView.LayoutParams) {
   6781                 ((LayoutParams) layoutParams).mInsetsDirty = true;
   6782             }
   6783             TraceCompat.endSection();
   6784         }
   6785 
   6786         /**
   6787          * Return the view type of the item at <code>position</code> for the purposes
   6788          * of view recycling.
   6789          *
   6790          * <p>The default implementation of this method returns 0, making the assumption of
   6791          * a single view type for the adapter. Unlike ListView adapters, types need not
   6792          * be contiguous. Consider using id resources to uniquely identify item view types.
   6793          *
   6794          * @param position position to query
   6795          * @return integer value identifying the type of the view needed to represent the item at
   6796          *                 <code>position</code>. Type codes need not be contiguous.
   6797          */
   6798         public int getItemViewType(int position) {
   6799             return 0;
   6800         }
   6801 
   6802         /**
   6803          * Indicates whether each item in the data set can be represented with a unique identifier
   6804          * of type {@link java.lang.Long}.
   6805          *
   6806          * @param hasStableIds Whether items in data set have unique identifiers or not.
   6807          * @see #hasStableIds()
   6808          * @see #getItemId(int)
   6809          */
   6810         public void setHasStableIds(boolean hasStableIds) {
   6811             if (hasObservers()) {
   6812                 throw new IllegalStateException("Cannot change whether this adapter has "
   6813                         + "stable IDs while the adapter has registered observers.");
   6814             }
   6815             mHasStableIds = hasStableIds;
   6816         }
   6817 
   6818         /**
   6819          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
   6820          * would return false this method should return {@link #NO_ID}. The default implementation
   6821          * of this method returns {@link #NO_ID}.
   6822          *
   6823          * @param position Adapter position to query
   6824          * @return the stable ID of the item at position
   6825          */
   6826         public long getItemId(int position) {
   6827             return NO_ID;
   6828         }
   6829 
   6830         /**
   6831          * Returns the total number of items in the data set held by the adapter.
   6832          *
   6833          * @return The total number of items in this adapter.
   6834          */
   6835         public abstract int getItemCount();
   6836 
   6837         /**
   6838          * Returns true if this adapter publishes a unique <code>long</code> value that can
   6839          * act as a key for the item at a given position in the data set. If that item is relocated
   6840          * in the data set, the ID returned for that item should be the same.
   6841          *
   6842          * @return true if this adapter's items have stable IDs
   6843          */
   6844         public final boolean hasStableIds() {
   6845             return mHasStableIds;
   6846         }
   6847 
   6848         /**
   6849          * Called when a view created by this adapter has been recycled.
   6850          *
   6851          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
   6852          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
   6853          * fallen out of visibility or a set of cached views represented by views still
   6854          * attached to the parent RecyclerView. If an item view has large or expensive data
   6855          * bound to it such as large bitmaps, this may be a good place to release those
   6856          * resources.</p>
   6857          * <p>
   6858          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   6859          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   6860          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   6861          * its adapter position.
   6862          *
   6863          * @param holder The ViewHolder for the view being recycled
   6864          */
   6865         public void onViewRecycled(@NonNull VH holder) {
   6866         }
   6867 
   6868         /**
   6869          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
   6870          * due to its transient state. Upon receiving this callback, Adapter can clear the
   6871          * animation(s) that effect the View's transient state and return <code>true</code> so that
   6872          * the View can be recycled. Keep in mind that the View in question is already removed from
   6873          * the RecyclerView.
   6874          * <p>
   6875          * In some cases, it is acceptable to recycle a View although it has transient state. Most
   6876          * of the time, this is a case where the transient state will be cleared in
   6877          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
   6878          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
   6879          * value of this method to decide whether the View should be recycled or not.
   6880          * <p>
   6881          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
   6882          * should never receive this callback because RecyclerView keeps those Views as children
   6883          * until their animations are complete. This callback is useful when children of the item
   6884          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
   6885          * <p>
   6886          * You should <em>never</em> fix this issue by calling
   6887          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
   6888          * <code>holder.itemView.setHasTransientState(true);</code>. Each
   6889          * <code>View.setHasTransientState(true)</code> call must be matched by a
   6890          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
   6891          * may become inconsistent. You should always prefer to end or cancel animations that are
   6892          * triggering the transient state instead of handling it manually.
   6893          *
   6894          * @param holder The ViewHolder containing the View that could not be recycled due to its
   6895          *               transient state.
   6896          * @return True if the View should be recycled, false otherwise. Note that if this method
   6897          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
   6898          * the View and recycle it regardless. If this method returns <code>false</code>,
   6899          * RecyclerView will check the View's transient state again before giving a final decision.
   6900          * Default implementation returns false.
   6901          */
   6902         public boolean onFailedToRecycleView(@NonNull VH holder) {
   6903             return false;
   6904         }
   6905 
   6906         /**
   6907          * Called when a view created by this adapter has been attached to a window.
   6908          *
   6909          * <p>This can be used as a reasonable signal that the view is about to be seen
   6910          * by the user. If the adapter previously freed any resources in
   6911          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
   6912          * those resources should be restored here.</p>
   6913          *
   6914          * @param holder Holder of the view being attached
   6915          */
   6916         public void onViewAttachedToWindow(@NonNull VH holder) {
   6917         }
   6918 
   6919         /**
   6920          * Called when a view created by this adapter has been detached from its window.
   6921          *
   6922          * <p>Becoming detached from the window is not necessarily a permanent condition;
   6923          * the consumer of an Adapter's views may choose to cache views offscreen while they
   6924          * are not visible, attaching and detaching them as appropriate.</p>
   6925          *
   6926          * @param holder Holder of the view being detached
   6927          */
   6928         public void onViewDetachedFromWindow(@NonNull VH holder) {
   6929         }
   6930 
   6931         /**
   6932          * Returns true if one or more observers are attached to this adapter.
   6933          *
   6934          * @return true if this adapter has observers
   6935          */
   6936         public final boolean hasObservers() {
   6937             return mObservable.hasObservers();
   6938         }
   6939 
   6940         /**
   6941          * Register a new observer to listen for data changes.
   6942          *
   6943          * <p>The adapter may publish a variety of events describing specific changes.
   6944          * Not all adapters may support all change types and some may fall back to a generic
   6945          * {@link RecyclerView.AdapterDataObserver#onChanged()
   6946          * "something changed"} event if more specific data is not available.</p>
   6947          *
   6948          * <p>Components registering observers with an adapter are responsible for
   6949          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6950          * unregistering} those observers when finished.</p>
   6951          *
   6952          * @param observer Observer to register
   6953          *
   6954          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6955          */
   6956         public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
   6957             mObservable.registerObserver(observer);
   6958         }
   6959 
   6960         /**
   6961          * Unregister an observer currently listening for data changes.
   6962          *
   6963          * <p>The unregistered observer will no longer receive events about changes
   6964          * to the adapter.</p>
   6965          *
   6966          * @param observer Observer to unregister
   6967          *
   6968          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
   6969          */
   6970         public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
   6971             mObservable.unregisterObserver(observer);
   6972         }
   6973 
   6974         /**
   6975          * Called by RecyclerView when it starts observing this Adapter.
   6976          * <p>
   6977          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
   6978          *
   6979          * @param recyclerView The RecyclerView instance which started observing this adapter.
   6980          * @see #onDetachedFromRecyclerView(RecyclerView)
   6981          */
   6982         public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
   6983         }
   6984 
   6985         /**
   6986          * Called by RecyclerView when it stops observing this Adapter.
   6987          *
   6988          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
   6989          * @see #onAttachedToRecyclerView(RecyclerView)
   6990          */
   6991         public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
   6992         }
   6993 
   6994         /**
   6995          * Notify any registered observers that the data set has changed.
   6996          *
   6997          * <p>There are two different classes of data change events, item changes and structural
   6998          * changes. Item changes are when a single item has its data updated but no positional
   6999          * changes have occurred. Structural changes are when items are inserted, removed or moved
   7000          * within the data set.</p>
   7001          *
   7002          * <p>This event does not specify what about the data set has changed, forcing
   7003          * any observers to assume that all existing items and structure may no longer be valid.
   7004          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
   7005          *
   7006          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
   7007          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
   7008          * this method is used. This can help for the purposes of animation and visual
   7009          * object persistence but individual item views will still need to be rebound
   7010          * and relaid out.</p>
   7011          *
   7012          * <p>If you are writing an adapter it will always be more efficient to use the more
   7013          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
   7014          * as a last resort.</p>
   7015          *
   7016          * @see #notifyItemChanged(int)
   7017          * @see #notifyItemInserted(int)
   7018          * @see #notifyItemRemoved(int)
   7019          * @see #notifyItemRangeChanged(int, int)
   7020          * @see #notifyItemRangeInserted(int, int)
   7021          * @see #notifyItemRangeRemoved(int, int)
   7022          */
   7023         public final void notifyDataSetChanged() {
   7024             mObservable.notifyChanged();
   7025         }
   7026 
   7027         /**
   7028          * Notify any registered observers that the item at <code>position</code> has changed.
   7029          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
   7030          *
   7031          * <p>This is an item change event, not a structural change event. It indicates that any
   7032          * reflection of the data at <code>position</code> is out of date and should be updated.
   7033          * The item at <code>position</code> retains the same identity.</p>
   7034          *
   7035          * @param position Position of the item that has changed
   7036          *
   7037          * @see #notifyItemRangeChanged(int, int)
   7038          */
   7039         public final void notifyItemChanged(int position) {
   7040             mObservable.notifyItemRangeChanged(position, 1);
   7041         }
   7042 
   7043         /**
   7044          * Notify any registered observers that the item at <code>position</code> has changed with
   7045          * an optional payload object.
   7046          *
   7047          * <p>This is an item change event, not a structural change event. It indicates that any
   7048          * reflection of the data at <code>position</code> is out of date and should be updated.
   7049          * The item at <code>position</code> retains the same identity.
   7050          * </p>
   7051          *
   7052          * <p>
   7053          * Client can optionally pass a payload for partial change. These payloads will be merged
   7054          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   7055          * item is already represented by a ViewHolder and it will be rebound to the same
   7056          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   7057          * payloads on that item and prevent future payload until
   7058          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   7059          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   7060          * attached, the payload will be simply dropped.
   7061          *
   7062          * @param position Position of the item that has changed
   7063          * @param payload Optional parameter, use null to identify a "full" update
   7064          *
   7065          * @see #notifyItemRangeChanged(int, int)
   7066          */
   7067         public final void notifyItemChanged(int position, @Nullable Object payload) {
   7068             mObservable.notifyItemRangeChanged(position, 1, payload);
   7069         }
   7070 
   7071         /**
   7072          * Notify any registered observers that the <code>itemCount</code> items starting at
   7073          * position <code>positionStart</code> have changed.
   7074          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
   7075          *
   7076          * <p>This is an item change event, not a structural change event. It indicates that
   7077          * any reflection of the data in the given position range is out of date and should
   7078          * be updated. The items in the given range retain the same identity.</p>
   7079          *
   7080          * @param positionStart Position of the first item that has changed
   7081          * @param itemCount Number of items that have changed
   7082          *
   7083          * @see #notifyItemChanged(int)
   7084          */
   7085         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
   7086             mObservable.notifyItemRangeChanged(positionStart, itemCount);
   7087         }
   7088 
   7089         /**
   7090          * Notify any registered observers that the <code>itemCount</code> items starting at
   7091          * position <code>positionStart</code> have changed. An optional payload can be
   7092          * passed to each changed item.
   7093          *
   7094          * <p>This is an item change event, not a structural change event. It indicates that any
   7095          * reflection of the data in the given position range is out of date and should be updated.
   7096          * The items in the given range retain the same identity.
   7097          * </p>
   7098          *
   7099          * <p>
   7100          * Client can optionally pass a payload for partial change. These payloads will be merged
   7101          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   7102          * item is already represented by a ViewHolder and it will be rebound to the same
   7103          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   7104          * payloads on that item and prevent future payload until
   7105          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   7106          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   7107          * attached, the payload will be simply dropped.
   7108          *
   7109          * @param positionStart Position of the first item that has changed
   7110          * @param itemCount Number of items that have changed
   7111          * @param payload  Optional parameter, use null to identify a "full" update
   7112          *
   7113          * @see #notifyItemChanged(int)
   7114          */
   7115         public final void notifyItemRangeChanged(int positionStart, int itemCount,
   7116                 @Nullable Object payload) {
   7117             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
   7118         }
   7119 
   7120         /**
   7121          * Notify any registered observers that the item reflected at <code>position</code>
   7122          * has been newly inserted. The item previously at <code>position</code> is now at
   7123          * position <code>position + 1</code>.
   7124          *
   7125          * <p>This is a structural change event. Representations of other existing items in the
   7126          * data set are still considered up to date and will not be rebound, though their
   7127          * positions may be altered.</p>
   7128          *
   7129          * @param position Position of the newly inserted item in the data set
   7130          *
   7131          * @see #notifyItemRangeInserted(int, int)
   7132          */
   7133         public final void notifyItemInserted(int position) {
   7134             mObservable.notifyItemRangeInserted(position, 1);
   7135         }
   7136 
   7137         /**
   7138          * Notify any registered observers that the item reflected at <code>fromPosition</code>
   7139          * has been moved to <code>toPosition</code>.
   7140          *
   7141          * <p>This is a structural change event. Representations of other existing items in the
   7142          * data set are still considered up to date and will not be rebound, though their
   7143          * positions may be altered.</p>
   7144          *
   7145          * @param fromPosition Previous position of the item.
   7146          * @param toPosition New position of the item.
   7147          */
   7148         public final void notifyItemMoved(int fromPosition, int toPosition) {
   7149             mObservable.notifyItemMoved(fromPosition, toPosition);
   7150         }
   7151 
   7152         /**
   7153          * Notify any registered observers that the currently reflected <code>itemCount</code>
   7154          * items starting at <code>positionStart</code> have been newly inserted. The items
   7155          * previously located at <code>positionStart</code> and beyond can now be found starting
   7156          * at position <code>positionStart + itemCount</code>.
   7157          *
   7158          * <p>This is a structural change event. Representations of other existing items in the
   7159          * data set are still considered up to date and will not be rebound, though their positions
   7160          * may be altered.</p>
   7161          *
   7162          * @param positionStart Position of the first item that was inserted
   7163          * @param itemCount Number of items inserted
   7164          *
   7165          * @see #notifyItemInserted(int)
   7166          */
   7167         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
   7168             mObservable.notifyItemRangeInserted(positionStart, itemCount);
   7169         }
   7170 
   7171         /**
   7172          * Notify any registered observers that the item previously located at <code>position</code>
   7173          * has been removed from the data set. The items previously located at and after
   7174          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
   7175          *
   7176          * <p>This is a structural change event. Representations of other existing items in the
   7177          * data set are still considered up to date and will not be rebound, though their positions
   7178          * may be altered.</p>
   7179          *
   7180          * @param position Position of the item that has now been removed
   7181          *
   7182          * @see #notifyItemRangeRemoved(int, int)
   7183          */
   7184         public final void notifyItemRemoved(int position) {
   7185             mObservable.notifyItemRangeRemoved(position, 1);
   7186         }
   7187 
   7188         /**
   7189          * Notify any registered observers that the <code>itemCount</code> items previously
   7190          * located at <code>positionStart</code> have been removed from the data set. The items
   7191          * previously located at and after <code>positionStart + itemCount</code> may now be found
   7192          * at <code>oldPosition - itemCount</code>.
   7193          *
   7194          * <p>This is a structural change event. Representations of other existing items in the data
   7195          * set are still considered up to date and will not be rebound, though their positions
   7196          * may be altered.</p>
   7197          *
   7198          * @param positionStart Previous position of the first item that was removed
   7199          * @param itemCount Number of items removed from the data set
   7200          */
   7201         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
   7202             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
   7203         }
   7204     }
   7205 
   7206     void dispatchChildDetached(View child) {
   7207         final ViewHolder viewHolder = getChildViewHolderInt(child);
   7208         onChildDetachedFromWindow(child);
   7209         if (mAdapter != null && viewHolder != null) {
   7210             mAdapter.onViewDetachedFromWindow(viewHolder);
   7211         }
   7212         if (mOnChildAttachStateListeners != null) {
   7213             final int cnt = mOnChildAttachStateListeners.size();
   7214             for (int i = cnt - 1; i >= 0; i--) {
   7215                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
   7216             }
   7217         }
   7218     }
   7219 
   7220     void dispatchChildAttached(View child) {
   7221         final ViewHolder viewHolder = getChildViewHolderInt(child);
   7222         onChildAttachedToWindow(child);
   7223         if (mAdapter != null && viewHolder != null) {
   7224             mAdapter.onViewAttachedToWindow(viewHolder);
   7225         }
   7226         if (mOnChildAttachStateListeners != null) {
   7227             final int cnt = mOnChildAttachStateListeners.size();
   7228             for (int i = cnt - 1; i >= 0; i--) {
   7229                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
   7230             }
   7231         }
   7232     }
   7233 
   7234     /**
   7235      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
   7236      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
   7237      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
   7238      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
   7239      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
   7240      * layout managers are provided for general use.
   7241      * <p/>
   7242      * If the LayoutManager specifies a default constructor or one with the signature
   7243      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
   7244      * instantiate and set the LayoutManager when being inflated. Most used properties can
   7245      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
   7246      * a LayoutManager specifies both constructors, the non-default constructor will take
   7247      * precedence.
   7248      *
   7249      */
   7250     public abstract static class LayoutManager {
   7251         ChildHelper mChildHelper;
   7252         RecyclerView mRecyclerView;
   7253 
   7254         /**
   7255          * The callback used for retrieving information about a RecyclerView and its children in the
   7256          * horizontal direction.
   7257          */
   7258         private final ViewBoundsCheck.Callback mHorizontalBoundCheckCallback =
   7259                 new ViewBoundsCheck.Callback() {
   7260                     @Override
   7261                     public int getChildCount() {
   7262                         return LayoutManager.this.getChildCount();
   7263                     }
   7264 
   7265                     @Override
   7266                     public View getParent() {
   7267                         return mRecyclerView;
   7268                     }
   7269 
   7270                     @Override
   7271                     public View getChildAt(int index) {
   7272                         return LayoutManager.this.getChildAt(index);
   7273                     }
   7274 
   7275                     @Override
   7276                     public int getParentStart() {
   7277                         return LayoutManager.this.getPaddingLeft();
   7278                     }
   7279 
   7280                     @Override
   7281                     public int getParentEnd() {
   7282                         return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
   7283                     }
   7284 
   7285                     @Override
   7286                     public int getChildStart(View view) {
   7287                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
   7288                                 view.getLayoutParams();
   7289                         return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
   7290                     }
   7291 
   7292                     @Override
   7293                     public int getChildEnd(View view) {
   7294                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
   7295                                 view.getLayoutParams();
   7296                         return LayoutManager.this.getDecoratedRight(view) + params.rightMargin;
   7297                     }
   7298                 };
   7299 
   7300         /**
   7301          * The callback used for retrieving information about a RecyclerView and its children in the
   7302          * vertical direction.
   7303          */
   7304         private final ViewBoundsCheck.Callback mVerticalBoundCheckCallback =
   7305                 new ViewBoundsCheck.Callback() {
   7306                     @Override
   7307                     public int getChildCount() {
   7308                         return LayoutManager.this.getChildCount();
   7309                     }
   7310 
   7311                     @Override
   7312                     public View getParent() {
   7313                         return mRecyclerView;
   7314                     }
   7315 
   7316                     @Override
   7317                     public View getChildAt(int index) {
   7318                         return LayoutManager.this.getChildAt(index);
   7319                     }
   7320 
   7321                     @Override
   7322                     public int getParentStart() {
   7323                         return LayoutManager.this.getPaddingTop();
   7324                     }
   7325 
   7326                     @Override
   7327                     public int getParentEnd() {
   7328                         return LayoutManager.this.getHeight()
   7329                                 - LayoutManager.this.getPaddingBottom();
   7330                     }
   7331 
   7332                     @Override
   7333                     public int getChildStart(View view) {
   7334                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
   7335                                 view.getLayoutParams();
   7336                         return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
   7337                     }
   7338 
   7339                     @Override
   7340                     public int getChildEnd(View view) {
   7341                         final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
   7342                                 view.getLayoutParams();
   7343                         return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
   7344                     }
   7345                 };
   7346 
   7347         /**
   7348          * Utility objects used to check the boundaries of children against their parent
   7349          * RecyclerView.
   7350          * @see #isViewPartiallyVisible(View, boolean, boolean),
   7351          * {@link LinearLayoutManager#findOneVisibleChild(int, int, boolean, boolean)},
   7352          * and {@link LinearLayoutManager#findOnePartiallyOrCompletelyInvisibleChild(int, int)}.
   7353          */
   7354         ViewBoundsCheck mHorizontalBoundCheck = new ViewBoundsCheck(mHorizontalBoundCheckCallback);
   7355         ViewBoundsCheck mVerticalBoundCheck = new ViewBoundsCheck(mVerticalBoundCheckCallback);
   7356 
   7357         @Nullable
   7358         SmoothScroller mSmoothScroller;
   7359 
   7360         boolean mRequestedSimpleAnimations = false;
   7361 
   7362         boolean mIsAttachedToWindow = false;
   7363 
   7364         /**
   7365          * This field is only set via the deprecated {@link #setAutoMeasureEnabled(boolean)} and is
   7366          * only accessed via {@link #isAutoMeasureEnabled()} for backwards compatability reasons.
   7367          */
   7368         boolean mAutoMeasure = false;
   7369 
   7370         /**
   7371          * LayoutManager has its own more strict measurement cache to avoid re-measuring a child
   7372          * if the space that will be given to it is already larger than what it has measured before.
   7373          */
   7374         private boolean mMeasurementCacheEnabled = true;
   7375 
   7376         private boolean mItemPrefetchEnabled = true;
   7377 
   7378         /**
   7379          * Written by {@link GapWorker} when prefetches occur to track largest number of view ever
   7380          * requested by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)} or
   7381          * {@link #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)} call.
   7382          *
   7383          * If expanded by a {@link #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)},
   7384          * will be reset upon layout to prevent initial prefetches (often large, since they're
   7385          * proportional to expected child count) from expanding cache permanently.
   7386          */
   7387         int mPrefetchMaxCountObserved;
   7388 
   7389         /**
   7390          * If true, mPrefetchMaxCountObserved is only valid until next layout, and should be reset.
   7391          */
   7392         boolean mPrefetchMaxObservedInInitialPrefetch;
   7393 
   7394         /**
   7395          * These measure specs might be the measure specs that were passed into RecyclerView's
   7396          * onMeasure method OR fake measure specs created by the RecyclerView.
   7397          * For example, when a layout is run, RecyclerView always sets these specs to be
   7398          * EXACTLY because a LayoutManager cannot resize RecyclerView during a layout pass.
   7399          * <p>
   7400          * Also, to be able to use the hint in unspecified measure specs, RecyclerView checks the
   7401          * API level and sets the size to 0 pre-M to avoid any issue that might be caused by
   7402          * corrupt values. Older platforms have no responsibility to provide a size if they set
   7403          * mode to unspecified.
   7404          */
   7405         private int mWidthMode, mHeightMode;
   7406         private int mWidth, mHeight;
   7407 
   7408 
   7409         /**
   7410          * Interface for LayoutManagers to request items to be prefetched, based on position, with
   7411          * specified distance from viewport, which indicates priority.
   7412          *
   7413          * @see LayoutManager#collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
   7414          * @see LayoutManager#collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
   7415          */
   7416         public interface LayoutPrefetchRegistry {
   7417             /**
   7418              * Requests an an item to be prefetched, based on position, with a specified distance,
   7419              * indicating priority.
   7420              *
   7421              * @param layoutPosition Position of the item to prefetch.
   7422              * @param pixelDistance Distance from the current viewport to the bounds of the item,
   7423              *                      must be non-negative.
   7424              */
   7425             void addPosition(int layoutPosition, int pixelDistance);
   7426         }
   7427 
   7428         void setRecyclerView(RecyclerView recyclerView) {
   7429             if (recyclerView == null) {
   7430                 mRecyclerView = null;
   7431                 mChildHelper = null;
   7432                 mWidth = 0;
   7433                 mHeight = 0;
   7434             } else {
   7435                 mRecyclerView = recyclerView;
   7436                 mChildHelper = recyclerView.mChildHelper;
   7437                 mWidth = recyclerView.getWidth();
   7438                 mHeight = recyclerView.getHeight();
   7439             }
   7440             mWidthMode = MeasureSpec.EXACTLY;
   7441             mHeightMode = MeasureSpec.EXACTLY;
   7442         }
   7443 
   7444         void setMeasureSpecs(int wSpec, int hSpec) {
   7445             mWidth = MeasureSpec.getSize(wSpec);
   7446             mWidthMode = MeasureSpec.getMode(wSpec);
   7447             if (mWidthMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
   7448                 mWidth = 0;
   7449             }
   7450 
   7451             mHeight = MeasureSpec.getSize(hSpec);
   7452             mHeightMode = MeasureSpec.getMode(hSpec);
   7453             if (mHeightMode == MeasureSpec.UNSPECIFIED && !ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
   7454                 mHeight = 0;
   7455             }
   7456         }
   7457 
   7458         /**
   7459          * Called after a layout is calculated during a measure pass when using auto-measure.
   7460          * <p>
   7461          * It simply traverses all children to calculate a bounding box then calls
   7462          * {@link #setMeasuredDimension(Rect, int, int)}. LayoutManagers can override that method
   7463          * if they need to handle the bounding box differently.
   7464          * <p>
   7465          * For example, GridLayoutManager override that method to ensure that even if a column is
   7466          * empty, the GridLayoutManager still measures wide enough to include it.
   7467          *
   7468          * @param widthSpec The widthSpec that was passing into RecyclerView's onMeasure
   7469          * @param heightSpec The heightSpec that was passing into RecyclerView's onMeasure
   7470          */
   7471         void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) {
   7472             final int count = getChildCount();
   7473             if (count == 0) {
   7474                 mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
   7475                 return;
   7476             }
   7477             int minX = Integer.MAX_VALUE;
   7478             int minY = Integer.MAX_VALUE;
   7479             int maxX = Integer.MIN_VALUE;
   7480             int maxY = Integer.MIN_VALUE;
   7481 
   7482             for (int i = 0; i < count; i++) {
   7483                 View child = getChildAt(i);
   7484                 final Rect bounds = mRecyclerView.mTempRect;
   7485                 getDecoratedBoundsWithMargins(child, bounds);
   7486                 if (bounds.left < minX) {
   7487                     minX = bounds.left;
   7488                 }
   7489                 if (bounds.right > maxX) {
   7490                     maxX = bounds.right;
   7491                 }
   7492                 if (bounds.top < minY) {
   7493                     minY = bounds.top;
   7494                 }
   7495                 if (bounds.bottom > maxY) {
   7496                     maxY = bounds.bottom;
   7497                 }
   7498             }
   7499             mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
   7500             setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
   7501         }
   7502 
   7503         /**
   7504          * Sets the measured dimensions from the given bounding box of the children and the
   7505          * measurement specs that were passed into {@link RecyclerView#onMeasure(int, int)}. It is
   7506          * only called if a LayoutManager returns <code>true</code> from
   7507          * {@link #isAutoMeasureEnabled()} and it is called after the RecyclerView calls
   7508          * {@link LayoutManager#onLayoutChildren(Recycler, State)} in the execution of
   7509          * {@link RecyclerView#onMeasure(int, int)}.
   7510          * <p>
   7511          * This method must call {@link #setMeasuredDimension(int, int)}.
   7512          * <p>
   7513          * The default implementation adds the RecyclerView's padding to the given bounding box
   7514          * then caps the value to be within the given measurement specs.
   7515          *
   7516          * @param childrenBounds The bounding box of all children
   7517          * @param wSpec The widthMeasureSpec that was passed into the RecyclerView.
   7518          * @param hSpec The heightMeasureSpec that was passed into the RecyclerView.
   7519          *
   7520          * @see #isAutoMeasureEnabled()
   7521          * @see #setMeasuredDimension(int, int)
   7522          */
   7523         public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
   7524             int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
   7525             int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
   7526             int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
   7527             int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
   7528             setMeasuredDimension(width, height);
   7529         }
   7530 
   7531         /**
   7532          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
   7533          */
   7534         public void requestLayout() {
   7535             if (mRecyclerView != null) {
   7536                 mRecyclerView.requestLayout();
   7537             }
   7538         }
   7539 
   7540         /**
   7541          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   7542          * {@link IllegalStateException} if it <b>is not</b>.
   7543          *
   7544          * @param message The message for the exception. Can be null.
   7545          * @see #assertNotInLayoutOrScroll(String)
   7546          */
   7547         public void assertInLayoutOrScroll(String message) {
   7548             if (mRecyclerView != null) {
   7549                 mRecyclerView.assertInLayoutOrScroll(message);
   7550             }
   7551         }
   7552 
   7553         /**
   7554          * Chooses a size from the given specs and parameters that is closest to the desired size
   7555          * and also complies with the spec.
   7556          *
   7557          * @param spec The measureSpec
   7558          * @param desired The preferred measurement
   7559          * @param min The minimum value
   7560          *
   7561          * @return A size that fits to the given specs
   7562          */
   7563         public static int chooseSize(int spec, int desired, int min) {
   7564             final int mode = View.MeasureSpec.getMode(spec);
   7565             final int size = View.MeasureSpec.getSize(spec);
   7566             switch (mode) {
   7567                 case View.MeasureSpec.EXACTLY:
   7568                     return size;
   7569                 case View.MeasureSpec.AT_MOST:
   7570                     return Math.min(size, Math.max(desired, min));
   7571                 case View.MeasureSpec.UNSPECIFIED:
   7572                 default:
   7573                     return Math.max(desired, min);
   7574             }
   7575         }
   7576 
   7577         /**
   7578          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   7579          * {@link IllegalStateException} if it <b>is</b>.
   7580          *
   7581          * @param message The message for the exception. Can be null.
   7582          * @see #assertInLayoutOrScroll(String)
   7583          */
   7584         public void assertNotInLayoutOrScroll(String message) {
   7585             if (mRecyclerView != null) {
   7586                 mRecyclerView.assertNotInLayoutOrScroll(message);
   7587             }
   7588         }
   7589 
   7590         /**
   7591          * Defines whether the measuring pass of layout should use the AutoMeasure mechanism of
   7592          * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
   7593          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
   7594          *
   7595          * @param enabled <code>True</code> if layout measurement should be done by the
   7596          *                RecyclerView, <code>false</code> if it should be done by this
   7597          *                LayoutManager.
   7598          *
   7599          * @see #isAutoMeasureEnabled()
   7600          *
   7601          * @deprecated Implementors of LayoutManager should define whether or not it uses
   7602          *             AutoMeasure by overriding {@link #isAutoMeasureEnabled()}.
   7603          */
   7604         @Deprecated
   7605         public void setAutoMeasureEnabled(boolean enabled) {
   7606             mAutoMeasure = enabled;
   7607         }
   7608 
   7609         /**
   7610          * Returns whether the measuring pass of layout should use the AutoMeasure mechanism of
   7611          * {@link RecyclerView} or if it should be done by the LayoutManager's implementation of
   7612          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
   7613          * <p>
   7614          * This method returns false by default (it actually returns the value passed to the
   7615          * deprecated {@link #setAutoMeasureEnabled(boolean)}) and should be overridden to return
   7616          * true if a LayoutManager wants to be auto measured by the RecyclerView.
   7617          * <p>
   7618          * If this method is overridden to return true,
   7619          * {@link LayoutManager#onMeasure(Recycler, State, int, int)} should not be overridden.
   7620          * <p>
   7621          * AutoMeasure is a RecyclerView mechanism that handles the measuring pass of layout in a
   7622          * simple and contract satisfying way, including the wrapping of children laid out by
   7623          * LayoutManager. Simply put, it handles wrapping children by calling
   7624          * {@link LayoutManager#onLayoutChildren(Recycler, State)} during a call to
   7625          * {@link RecyclerView#onMeasure(int, int)}, and then calculating desired dimensions based
   7626          * on children's dimensions and positions. It does this while supporting all existing
   7627          * animation capabilities of the RecyclerView.
   7628          * <p>
   7629          * More specifically:
   7630          * <ol>
   7631          * <li>When {@link RecyclerView#onMeasure(int, int)} is called, if the provided measure
   7632          * specs both have a mode of {@link View.MeasureSpec#EXACTLY}, RecyclerView will set its
   7633          * measured dimensions accordingly and return, allowing layout to continue as normal
   7634          * (Actually, RecyclerView will call
   7635          * {@link LayoutManager#onMeasure(Recycler, State, int, int)} for backwards compatibility
   7636          * reasons but it should not be overridden if AutoMeasure is being used).</li>
   7637          * <li>If one of the layout specs is not {@code EXACT}, the RecyclerView will start the
   7638          * layout process. It will first process all pending Adapter updates and
   7639          * then decide whether to run a predictive layout. If it decides to do so, it will first
   7640          * call {@link #onLayoutChildren(Recycler, State)} with {@link State#isPreLayout()} set to
   7641          * {@code true}. At this stage, {@link #getWidth()} and {@link #getHeight()} will still
   7642          * return the width and height of the RecyclerView as of the last layout calculation.
   7643          * <p>
   7644          * After handling the predictive case, RecyclerView will call
   7645          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
   7646          * {@code true} and {@link State#isPreLayout()} set to {@code false}. The LayoutManager can
   7647          * access the measurement specs via {@link #getHeight()}, {@link #getHeightMode()},
   7648          * {@link #getWidth()} and {@link #getWidthMode()}.</li>
   7649          * <li>After the layout calculation, RecyclerView sets the measured width & height by
   7650          * calculating the bounding box for the children (+ RecyclerView's padding). The
   7651          * LayoutManagers can override {@link #setMeasuredDimension(Rect, int, int)} to choose
   7652          * different values. For instance, GridLayoutManager overrides this value to handle the case
   7653          * where if it is vertical and has 3 columns but only 2 items, it should still measure its
   7654          * width to fit 3 items, not 2.</li>
   7655          * <li>Any following calls to {@link RecyclerView#onMeasure(int, int)} will run
   7656          * {@link #onLayoutChildren(Recycler, State)} with {@link State#isMeasuring()} set to
   7657          * {@code true} and {@link State#isPreLayout()} set to {@code false}. RecyclerView will
   7658          * take care of which views are actually added / removed / moved / changed for animations so
   7659          * that the LayoutManager should not worry about them and handle each
   7660          * {@link #onLayoutChildren(Recycler, State)} call as if it is the last one.</li>
   7661          * <li>When measure is complete and RecyclerView's
   7662          * {@link #onLayout(boolean, int, int, int, int)} method is called, RecyclerView checks
   7663          * whether it already did layout calculations during the measure pass and if so, it re-uses
   7664          * that information. It may still decide to call {@link #onLayoutChildren(Recycler, State)}
   7665          * if the last measure spec was different from the final dimensions or adapter contents
   7666          * have changed between the measure call and the layout call.</li>
   7667          * <li>Finally, animations are calculated and run as usual.</li>
   7668          * </ol>
   7669          *
   7670          * @return <code>True</code> if the measuring pass of layout should use the AutoMeasure
   7671          * mechanism of {@link RecyclerView} or <code>False</code> if it should be done by the
   7672          * LayoutManager's implementation of
   7673          * {@link LayoutManager#onMeasure(Recycler, State, int, int)}.
   7674          *
   7675          * @see #setMeasuredDimension(Rect, int, int)
   7676          * @see #onMeasure(Recycler, State, int, int)
   7677          */
   7678         public boolean isAutoMeasureEnabled() {
   7679             return mAutoMeasure;
   7680         }
   7681 
   7682         /**
   7683          * Returns whether this LayoutManager supports "predictive item animations".
   7684          * <p>
   7685          * "Predictive item animations" are automatically created animations that show
   7686          * where items came from, and where they are going to, as items are added, removed,
   7687          * or moved within a layout.
   7688          * <p>
   7689          * A LayoutManager wishing to support predictive item animations must override this
   7690          * method to return true (the default implementation returns false) and must obey certain
   7691          * behavioral contracts outlined in {@link #onLayoutChildren(Recycler, State)}.
   7692          * <p>
   7693          * Whether item animations actually occur in a RecyclerView is actually determined by both
   7694          * the return value from this method and the
   7695          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
   7696          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
   7697          * method returns false, then only "simple item animations" will be enabled in the
   7698          * RecyclerView, in which views whose position are changing are simply faded in/out. If the
   7699          * RecyclerView has a non-null ItemAnimator and this method returns true, then predictive
   7700          * item animations will be enabled in the RecyclerView.
   7701          *
   7702          * @return true if this LayoutManager supports predictive item animations, false otherwise.
   7703          */
   7704         public boolean supportsPredictiveItemAnimations() {
   7705             return false;
   7706         }
   7707 
   7708         /**
   7709          * Sets whether the LayoutManager should be queried for views outside of
   7710          * its viewport while the UI thread is idle between frames.
   7711          *
   7712          * <p>If enabled, the LayoutManager will be queried for items to inflate/bind in between
   7713          * view system traversals on devices running API 21 or greater. Default value is true.</p>
   7714          *
   7715          * <p>On platforms API level 21 and higher, the UI thread is idle between passing a frame
   7716          * to RenderThread and the starting up its next frame at the next VSync pulse. By
   7717          * prefetching out of window views in this time period, delays from inflation and view
   7718          * binding are much less likely to cause jank and stuttering during scrolls and flings.</p>
   7719          *
   7720          * <p>While prefetch is enabled, it will have the side effect of expanding the effective
   7721          * size of the View cache to hold prefetched views.</p>
   7722          *
   7723          * @param enabled <code>True</code> if items should be prefetched in between traversals.
   7724          *
   7725          * @see #isItemPrefetchEnabled()
   7726          */
   7727         public final void setItemPrefetchEnabled(boolean enabled) {
   7728             if (enabled != mItemPrefetchEnabled) {
   7729                 mItemPrefetchEnabled = enabled;
   7730                 mPrefetchMaxCountObserved = 0;
   7731                 if (mRecyclerView != null) {
   7732                     mRecyclerView.mRecycler.updateViewCacheSize();
   7733                 }
   7734             }
   7735         }
   7736 
   7737         /**
   7738          * Sets whether the LayoutManager should be queried for views outside of
   7739          * its viewport while the UI thread is idle between frames.
   7740          *
   7741          * @see #setItemPrefetchEnabled(boolean)
   7742          *
   7743          * @return true if item prefetch is enabled, false otherwise
   7744          */
   7745         public final boolean isItemPrefetchEnabled() {
   7746             return mItemPrefetchEnabled;
   7747         }
   7748 
   7749         /**
   7750          * Gather all positions from the LayoutManager to be prefetched, given specified momentum.
   7751          *
   7752          * <p>If item prefetch is enabled, this method is called in between traversals to gather
   7753          * which positions the LayoutManager will soon need, given upcoming movement in subsequent
   7754          * traversals.</p>
   7755          *
   7756          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
   7757          * each item to be prepared, and these positions will have their ViewHolders created and
   7758          * bound, if there is sufficient time available, in advance of being needed by a
   7759          * scroll or layout.</p>
   7760          *
   7761          * @param dx X movement component.
   7762          * @param dy Y movement component.
   7763          * @param state State of RecyclerView
   7764          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
   7765          *
   7766          * @see #isItemPrefetchEnabled()
   7767          * @see #collectInitialPrefetchPositions(int, LayoutPrefetchRegistry)
   7768          */
   7769         public void collectAdjacentPrefetchPositions(int dx, int dy, State state,
   7770                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
   7771 
   7772         /**
   7773          * Gather all positions from the LayoutManager to be prefetched in preperation for its
   7774          * RecyclerView to come on screen, due to the movement of another, containing RecyclerView.
   7775          *
   7776          * <p>This method is only called when a RecyclerView is nested in another RecyclerView.</p>
   7777          *
   7778          * <p>If item prefetch is enabled for this LayoutManager, as well in another containing
   7779          * LayoutManager, this method is called in between draw traversals to gather
   7780          * which positions this LayoutManager will first need, once it appears on the screen.</p>
   7781          *
   7782          * <p>For example, if this LayoutManager represents a horizontally scrolling list within a
   7783          * vertically scrolling LayoutManager, this method would be called when the horizontal list
   7784          * is about to come onscreen.</p>
   7785          *
   7786          * <p>The LayoutManager should call {@link LayoutPrefetchRegistry#addPosition(int, int)} for
   7787          * each item to be prepared, and these positions will have their ViewHolders created and
   7788          * bound, if there is sufficient time available, in advance of being needed by a
   7789          * scroll or layout.</p>
   7790          *
   7791          * @param adapterItemCount number of items in the associated adapter.
   7792          * @param layoutPrefetchRegistry PrefetchRegistry to add prefetch entries into.
   7793          *
   7794          * @see #isItemPrefetchEnabled()
   7795          * @see #collectAdjacentPrefetchPositions(int, int, State, LayoutPrefetchRegistry)
   7796          */
   7797         public void collectInitialPrefetchPositions(int adapterItemCount,
   7798                 LayoutPrefetchRegistry layoutPrefetchRegistry) {}
   7799 
   7800         void dispatchAttachedToWindow(RecyclerView view) {
   7801             mIsAttachedToWindow = true;
   7802             onAttachedToWindow(view);
   7803         }
   7804 
   7805         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
   7806             mIsAttachedToWindow = false;
   7807             onDetachedFromWindow(view, recycler);
   7808         }
   7809 
   7810         /**
   7811          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
   7812          * to a window.
   7813          *
   7814          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
   7815          * is attached to window.
   7816          */
   7817         public boolean isAttachedToWindow() {
   7818             return mIsAttachedToWindow;
   7819         }
   7820 
   7821         /**
   7822          * Causes the Runnable to execute on the next animation time step.
   7823          * The runnable will be run on the user interface thread.
   7824          * <p>
   7825          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   7826          *
   7827          * @param action The Runnable that will be executed.
   7828          *
   7829          * @see #removeCallbacks
   7830          */
   7831         public void postOnAnimation(Runnable action) {
   7832             if (mRecyclerView != null) {
   7833                 ViewCompat.postOnAnimation(mRecyclerView, action);
   7834             }
   7835         }
   7836 
   7837         /**
   7838          * Removes the specified Runnable from the message queue.
   7839          * <p>
   7840          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   7841          *
   7842          * @param action The Runnable to remove from the message handling queue
   7843          *
   7844          * @return true if RecyclerView could ask the Handler to remove the Runnable,
   7845          *         false otherwise. When the returned value is true, the Runnable
   7846          *         may or may not have been actually removed from the message queue
   7847          *         (for instance, if the Runnable was not in the queue already.)
   7848          *
   7849          * @see #postOnAnimation
   7850          */
   7851         public boolean removeCallbacks(Runnable action) {
   7852             if (mRecyclerView != null) {
   7853                 return mRecyclerView.removeCallbacks(action);
   7854             }
   7855             return false;
   7856         }
   7857         /**
   7858          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
   7859          * is attached to a window.
   7860          * <p>
   7861          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
   7862          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
   7863          * not requested on the RecyclerView while it was detached.
   7864          * <p>
   7865          * Subclass implementations should always call through to the superclass implementation.
   7866          *
   7867          * @param view The RecyclerView this LayoutManager is bound to
   7868          *
   7869          * @see #onDetachedFromWindow(RecyclerView, Recycler)
   7870          */
   7871         @CallSuper
   7872         public void onAttachedToWindow(RecyclerView view) {
   7873         }
   7874 
   7875         /**
   7876          * @deprecated
   7877          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
   7878          */
   7879         @Deprecated
   7880         public void onDetachedFromWindow(RecyclerView view) {
   7881 
   7882         }
   7883 
   7884         /**
   7885          * Called when this LayoutManager is detached from its parent RecyclerView or when
   7886          * its parent RecyclerView is detached from its window.
   7887          * <p>
   7888          * LayoutManager should clear all of its View references as another LayoutManager might be
   7889          * assigned to the RecyclerView.
   7890          * <p>
   7891          * If the RecyclerView is re-attached with the same LayoutManager and Adapter, it may not
   7892          * call {@link #onLayoutChildren(Recycler, State)} if nothing has changed and a layout was
   7893          * not requested on the RecyclerView while it was detached.
   7894          * <p>
   7895          * If your LayoutManager has View references that it cleans in on-detach, it should also
   7896          * call {@link RecyclerView#requestLayout()} to ensure that it is re-laid out when
   7897          * RecyclerView is re-attached.
   7898          * <p>
   7899          * Subclass implementations should always call through to the superclass implementation.
   7900          *
   7901          * @param view The RecyclerView this LayoutManager is bound to
   7902          * @param recycler The recycler to use if you prefer to recycle your children instead of
   7903          *                 keeping them around.
   7904          *
   7905          * @see #onAttachedToWindow(RecyclerView)
   7906          */
   7907         @CallSuper
   7908         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
   7909             onDetachedFromWindow(view);
   7910         }
   7911 
   7912         /**
   7913          * Check if the RecyclerView is configured to clip child views to its padding.
   7914          *
   7915          * @return true if this RecyclerView clips children to its padding, false otherwise
   7916          */
   7917         public boolean getClipToPadding() {
   7918             return mRecyclerView != null && mRecyclerView.mClipToPadding;
   7919         }
   7920 
   7921         /**
   7922          * Lay out all relevant child views from the given adapter.
   7923          *
   7924          * The LayoutManager is in charge of the behavior of item animations. By default,
   7925          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
   7926          * item animations are enabled. This means that add/remove operations on the
   7927          * adapter will result in animations to add new or appearing items, removed or
   7928          * disappearing items, and moved items. If a LayoutManager returns false from
   7929          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
   7930          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
   7931          * RecyclerView will have enough information to run those animations in a simple
   7932          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
   7933          * simply fade views in and out, whether they are actually added/removed or whether
   7934          * they are moved on or off the screen due to other add/remove operations.
   7935          *
   7936          * <p>A LayoutManager wanting a better item animation experience, where items can be
   7937          * animated onto and off of the screen according to where the items exist when they
   7938          * are not on screen, then the LayoutManager should return true from
   7939          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
   7940          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
   7941          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
   7942          * once as a "pre" layout step to determine where items would have been prior to
   7943          * a real layout, and again to do the "real" layout. In the pre-layout phase,
   7944          * items will remember their pre-layout positions to allow them to be laid out
   7945          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
   7946          * be returned from the scrap to help determine correct placement of other items.
   7947          * These removed items should not be added to the child list, but should be used
   7948          * to help calculate correct positioning of other views, including views that
   7949          * were not previously onscreen (referred to as APPEARING views), but whose
   7950          * pre-layout offscreen position can be determined given the extra
   7951          * information about the pre-layout removed views.</p>
   7952          *
   7953          * <p>The second layout pass is the real layout in which only non-removed views
   7954          * will be used. The only additional requirement during this pass is, if
   7955          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
   7956          * views exist in the child list prior to layout and which are not there after
   7957          * layout (referred to as DISAPPEARING views), and to position/layout those views
   7958          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
   7959          * the animation system to know the location to which to animate these disappearing
   7960          * views.</p>
   7961          *
   7962          * <p>The default LayoutManager implementations for RecyclerView handle all of these
   7963          * requirements for animations already. Clients of RecyclerView can either use one
   7964          * of these layout managers directly or look at their implementations of
   7965          * onLayoutChildren() to see how they account for the APPEARING and
   7966          * DISAPPEARING views.</p>
   7967          *
   7968          * @param recycler         Recycler to use for fetching potentially cached views for a
   7969          *                         position
   7970          * @param state            Transient state of RecyclerView
   7971          */
   7972         public void onLayoutChildren(Recycler recycler, State state) {
   7973             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
   7974         }
   7975 
   7976         /**
   7977          * Called after a full layout calculation is finished. The layout calculation may include
   7978          * multiple {@link #onLayoutChildren(Recycler, State)} calls due to animations or
   7979          * layout measurement but it will include only one {@link #onLayoutCompleted(State)} call.
   7980          * This method will be called at the end of {@link View#layout(int, int, int, int)} call.
   7981          * <p>
   7982          * This is a good place for the LayoutManager to do some cleanup like pending scroll
   7983          * position, saved state etc.
   7984          *
   7985          * @param state Transient state of RecyclerView
   7986          */
   7987         public void onLayoutCompleted(State state) {
   7988         }
   7989 
   7990         /**
   7991          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
   7992          *
   7993          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
   7994          * to store extra information specific to the layout. Client code should subclass
   7995          * {@link RecyclerView.LayoutParams} for this purpose.</p>
   7996          *
   7997          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   7998          * you must also override
   7999          * {@link #checkLayoutParams(LayoutParams)},
   8000          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   8001          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   8002          *
   8003          * @return A new LayoutParams for a child view
   8004          */
   8005         public abstract LayoutParams generateDefaultLayoutParams();
   8006 
   8007         /**
   8008          * Determines the validity of the supplied LayoutParams object.
   8009          *
   8010          * <p>This should check to make sure that the object is of the correct type
   8011          * and all values are within acceptable ranges. The default implementation
   8012          * returns <code>true</code> for non-null params.</p>
   8013          *
   8014          * @param lp LayoutParams object to check
   8015          * @return true if this LayoutParams object is valid, false otherwise
   8016          */
   8017         public boolean checkLayoutParams(LayoutParams lp) {
   8018             return lp != null;
   8019         }
   8020 
   8021         /**
   8022          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
   8023          * values from the supplied LayoutParams object if possible.
   8024          *
   8025          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   8026          * you must also override
   8027          * {@link #checkLayoutParams(LayoutParams)},
   8028          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   8029          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   8030          *
   8031          * @param lp Source LayoutParams object to copy values from
   8032          * @return a new LayoutParams object
   8033          */
   8034         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
   8035             if (lp instanceof LayoutParams) {
   8036                 return new LayoutParams((LayoutParams) lp);
   8037             } else if (lp instanceof MarginLayoutParams) {
   8038                 return new LayoutParams((MarginLayoutParams) lp);
   8039             } else {
   8040                 return new LayoutParams(lp);
   8041             }
   8042         }
   8043 
   8044         /**
   8045          * Create a LayoutParams object suitable for this LayoutManager from
   8046          * an inflated layout resource.
   8047          *
   8048          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   8049          * you must also override
   8050          * {@link #checkLayoutParams(LayoutParams)},
   8051          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   8052          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   8053          *
   8054          * @param c Context for obtaining styled attributes
   8055          * @param attrs AttributeSet describing the supplied arguments
   8056          * @return a new LayoutParams object
   8057          */
   8058         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
   8059             return new LayoutParams(c, attrs);
   8060         }
   8061 
   8062         /**
   8063          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
   8064          * The default implementation does nothing and returns 0.
   8065          *
   8066          * @param dx            distance to scroll by in pixels. X increases as scroll position
   8067          *                      approaches the right.
   8068          * @param recycler      Recycler to use for fetching potentially cached views for a
   8069          *                      position
   8070          * @param state         Transient state of RecyclerView
   8071          * @return The actual distance scrolled. The return value will be negative if dx was
   8072          * negative and scrolling proceeeded in that direction.
   8073          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
   8074          */
   8075         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
   8076             return 0;
   8077         }
   8078 
   8079         /**
   8080          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
   8081          * The default implementation does nothing and returns 0.
   8082          *
   8083          * @param dy            distance to scroll in pixels. Y increases as scroll position
   8084          *                      approaches the bottom.
   8085          * @param recycler      Recycler to use for fetching potentially cached views for a
   8086          *                      position
   8087          * @param state         Transient state of RecyclerView
   8088          * @return The actual distance scrolled. The return value will be negative if dy was
   8089          * negative and scrolling proceeeded in that direction.
   8090          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
   8091          */
   8092         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
   8093             return 0;
   8094         }
   8095 
   8096         /**
   8097          * Query if horizontal scrolling is currently supported. The default implementation
   8098          * returns false.
   8099          *
   8100          * @return True if this LayoutManager can scroll the current contents horizontally
   8101          */
   8102         public boolean canScrollHorizontally() {
   8103             return false;
   8104         }
   8105 
   8106         /**
   8107          * Query if vertical scrolling is currently supported. The default implementation
   8108          * returns false.
   8109          *
   8110          * @return True if this LayoutManager can scroll the current contents vertically
   8111          */
   8112         public boolean canScrollVertically() {
   8113             return false;
   8114         }
   8115 
   8116         /**
   8117          * Scroll to the specified adapter position.
   8118          *
   8119          * Actual position of the item on the screen depends on the LayoutManager implementation.
   8120          * @param position Scroll to this adapter position.
   8121          */
   8122         public void scrollToPosition(int position) {
   8123             if (DEBUG) {
   8124                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
   8125             }
   8126         }
   8127 
   8128         /**
   8129          * <p>Smooth scroll to the specified adapter position.</p>
   8130          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
   8131          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
   8132          * </p>
   8133          * @param recyclerView The RecyclerView to which this layout manager is attached
   8134          * @param state    Current State of RecyclerView
   8135          * @param position Scroll to this adapter position.
   8136          */
   8137         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
   8138                 int position) {
   8139             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
   8140         }
   8141 
   8142         /**
   8143          * Starts a smooth scroll using the provided {@link SmoothScroller}.
   8144          *
   8145          * <p>Each instance of SmoothScroller is intended to only be used once. Provide a new
   8146          * SmoothScroller instance each time this method is called.
   8147          *
   8148          * <p>Calling this method will cancel any previous smooth scroll request.
   8149          *
   8150          * @param smoothScroller Instance which defines how smooth scroll should be animated
   8151          */
   8152         public void startSmoothScroll(SmoothScroller smoothScroller) {
   8153             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
   8154                     && mSmoothScroller.isRunning()) {
   8155                 mSmoothScroller.stop();
   8156             }
   8157             mSmoothScroller = smoothScroller;
   8158             mSmoothScroller.start(mRecyclerView, this);
   8159         }
   8160 
   8161         /**
   8162          * @return true if RecyclerView is currently in the state of smooth scrolling.
   8163          */
   8164         public boolean isSmoothScrolling() {
   8165             return mSmoothScroller != null && mSmoothScroller.isRunning();
   8166         }
   8167 
   8168 
   8169         /**
   8170          * Returns the resolved layout direction for this RecyclerView.
   8171          *
   8172          * @return {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
   8173          * direction is RTL or returns
   8174          * {@link androidx.core.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
   8175          * is not RTL.
   8176          */
   8177         public int getLayoutDirection() {
   8178             return ViewCompat.getLayoutDirection(mRecyclerView);
   8179         }
   8180 
   8181         /**
   8182          * Ends all animations on the view created by the {@link ItemAnimator}.
   8183          *
   8184          * @param view The View for which the animations should be ended.
   8185          * @see RecyclerView.ItemAnimator#endAnimations()
   8186          */
   8187         public void endAnimation(View view) {
   8188             if (mRecyclerView.mItemAnimator != null) {
   8189                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
   8190             }
   8191         }
   8192 
   8193         /**
   8194          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   8195          * to the layout that is known to be going away, either because it has been
   8196          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   8197          * visible portion of the container but is being laid out in order to inform RecyclerView
   8198          * in how to animate the item out of view.
   8199          * <p>
   8200          * Views added via this method are going to be invisible to LayoutManager after the
   8201          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   8202          * or won't be included in {@link #getChildCount()} method.
   8203          *
   8204          * @param child View to add and then remove with animation.
   8205          */
   8206         public void addDisappearingView(View child) {
   8207             addDisappearingView(child, -1);
   8208         }
   8209 
   8210         /**
   8211          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   8212          * to the layout that is known to be going away, either because it has been
   8213          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   8214          * visible portion of the container but is being laid out in order to inform RecyclerView
   8215          * in how to animate the item out of view.
   8216          * <p>
   8217          * Views added via this method are going to be invisible to LayoutManager after the
   8218          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   8219          * or won't be included in {@link #getChildCount()} method.
   8220          *
   8221          * @param child View to add and then remove with animation.
   8222          * @param index Index of the view.
   8223          */
   8224         public void addDisappearingView(View child, int index) {
   8225             addViewInt(child, index, true);
   8226         }
   8227 
   8228         /**
   8229          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   8230          * use this method to add views obtained from a {@link Recycler} using
   8231          * {@link Recycler#getViewForPosition(int)}.
   8232          *
   8233          * @param child View to add
   8234          */
   8235         public void addView(View child) {
   8236             addView(child, -1);
   8237         }
   8238 
   8239         /**
   8240          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   8241          * use this method to add views obtained from a {@link Recycler} using
   8242          * {@link Recycler#getViewForPosition(int)}.
   8243          *
   8244          * @param child View to add
   8245          * @param index Index to add child at
   8246          */
   8247         public void addView(View child, int index) {
   8248             addViewInt(child, index, false);
   8249         }
   8250 
   8251         private void addViewInt(View child, int index, boolean disappearing) {
   8252             final ViewHolder holder = getChildViewHolderInt(child);
   8253             if (disappearing || holder.isRemoved()) {
   8254                 // these views will be hidden at the end of the layout pass.
   8255                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
   8256             } else {
   8257                 // This may look like unnecessary but may happen if layout manager supports
   8258                 // predictive layouts and adapter removed then re-added the same item.
   8259                 // In this case, added version will be visible in the post layout (because add is
   8260                 // deferred) but RV will still bind it to the same View.
   8261                 // So if a View re-appears in post layout pass, remove it from disappearing list.
   8262                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
   8263             }
   8264             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8265             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
   8266                 if (holder.isScrap()) {
   8267                     holder.unScrap();
   8268                 } else {
   8269                     holder.clearReturnedFromScrapFlag();
   8270                 }
   8271                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
   8272                 if (DISPATCH_TEMP_DETACH) {
   8273                     ViewCompat.dispatchFinishTemporaryDetach(child);
   8274                 }
   8275             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
   8276                 // ensure in correct position
   8277                 int currentIndex = mChildHelper.indexOfChild(child);
   8278                 if (index == -1) {
   8279                     index = mChildHelper.getChildCount();
   8280                 }
   8281                 if (currentIndex == -1) {
   8282                     throw new IllegalStateException("Added View has RecyclerView as parent but"
   8283                             + " view is not a real child. Unfiltered index:"
   8284                             + mRecyclerView.indexOfChild(child) + mRecyclerView.exceptionLabel());
   8285                 }
   8286                 if (currentIndex != index) {
   8287                     mRecyclerView.mLayout.moveView(currentIndex, index);
   8288                 }
   8289             } else {
   8290                 mChildHelper.addView(child, index, false);
   8291                 lp.mInsetsDirty = true;
   8292                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
   8293                     mSmoothScroller.onChildAttachedToWindow(child);
   8294                 }
   8295             }
   8296             if (lp.mPendingInvalidate) {
   8297                 if (DEBUG) {
   8298                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
   8299                 }
   8300                 holder.itemView.invalidate();
   8301                 lp.mPendingInvalidate = false;
   8302             }
   8303         }
   8304 
   8305         /**
   8306          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   8307          * use this method to completely remove a child view that is no longer needed.
   8308          * LayoutManagers should strongly consider recycling removed views using
   8309          * {@link Recycler#recycleView(android.view.View)}.
   8310          *
   8311          * @param child View to remove
   8312          */
   8313         public void removeView(View child) {
   8314             mChildHelper.removeView(child);
   8315         }
   8316 
   8317         /**
   8318          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   8319          * use this method to completely remove a child view that is no longer needed.
   8320          * LayoutManagers should strongly consider recycling removed views using
   8321          * {@link Recycler#recycleView(android.view.View)}.
   8322          *
   8323          * @param index Index of the child view to remove
   8324          */
   8325         public void removeViewAt(int index) {
   8326             final View child = getChildAt(index);
   8327             if (child != null) {
   8328                 mChildHelper.removeViewAt(index);
   8329             }
   8330         }
   8331 
   8332         /**
   8333          * Remove all views from the currently attached RecyclerView. This will not recycle
   8334          * any of the affected views; the LayoutManager is responsible for doing so if desired.
   8335          */
   8336         public void removeAllViews() {
   8337             // Only remove non-animating views
   8338             final int childCount = getChildCount();
   8339             for (int i = childCount - 1; i >= 0; i--) {
   8340                 mChildHelper.removeViewAt(i);
   8341             }
   8342         }
   8343 
   8344         /**
   8345          * Returns offset of the RecyclerView's text baseline from the its top boundary.
   8346          *
   8347          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
   8348          * there is no baseline.
   8349          */
   8350         public int getBaseline() {
   8351             return -1;
   8352         }
   8353 
   8354         /**
   8355          * Returns the adapter position of the item represented by the given View. This does not
   8356          * contain any adapter changes that might have happened after the last layout.
   8357          *
   8358          * @param view The view to query
   8359          * @return The adapter position of the item which is rendered by this View.
   8360          */
   8361         public int getPosition(@NonNull View view) {
   8362             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
   8363         }
   8364 
   8365         /**
   8366          * Returns the View type defined by the adapter.
   8367          *
   8368          * @param view The view to query
   8369          * @return The type of the view assigned by the adapter.
   8370          */
   8371         public int getItemViewType(@NonNull View view) {
   8372             return getChildViewHolderInt(view).getItemViewType();
   8373         }
   8374 
   8375         /**
   8376          * Traverses the ancestors of the given view and returns the item view that contains it
   8377          * and also a direct child of the LayoutManager.
   8378          * <p>
   8379          * Note that this method may return null if the view is a child of the RecyclerView but
   8380          * not a child of the LayoutManager (e.g. running a disappear animation).
   8381          *
   8382          * @param view The view that is a descendant of the LayoutManager.
   8383          *
   8384          * @return The direct child of the LayoutManager which contains the given view or null if
   8385          * the provided view is not a descendant of this LayoutManager.
   8386          *
   8387          * @see RecyclerView#getChildViewHolder(View)
   8388          * @see RecyclerView#findContainingViewHolder(View)
   8389          */
   8390         @Nullable
   8391         public View findContainingItemView(@NonNull View view) {
   8392             if (mRecyclerView == null) {
   8393                 return null;
   8394             }
   8395             View found = mRecyclerView.findContainingItemView(view);
   8396             if (found == null) {
   8397                 return null;
   8398             }
   8399             if (mChildHelper.isHidden(found)) {
   8400                 return null;
   8401             }
   8402             return found;
   8403         }
   8404 
   8405         /**
   8406          * Finds the view which represents the given adapter position.
   8407          * <p>
   8408          * This method traverses each child since it has no information about child order.
   8409          * Override this method to improve performance if your LayoutManager keeps data about
   8410          * child views.
   8411          * <p>
   8412          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
   8413          *
   8414          * @param position Position of the item in adapter
   8415          * @return The child view that represents the given position or null if the position is not
   8416          * laid out
   8417          */
   8418         @Nullable
   8419         public View findViewByPosition(int position) {
   8420             final int childCount = getChildCount();
   8421             for (int i = 0; i < childCount; i++) {
   8422                 View child = getChildAt(i);
   8423                 ViewHolder vh = getChildViewHolderInt(child);
   8424                 if (vh == null) {
   8425                     continue;
   8426                 }
   8427                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore()
   8428                         && (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
   8429                     return child;
   8430                 }
   8431             }
   8432             return null;
   8433         }
   8434 
   8435         /**
   8436          * Temporarily detach a child view.
   8437          *
   8438          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   8439          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   8440          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   8441          * so that the detached view may be rebound and reused.</p>
   8442          *
   8443          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   8444          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   8445          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   8446          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   8447          *
   8448          * @param child Child to detach
   8449          */
   8450         public void detachView(@NonNull View child) {
   8451             final int ind = mChildHelper.indexOfChild(child);
   8452             if (ind >= 0) {
   8453                 detachViewInternal(ind, child);
   8454             }
   8455         }
   8456 
   8457         /**
   8458          * Temporarily detach a child view.
   8459          *
   8460          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   8461          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   8462          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   8463          * so that the detached view may be rebound and reused.</p>
   8464          *
   8465          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   8466          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   8467          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   8468          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   8469          *
   8470          * @param index Index of the child to detach
   8471          */
   8472         public void detachViewAt(int index) {
   8473             detachViewInternal(index, getChildAt(index));
   8474         }
   8475 
   8476         private void detachViewInternal(int index, @NonNull View view) {
   8477             if (DISPATCH_TEMP_DETACH) {
   8478                 ViewCompat.dispatchStartTemporaryDetach(view);
   8479             }
   8480             mChildHelper.detachViewFromParent(index);
   8481         }
   8482 
   8483         /**
   8484          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   8485          * This method should not be used to reattach views that were previously
   8486          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   8487          *
   8488          * @param child Child to reattach
   8489          * @param index Intended child index for child
   8490          * @param lp LayoutParams for child
   8491          */
   8492         public void attachView(@NonNull View child, int index, LayoutParams lp) {
   8493             ViewHolder vh = getChildViewHolderInt(child);
   8494             if (vh.isRemoved()) {
   8495                 mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
   8496             } else {
   8497                 mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
   8498             }
   8499             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
   8500             if (DISPATCH_TEMP_DETACH)  {
   8501                 ViewCompat.dispatchFinishTemporaryDetach(child);
   8502             }
   8503         }
   8504 
   8505         /**
   8506          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   8507          * This method should not be used to reattach views that were previously
   8508          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   8509          *
   8510          * @param child Child to reattach
   8511          * @param index Intended child index for child
   8512          */
   8513         public void attachView(@NonNull View child, int index) {
   8514             attachView(child, index, (LayoutParams) child.getLayoutParams());
   8515         }
   8516 
   8517         /**
   8518          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   8519          * This method should not be used to reattach views that were previously
   8520          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   8521          *
   8522          * @param child Child to reattach
   8523          */
   8524         public void attachView(@NonNull View child) {
   8525             attachView(child, -1);
   8526         }
   8527 
   8528         /**
   8529          * Finish removing a view that was previously temporarily
   8530          * {@link #detachView(android.view.View) detached}.
   8531          *
   8532          * @param child Detached child to remove
   8533          */
   8534         public void removeDetachedView(@NonNull View child) {
   8535             mRecyclerView.removeDetachedView(child, false);
   8536         }
   8537 
   8538         /**
   8539          * Moves a View from one position to another.
   8540          *
   8541          * @param fromIndex The View's initial index
   8542          * @param toIndex The View's target index
   8543          */
   8544         public void moveView(int fromIndex, int toIndex) {
   8545             View view = getChildAt(fromIndex);
   8546             if (view == null) {
   8547                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
   8548                         + fromIndex + mRecyclerView.toString());
   8549             }
   8550             detachViewAt(fromIndex);
   8551             attachView(view, toIndex);
   8552         }
   8553 
   8554         /**
   8555          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
   8556          *
   8557          * <p>Scrapping a view allows it to be rebound and reused to show updated or
   8558          * different data.</p>
   8559          *
   8560          * @param child Child to detach and scrap
   8561          * @param recycler Recycler to deposit the new scrap view into
   8562          */
   8563         public void detachAndScrapView(@NonNull View child, @NonNull Recycler recycler) {
   8564             int index = mChildHelper.indexOfChild(child);
   8565             scrapOrRecycleView(recycler, index, child);
   8566         }
   8567 
   8568         /**
   8569          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
   8570          *
   8571          * <p>Scrapping a view allows it to be rebound and reused to show updated or
   8572          * different data.</p>
   8573          *
   8574          * @param index Index of child to detach and scrap
   8575          * @param recycler Recycler to deposit the new scrap view into
   8576          */
   8577         public void detachAndScrapViewAt(int index, @NonNull Recycler recycler) {
   8578             final View child = getChildAt(index);
   8579             scrapOrRecycleView(recycler, index, child);
   8580         }
   8581 
   8582         /**
   8583          * Remove a child view and recycle it using the given Recycler.
   8584          *
   8585          * @param child Child to remove and recycle
   8586          * @param recycler Recycler to use to recycle child
   8587          */
   8588         public void removeAndRecycleView(@NonNull View child, @NonNull Recycler recycler) {
   8589             removeView(child);
   8590             recycler.recycleView(child);
   8591         }
   8592 
   8593         /**
   8594          * Remove a child view and recycle it using the given Recycler.
   8595          *
   8596          * @param index Index of child to remove and recycle
   8597          * @param recycler Recycler to use to recycle child
   8598          */
   8599         public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
   8600             final View view = getChildAt(index);
   8601             removeViewAt(index);
   8602             recycler.recycleView(view);
   8603         }
   8604 
   8605         /**
   8606          * Return the current number of child views attached to the parent RecyclerView.
   8607          * This does not include child views that were temporarily detached and/or scrapped.
   8608          *
   8609          * @return Number of attached children
   8610          */
   8611         public int getChildCount() {
   8612             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
   8613         }
   8614 
   8615         /**
   8616          * Return the child view at the given index
   8617          * @param index Index of child to return
   8618          * @return Child view at index
   8619          */
   8620         @Nullable
   8621         public View getChildAt(int index) {
   8622             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
   8623         }
   8624 
   8625         /**
   8626          * Return the width measurement spec mode that is currently relevant to the LayoutManager.
   8627          *
   8628          * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
   8629          * {@link #setAutoMeasureEnabled(boolean)}.
   8630          *
   8631          * <p>When RecyclerView is running a layout, this value is always set to
   8632          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
   8633          *
   8634          * @return Width measure spec mode
   8635          *
   8636          * @see View.MeasureSpec#getMode(int)
   8637          */
   8638         public int getWidthMode() {
   8639             return mWidthMode;
   8640         }
   8641 
   8642         /**
   8643          * Return the height measurement spec mode that is currently relevant to the LayoutManager.
   8644          *
   8645          * <p>This value is set only if the LayoutManager opts into the AutoMeasure api via
   8646          * {@link #setAutoMeasureEnabled(boolean)}.
   8647          *
   8648          * <p>When RecyclerView is running a layout, this value is always set to
   8649          * {@link View.MeasureSpec#EXACTLY} even if it was measured with a different spec mode.
   8650          *
   8651          * @return Height measure spec mode
   8652          *
   8653          * @see View.MeasureSpec#getMode(int)
   8654          */
   8655         public int getHeightMode() {
   8656             return mHeightMode;
   8657         }
   8658 
   8659         /**
   8660          * Returns the width that is currently relevant to the LayoutManager.
   8661          *
   8662          * <p>This value is usually equal to the laid out width of the {@link RecyclerView} but may
   8663          * reflect the current {@link android.view.View.MeasureSpec} width if the
   8664          * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
   8665          * measuring. The LayoutManager must always use this method to retrieve the width relevant
   8666          * to it at any given time.
   8667          *
   8668          * @return Width in pixels
   8669          */
   8670         @Px
   8671         public int getWidth() {
   8672             return mWidth;
   8673         }
   8674 
   8675         /**
   8676          * Returns the height that is currently relevant to the LayoutManager.
   8677          *
   8678          * <p>This value is usually equal to the laid out height of the {@link RecyclerView} but may
   8679          * reflect the current {@link android.view.View.MeasureSpec} height if the
   8680          * {@link LayoutManager} is using AutoMeasure and the RecyclerView is in the process of
   8681          * measuring. The LayoutManager must always use this method to retrieve the height relevant
   8682          * to it at any given time.
   8683          *
   8684          * @return Height in pixels
   8685          */
   8686         @Px
   8687         public int getHeight() {
   8688             return mHeight;
   8689         }
   8690 
   8691         /**
   8692          * Return the left padding of the parent RecyclerView
   8693          *
   8694          * @return Padding in pixels
   8695          */
   8696         @Px
   8697         public int getPaddingLeft() {
   8698             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
   8699         }
   8700 
   8701         /**
   8702          * Return the top padding of the parent RecyclerView
   8703          *
   8704          * @return Padding in pixels
   8705          */
   8706         @Px
   8707         public int getPaddingTop() {
   8708             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
   8709         }
   8710 
   8711         /**
   8712          * Return the right padding of the parent RecyclerView
   8713          *
   8714          * @return Padding in pixels
   8715          */
   8716         @Px
   8717         public int getPaddingRight() {
   8718             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
   8719         }
   8720 
   8721         /**
   8722          * Return the bottom padding of the parent RecyclerView
   8723          *
   8724          * @return Padding in pixels
   8725          */
   8726         @Px
   8727         public int getPaddingBottom() {
   8728             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
   8729         }
   8730 
   8731         /**
   8732          * Return the start padding of the parent RecyclerView
   8733          *
   8734          * @return Padding in pixels
   8735          */
   8736         @Px
   8737         public int getPaddingStart() {
   8738             return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
   8739         }
   8740 
   8741         /**
   8742          * Return the end padding of the parent RecyclerView
   8743          *
   8744          * @return Padding in pixels
   8745          */
   8746         @Px
   8747         public int getPaddingEnd() {
   8748             return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
   8749         }
   8750 
   8751         /**
   8752          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
   8753          *
   8754          * @return True if the RecyclerView has focus, false otherwise.
   8755          * @see View#isFocused()
   8756          */
   8757         public boolean isFocused() {
   8758             return mRecyclerView != null && mRecyclerView.isFocused();
   8759         }
   8760 
   8761         /**
   8762          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
   8763          *
   8764          * @return true if the RecyclerView has or contains focus
   8765          * @see View#hasFocus()
   8766          */
   8767         public boolean hasFocus() {
   8768             return mRecyclerView != null && mRecyclerView.hasFocus();
   8769         }
   8770 
   8771         /**
   8772          * Returns the item View which has or contains focus.
   8773          *
   8774          * @return A direct child of RecyclerView which has focus or contains the focused child.
   8775          */
   8776         @Nullable
   8777         public View getFocusedChild() {
   8778             if (mRecyclerView == null) {
   8779                 return null;
   8780             }
   8781             final View focused = mRecyclerView.getFocusedChild();
   8782             if (focused == null || mChildHelper.isHidden(focused)) {
   8783                 return null;
   8784             }
   8785             return focused;
   8786         }
   8787 
   8788         /**
   8789          * Returns the number of items in the adapter bound to the parent RecyclerView.
   8790          * <p>
   8791          * Note that this number is not necessarily equal to
   8792          * {@link State#getItemCount() State#getItemCount()}. In methods where {@link State} is
   8793          * available, you should use {@link State#getItemCount() State#getItemCount()} instead.
   8794          * For more details, check the documentation for
   8795          * {@link State#getItemCount() State#getItemCount()}.
   8796          *
   8797          * @return The number of items in the bound adapter
   8798          * @see State#getItemCount()
   8799          */
   8800         public int getItemCount() {
   8801             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
   8802             return a != null ? a.getItemCount() : 0;
   8803         }
   8804 
   8805         /**
   8806          * Offset all child views attached to the parent RecyclerView by dx pixels along
   8807          * the horizontal axis.
   8808          *
   8809          * @param dx Pixels to offset by
   8810          */
   8811         public void offsetChildrenHorizontal(@Px int dx) {
   8812             if (mRecyclerView != null) {
   8813                 mRecyclerView.offsetChildrenHorizontal(dx);
   8814             }
   8815         }
   8816 
   8817         /**
   8818          * Offset all child views attached to the parent RecyclerView by dy pixels along
   8819          * the vertical axis.
   8820          *
   8821          * @param dy Pixels to offset by
   8822          */
   8823         public void offsetChildrenVertical(@Px int dy) {
   8824             if (mRecyclerView != null) {
   8825                 mRecyclerView.offsetChildrenVertical(dy);
   8826             }
   8827         }
   8828 
   8829         /**
   8830          * Flags a view so that it will not be scrapped or recycled.
   8831          * <p>
   8832          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
   8833          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
   8834          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
   8835          * ignore the child.
   8836          * <p>
   8837          * Before this child can be recycled again, you have to call
   8838          * {@link #stopIgnoringView(View)}.
   8839          * <p>
   8840          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   8841          *
   8842          * @param view View to ignore.
   8843          * @see #stopIgnoringView(View)
   8844          */
   8845         public void ignoreView(@NonNull View view) {
   8846             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
   8847                 // checking this because calling this method on a recycled or detached view may
   8848                 // cause loss of state.
   8849                 throw new IllegalArgumentException("View should be fully attached to be ignored"
   8850                         + mRecyclerView.exceptionLabel());
   8851             }
   8852             final ViewHolder vh = getChildViewHolderInt(view);
   8853             vh.addFlags(ViewHolder.FLAG_IGNORE);
   8854             mRecyclerView.mViewInfoStore.removeViewHolder(vh);
   8855         }
   8856 
   8857         /**
   8858          * View can be scrapped and recycled again.
   8859          * <p>
   8860          * Note that calling this method removes all information in the view holder.
   8861          * <p>
   8862          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   8863          *
   8864          * @param view View to ignore.
   8865          */
   8866         public void stopIgnoringView(@NonNull View view) {
   8867             final ViewHolder vh = getChildViewHolderInt(view);
   8868             vh.stopIgnoring();
   8869             vh.resetInternal();
   8870             vh.addFlags(ViewHolder.FLAG_INVALID);
   8871         }
   8872 
   8873         /**
   8874          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
   8875          * into the given Recycler. The Recycler may prefer to reuse scrap views before
   8876          * other views that were previously recycled.
   8877          *
   8878          * @param recycler Recycler to scrap views into
   8879          */
   8880         public void detachAndScrapAttachedViews(@NonNull Recycler recycler) {
   8881             final int childCount = getChildCount();
   8882             for (int i = childCount - 1; i >= 0; i--) {
   8883                 final View v = getChildAt(i);
   8884                 scrapOrRecycleView(recycler, i, v);
   8885             }
   8886         }
   8887 
   8888         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
   8889             final ViewHolder viewHolder = getChildViewHolderInt(view);
   8890             if (viewHolder.shouldIgnore()) {
   8891                 if (DEBUG) {
   8892                     Log.d(TAG, "ignoring view " + viewHolder);
   8893                 }
   8894                 return;
   8895             }
   8896             if (viewHolder.isInvalid() && !viewHolder.isRemoved()
   8897                     && !mRecyclerView.mAdapter.hasStableIds()) {
   8898                 removeViewAt(index);
   8899                 recycler.recycleViewHolderInternal(viewHolder);
   8900             } else {
   8901                 detachViewAt(index);
   8902                 recycler.scrapView(view);
   8903                 mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
   8904             }
   8905         }
   8906 
   8907         /**
   8908          * Recycles the scrapped views.
   8909          * <p>
   8910          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
   8911          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
   8912          * call remove and invalidate RecyclerView to ensure UI update.
   8913          *
   8914          * @param recycler Recycler
   8915          */
   8916         void removeAndRecycleScrapInt(Recycler recycler) {
   8917             final int scrapCount = recycler.getScrapCount();
   8918             // Loop backward, recycler might be changed by removeDetachedView()
   8919             for (int i = scrapCount - 1; i >= 0; i--) {
   8920                 final View scrap = recycler.getScrapViewAt(i);
   8921                 final ViewHolder vh = getChildViewHolderInt(scrap);
   8922                 if (vh.shouldIgnore()) {
   8923                     continue;
   8924                 }
   8925                 // If the scrap view is animating, we need to cancel them first. If we cancel it
   8926                 // here, ItemAnimator callback may recycle it which will cause double recycling.
   8927                 // To avoid this, we mark it as not recycleable before calling the item animator.
   8928                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
   8929                 // the view) may recycle it too, so we guard it before we call user APIs.
   8930                 vh.setIsRecyclable(false);
   8931                 if (vh.isTmpDetached()) {
   8932                     mRecyclerView.removeDetachedView(scrap, false);
   8933                 }
   8934                 if (mRecyclerView.mItemAnimator != null) {
   8935                     mRecyclerView.mItemAnimator.endAnimation(vh);
   8936                 }
   8937                 vh.setIsRecyclable(true);
   8938                 recycler.quickRecycleScrapView(scrap);
   8939             }
   8940             recycler.clearScrap();
   8941             if (scrapCount > 0) {
   8942                 mRecyclerView.invalidate();
   8943             }
   8944         }
   8945 
   8946 
   8947         /**
   8948          * Measure a child view using standard measurement policy, taking the padding
   8949          * of the parent RecyclerView and any added item decorations into account.
   8950          *
   8951          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   8952          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   8953          *
   8954          * @param child Child view to measure
   8955          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   8956          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   8957          */
   8958         public void measureChild(@NonNull View child, int widthUsed, int heightUsed) {
   8959             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   8960 
   8961             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   8962             widthUsed += insets.left + insets.right;
   8963             heightUsed += insets.top + insets.bottom;
   8964             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
   8965                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
   8966                     canScrollHorizontally());
   8967             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
   8968                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
   8969                     canScrollVertically());
   8970             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
   8971                 child.measure(widthSpec, heightSpec);
   8972             }
   8973         }
   8974 
   8975         /**
   8976          * RecyclerView internally does its own View measurement caching which should help with
   8977          * WRAP_CONTENT.
   8978          * <p>
   8979          * Use this method if the View is already measured once in this layout pass.
   8980          */
   8981         boolean shouldReMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
   8982             return !mMeasurementCacheEnabled
   8983                     || !isMeasurementUpToDate(child.getMeasuredWidth(), widthSpec, lp.width)
   8984                     || !isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
   8985         }
   8986 
   8987         // we may consider making this public
   8988         /**
   8989          * RecyclerView internally does its own View measurement caching which should help with
   8990          * WRAP_CONTENT.
   8991          * <p>
   8992          * Use this method if the View is not yet measured and you need to decide whether to
   8993          * measure this View or not.
   8994          */
   8995         boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, LayoutParams lp) {
   8996             return child.isLayoutRequested()
   8997                     || !mMeasurementCacheEnabled
   8998                     || !isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
   8999                     || !isMeasurementUpToDate(child.getHeight(), heightSpec, lp.height);
   9000         }
   9001 
   9002         /**
   9003          * In addition to the View Framework's measurement cache, RecyclerView uses its own
   9004          * additional measurement cache for its children to avoid re-measuring them when not
   9005          * necessary. It is on by default but it can be turned off via
   9006          * {@link #setMeasurementCacheEnabled(boolean)}.
   9007          *
   9008          * @return True if measurement cache is enabled, false otherwise.
   9009          *
   9010          * @see #setMeasurementCacheEnabled(boolean)
   9011          */
   9012         public boolean isMeasurementCacheEnabled() {
   9013             return mMeasurementCacheEnabled;
   9014         }
   9015 
   9016         /**
   9017          * Sets whether RecyclerView should use its own measurement cache for the children. This is
   9018          * a more aggressive cache than the framework uses.
   9019          *
   9020          * @param measurementCacheEnabled True to enable the measurement cache, false otherwise.
   9021          *
   9022          * @see #isMeasurementCacheEnabled()
   9023          */
   9024         public void setMeasurementCacheEnabled(boolean measurementCacheEnabled) {
   9025             mMeasurementCacheEnabled = measurementCacheEnabled;
   9026         }
   9027 
   9028         private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) {
   9029             final int specMode = MeasureSpec.getMode(spec);
   9030             final int specSize = MeasureSpec.getSize(spec);
   9031             if (dimension > 0 && childSize != dimension) {
   9032                 return false;
   9033             }
   9034             switch (specMode) {
   9035                 case MeasureSpec.UNSPECIFIED:
   9036                     return true;
   9037                 case MeasureSpec.AT_MOST:
   9038                     return specSize >= childSize;
   9039                 case MeasureSpec.EXACTLY:
   9040                     return  specSize == childSize;
   9041             }
   9042             return false;
   9043         }
   9044 
   9045         /**
   9046          * Measure a child view using standard measurement policy, taking the padding
   9047          * of the parent RecyclerView, any added item decorations and the child margins
   9048          * into account.
   9049          *
   9050          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   9051          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   9052          *
   9053          * @param child Child view to measure
   9054          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   9055          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   9056          */
   9057         public void measureChildWithMargins(@NonNull View child, int widthUsed, int heightUsed) {
   9058             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   9059 
   9060             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   9061             widthUsed += insets.left + insets.right;
   9062             heightUsed += insets.top + insets.bottom;
   9063 
   9064             final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
   9065                     getPaddingLeft() + getPaddingRight()
   9066                             + lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
   9067                     canScrollHorizontally());
   9068             final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
   9069                     getPaddingTop() + getPaddingBottom()
   9070                             + lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
   9071                     canScrollVertically());
   9072             if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
   9073                 child.measure(widthSpec, heightSpec);
   9074             }
   9075         }
   9076 
   9077         /**
   9078          * Calculate a MeasureSpec value for measuring a child view in one dimension.
   9079          *
   9080          * @param parentSize Size of the parent view where the child will be placed
   9081          * @param padding Total space currently consumed by other elements of the parent
   9082          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
   9083          *                       Generally obtained from the child view's LayoutParams
   9084          * @param canScroll true if the parent RecyclerView can scroll in this dimension
   9085          *
   9086          * @return a MeasureSpec value for the child view
   9087          * @deprecated use {@link #getChildMeasureSpec(int, int, int, int, boolean)}
   9088          */
   9089         @Deprecated
   9090         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
   9091                 boolean canScroll) {
   9092             int size = Math.max(0, parentSize - padding);
   9093             int resultSize = 0;
   9094             int resultMode = 0;
   9095             if (canScroll) {
   9096                 if (childDimension >= 0) {
   9097                     resultSize = childDimension;
   9098                     resultMode = MeasureSpec.EXACTLY;
   9099                 } else {
   9100                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
   9101                     // instead using UNSPECIFIED.
   9102                     resultSize = 0;
   9103                     resultMode = MeasureSpec.UNSPECIFIED;
   9104                 }
   9105             } else {
   9106                 if (childDimension >= 0) {
   9107                     resultSize = childDimension;
   9108                     resultMode = MeasureSpec.EXACTLY;
   9109                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   9110                     resultSize = size;
   9111                     // TODO this should be my spec.
   9112                     resultMode = MeasureSpec.EXACTLY;
   9113                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   9114                     resultSize = size;
   9115                     resultMode = MeasureSpec.AT_MOST;
   9116                 }
   9117             }
   9118             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
   9119         }
   9120 
   9121         /**
   9122          * Calculate a MeasureSpec value for measuring a child view in one dimension.
   9123          *
   9124          * @param parentSize Size of the parent view where the child will be placed
   9125          * @param parentMode The measurement spec mode of the parent
   9126          * @param padding Total space currently consumed by other elements of parent
   9127          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
   9128          *                       Generally obtained from the child view's LayoutParams
   9129          * @param canScroll true if the parent RecyclerView can scroll in this dimension
   9130          *
   9131          * @return a MeasureSpec value for the child view
   9132          */
   9133         public static int getChildMeasureSpec(int parentSize, int parentMode, int padding,
   9134                 int childDimension, boolean canScroll) {
   9135             int size = Math.max(0, parentSize - padding);
   9136             int resultSize = 0;
   9137             int resultMode = 0;
   9138             if (canScroll) {
   9139                 if (childDimension >= 0) {
   9140                     resultSize = childDimension;
   9141                     resultMode = MeasureSpec.EXACTLY;
   9142                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   9143                     switch (parentMode) {
   9144                         case MeasureSpec.AT_MOST:
   9145                         case MeasureSpec.EXACTLY:
   9146                             resultSize = size;
   9147                             resultMode = parentMode;
   9148                             break;
   9149                         case MeasureSpec.UNSPECIFIED:
   9150                             resultSize = 0;
   9151                             resultMode = MeasureSpec.UNSPECIFIED;
   9152                             break;
   9153                     }
   9154                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   9155                     resultSize = 0;
   9156                     resultMode = MeasureSpec.UNSPECIFIED;
   9157                 }
   9158             } else {
   9159                 if (childDimension >= 0) {
   9160                     resultSize = childDimension;
   9161                     resultMode = MeasureSpec.EXACTLY;
   9162                 } else if (childDimension == LayoutParams.MATCH_PARENT) {
   9163                     resultSize = size;
   9164                     resultMode = parentMode;
   9165                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   9166                     resultSize = size;
   9167                     if (parentMode == MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
   9168                         resultMode = MeasureSpec.AT_MOST;
   9169                     } else {
   9170                         resultMode = MeasureSpec.UNSPECIFIED;
   9171                     }
   9172 
   9173                 }
   9174             }
   9175             //noinspection WrongConstant
   9176             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
   9177         }
   9178 
   9179         /**
   9180          * Returns the measured width of the given child, plus the additional size of
   9181          * any insets applied by {@link ItemDecoration ItemDecorations}.
   9182          *
   9183          * @param child Child view to query
   9184          * @return child's measured width plus <code>ItemDecoration</code> insets
   9185          *
   9186          * @see View#getMeasuredWidth()
   9187          */
   9188         public int getDecoratedMeasuredWidth(@NonNull View child) {
   9189             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   9190             return child.getMeasuredWidth() + insets.left + insets.right;
   9191         }
   9192 
   9193         /**
   9194          * Returns the measured height of the given child, plus the additional size of
   9195          * any insets applied by {@link ItemDecoration ItemDecorations}.
   9196          *
   9197          * @param child Child view to query
   9198          * @return child's measured height plus <code>ItemDecoration</code> insets
   9199          *
   9200          * @see View#getMeasuredHeight()
   9201          */
   9202         public int getDecoratedMeasuredHeight(@NonNull View child) {
   9203             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   9204             return child.getMeasuredHeight() + insets.top + insets.bottom;
   9205         }
   9206 
   9207         /**
   9208          * Lay out the given child view within the RecyclerView using coordinates that
   9209          * include any current {@link ItemDecoration ItemDecorations}.
   9210          *
   9211          * <p>LayoutManagers should prefer working in sizes and coordinates that include
   9212          * item decoration insets whenever possible. This allows the LayoutManager to effectively
   9213          * ignore decoration insets within measurement and layout code. See the following
   9214          * methods:</p>
   9215          * <ul>
   9216          *     <li>{@link #layoutDecoratedWithMargins(View, int, int, int, int)}</li>
   9217          *     <li>{@link #getDecoratedBoundsWithMargins(View, Rect)}</li>
   9218          *     <li>{@link #measureChild(View, int, int)}</li>
   9219          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
   9220          *     <li>{@link #getDecoratedLeft(View)}</li>
   9221          *     <li>{@link #getDecoratedTop(View)}</li>
   9222          *     <li>{@link #getDecoratedRight(View)}</li>
   9223          *     <li>{@link #getDecoratedBottom(View)}</li>
   9224          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
   9225          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
   9226          * </ul>
   9227          *
   9228          * @param child Child to lay out
   9229          * @param left Left edge, with item decoration insets included
   9230          * @param top Top edge, with item decoration insets included
   9231          * @param right Right edge, with item decoration insets included
   9232          * @param bottom Bottom edge, with item decoration insets included
   9233          *
   9234          * @see View#layout(int, int, int, int)
   9235          * @see #layoutDecoratedWithMargins(View, int, int, int, int)
   9236          */
   9237         public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
   9238             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   9239             child.layout(left + insets.left, top + insets.top, right - insets.right,
   9240                     bottom - insets.bottom);
   9241         }
   9242 
   9243         /**
   9244          * Lay out the given child view within the RecyclerView using coordinates that
   9245          * include any current {@link ItemDecoration ItemDecorations} and margins.
   9246          *
   9247          * <p>LayoutManagers should prefer working in sizes and coordinates that include
   9248          * item decoration insets whenever possible. This allows the LayoutManager to effectively
   9249          * ignore decoration insets within measurement and layout code. See the following
   9250          * methods:</p>
   9251          * <ul>
   9252          *     <li>{@link #layoutDecorated(View, int, int, int, int)}</li>
   9253          *     <li>{@link #measureChild(View, int, int)}</li>
   9254          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
   9255          *     <li>{@link #getDecoratedLeft(View)}</li>
   9256          *     <li>{@link #getDecoratedTop(View)}</li>
   9257          *     <li>{@link #getDecoratedRight(View)}</li>
   9258          *     <li>{@link #getDecoratedBottom(View)}</li>
   9259          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
   9260          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
   9261          * </ul>
   9262          *
   9263          * @param child Child to lay out
   9264          * @param left Left edge, with item decoration insets and left margin included
   9265          * @param top Top edge, with item decoration insets and top margin included
   9266          * @param right Right edge, with item decoration insets and right margin included
   9267          * @param bottom Bottom edge, with item decoration insets and bottom margin included
   9268          *
   9269          * @see View#layout(int, int, int, int)
   9270          * @see #layoutDecorated(View, int, int, int, int)
   9271          */
   9272         public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
   9273                 int bottom) {
   9274             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   9275             final Rect insets = lp.mDecorInsets;
   9276             child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
   9277                     right - insets.right - lp.rightMargin,
   9278                     bottom - insets.bottom - lp.bottomMargin);
   9279         }
   9280 
   9281         /**
   9282          * Calculates the bounding box of the View while taking into account its matrix changes
   9283          * (translation, scale etc) with respect to the RecyclerView.
   9284          * <p>
   9285          * If {@code includeDecorInsets} is {@code true}, they are applied first before applying
   9286          * the View's matrix so that the decor offsets also go through the same transformation.
   9287          *
   9288          * @param child The ItemView whose bounding box should be calculated.
   9289          * @param includeDecorInsets True if the decor insets should be included in the bounding box
   9290          * @param out The rectangle into which the output will be written.
   9291          */
   9292         public void getTransformedBoundingBox(@NonNull View child, boolean includeDecorInsets,
   9293                 @NonNull Rect out) {
   9294             if (includeDecorInsets) {
   9295                 Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   9296                 out.set(-insets.left, -insets.top,
   9297                         child.getWidth() + insets.right, child.getHeight() + insets.bottom);
   9298             } else {
   9299                 out.set(0, 0, child.getWidth(), child.getHeight());
   9300             }
   9301 
   9302             if (mRecyclerView != null) {
   9303                 final Matrix childMatrix = child.getMatrix();
   9304                 if (childMatrix != null && !childMatrix.isIdentity()) {
   9305                     final RectF tempRectF = mRecyclerView.mTempRectF;
   9306                     tempRectF.set(out);
   9307                     childMatrix.mapRect(tempRectF);
   9308                     out.set(
   9309                             (int) Math.floor(tempRectF.left),
   9310                             (int) Math.floor(tempRectF.top),
   9311                             (int) Math.ceil(tempRectF.right),
   9312                             (int) Math.ceil(tempRectF.bottom)
   9313                     );
   9314                 }
   9315             }
   9316             out.offset(child.getLeft(), child.getTop());
   9317         }
   9318 
   9319         /**
   9320          * Returns the bounds of the view including its decoration and margins.
   9321          *
   9322          * @param view The view element to check
   9323          * @param outBounds A rect that will receive the bounds of the element including its
   9324          *                  decoration and margins.
   9325          */
   9326         public void getDecoratedBoundsWithMargins(@NonNull View view, @NonNull Rect outBounds) {
   9327             RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
   9328         }
   9329 
   9330         /**
   9331          * Returns the left edge of the given child view within its parent, offset by any applied
   9332          * {@link ItemDecoration ItemDecorations}.
   9333          *
   9334          * @param child Child to query
   9335          * @return Child left edge with offsets applied
   9336          * @see #getLeftDecorationWidth(View)
   9337          */
   9338         public int getDecoratedLeft(@NonNull View child) {
   9339             return child.getLeft() - getLeftDecorationWidth(child);
   9340         }
   9341 
   9342         /**
   9343          * Returns the top edge of the given child view within its parent, offset by any applied
   9344          * {@link ItemDecoration ItemDecorations}.
   9345          *
   9346          * @param child Child to query
   9347          * @return Child top edge with offsets applied
   9348          * @see #getTopDecorationHeight(View)
   9349          */
   9350         public int getDecoratedTop(@NonNull View child) {
   9351             return child.getTop() - getTopDecorationHeight(child);
   9352         }
   9353 
   9354         /**
   9355          * Returns the right edge of the given child view within its parent, offset by any applied
   9356          * {@link ItemDecoration ItemDecorations}.
   9357          *
   9358          * @param child Child to query
   9359          * @return Child right edge with offsets applied
   9360          * @see #getRightDecorationWidth(View)
   9361          */
   9362         public int getDecoratedRight(@NonNull View child) {
   9363             return child.getRight() + getRightDecorationWidth(child);
   9364         }
   9365 
   9366         /**
   9367          * Returns the bottom edge of the given child view within its parent, offset by any applied
   9368          * {@link ItemDecoration ItemDecorations}.
   9369          *
   9370          * @param child Child to query
   9371          * @return Child bottom edge with offsets applied
   9372          * @see #getBottomDecorationHeight(View)
   9373          */
   9374         public int getDecoratedBottom(@NonNull View child) {
   9375             return child.getBottom() + getBottomDecorationHeight(child);
   9376         }
   9377 
   9378         /**
   9379          * Calculates the item decor insets applied to the given child and updates the provided
   9380          * Rect instance with the inset values.
   9381          * <ul>
   9382          *     <li>The Rect's left is set to the total width of left decorations.</li>
   9383          *     <li>The Rect's top is set to the total height of top decorations.</li>
   9384          *     <li>The Rect's right is set to the total width of right decorations.</li>
   9385          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
   9386          * </ul>
   9387          * <p>
   9388          * Note that item decorations are automatically calculated when one of the LayoutManager's
   9389          * measure child methods is called. If you need to measure the child with custom specs via
   9390          * {@link View#measure(int, int)}, you can use this method to get decorations.
   9391          *
   9392          * @param child The child view whose decorations should be calculated
   9393          * @param outRect The Rect to hold result values
   9394          */
   9395         public void calculateItemDecorationsForChild(@NonNull View child, @NonNull Rect outRect) {
   9396             if (mRecyclerView == null) {
   9397                 outRect.set(0, 0, 0, 0);
   9398                 return;
   9399             }
   9400             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   9401             outRect.set(insets);
   9402         }
   9403 
   9404         /**
   9405          * Returns the total height of item decorations applied to child's top.
   9406          * <p>
   9407          * Note that this value is not updated until the View is measured or
   9408          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   9409          *
   9410          * @param child Child to query
   9411          * @return The total height of item decorations applied to the child's top.
   9412          * @see #getDecoratedTop(View)
   9413          * @see #calculateItemDecorationsForChild(View, Rect)
   9414          */
   9415         public int getTopDecorationHeight(@NonNull View child) {
   9416             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
   9417         }
   9418 
   9419         /**
   9420          * Returns the total height of item decorations applied to child's bottom.
   9421          * <p>
   9422          * Note that this value is not updated until the View is measured or
   9423          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   9424          *
   9425          * @param child Child to query
   9426          * @return The total height of item decorations applied to the child's bottom.
   9427          * @see #getDecoratedBottom(View)
   9428          * @see #calculateItemDecorationsForChild(View, Rect)
   9429          */
   9430         public int getBottomDecorationHeight(@NonNull View child) {
   9431             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
   9432         }
   9433 
   9434         /**
   9435          * Returns the total width of item decorations applied to child's left.
   9436          * <p>
   9437          * Note that this value is not updated until the View is measured or
   9438          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   9439          *
   9440          * @param child Child to query
   9441          * @return The total width of item decorations applied to the child's left.
   9442          * @see #getDecoratedLeft(View)
   9443          * @see #calculateItemDecorationsForChild(View, Rect)
   9444          */
   9445         public int getLeftDecorationWidth(@NonNull View child) {
   9446             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
   9447         }
   9448 
   9449         /**
   9450          * Returns the total width of item decorations applied to child's right.
   9451          * <p>
   9452          * Note that this value is not updated until the View is measured or
   9453          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   9454          *
   9455          * @param child Child to query
   9456          * @return The total width of item decorations applied to the child's right.
   9457          * @see #getDecoratedRight(View)
   9458          * @see #calculateItemDecorationsForChild(View, Rect)
   9459          */
   9460         public int getRightDecorationWidth(@NonNull View child) {
   9461             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
   9462         }
   9463 
   9464         /**
   9465          * Called when searching for a focusable view in the given direction has failed
   9466          * for the current content of the RecyclerView.
   9467          *
   9468          * <p>This is the LayoutManager's opportunity to populate views in the given direction
   9469          * to fulfill the request if it can. The LayoutManager should attach and return
   9470          * the view to be focused, if a focusable view in the given direction is found.
   9471          * Otherwise, if all the existing (or the newly populated views) are unfocusable, it returns
   9472          * the next unfocusable view to become visible on the screen. This unfocusable view is
   9473          * typically the first view that's either partially or fully out of RV's padded bounded
   9474          * area in the given direction. The default implementation returns null.</p>
   9475          *
   9476          * @param focused   The currently focused view
   9477          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   9478          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   9479          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   9480          *                  or 0 for not applicable
   9481          * @param recycler  The recycler to use for obtaining views for currently offscreen items
   9482          * @param state     Transient state of RecyclerView
   9483          * @return The chosen view to be focused if a focusable view is found, otherwise an
   9484          * unfocusable view to become visible onto the screen, else null.
   9485          */
   9486         @Nullable
   9487         public View onFocusSearchFailed(@NonNull View focused, int direction,
   9488                 @NonNull Recycler recycler, @NonNull State state) {
   9489             return null;
   9490         }
   9491 
   9492         /**
   9493          * This method gives a LayoutManager an opportunity to intercept the initial focus search
   9494          * before the default behavior of {@link FocusFinder} is used. If this method returns
   9495          * null FocusFinder will attempt to find a focusable child view. If it fails
   9496          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
   9497          * will be called to give the LayoutManager an opportunity to add new views for items
   9498          * that did not have attached views representing them. The LayoutManager should not add
   9499          * or remove views from this method.
   9500          *
   9501          * @param focused The currently focused view
   9502          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   9503          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   9504          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   9505          * @return A descendant view to focus or null to fall back to default behavior.
   9506          *         The default implementation returns null.
   9507          */
   9508         @Nullable
   9509         public View onInterceptFocusSearch(@NonNull View focused, int direction) {
   9510             return null;
   9511         }
   9512 
   9513         /**
   9514          * Returns the scroll amount that brings the given rect in child's coordinate system within
   9515          * the padded area of RecyclerView.
   9516          * @param parent The parent RecyclerView.
   9517          * @param child The direct child making the request.
   9518          * @param rect The rectangle in the child's coordinates the child
   9519          *             wishes to be on the screen.
   9520          * @param immediate True to forbid animated or delayed scrolling,
   9521          *                  false otherwise
   9522          * @return The array containing the scroll amount in x and y directions that brings the
   9523          * given rect into RV's padded area.
   9524          */
   9525         private int[] getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
   9526                 Rect rect, boolean immediate) {
   9527             int[] out = new int[2];
   9528             final int parentLeft = getPaddingLeft();
   9529             final int parentTop = getPaddingTop();
   9530             final int parentRight = getWidth() - getPaddingRight();
   9531             final int parentBottom = getHeight() - getPaddingBottom();
   9532             final int childLeft = child.getLeft() + rect.left - child.getScrollX();
   9533             final int childTop = child.getTop() + rect.top - child.getScrollY();
   9534             final int childRight = childLeft + rect.width();
   9535             final int childBottom = childTop + rect.height();
   9536 
   9537             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
   9538             final int offScreenTop = Math.min(0, childTop - parentTop);
   9539             final int offScreenRight = Math.max(0, childRight - parentRight);
   9540             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
   9541 
   9542             // Favor the "start" layout direction over the end when bringing one side or the other
   9543             // of a large rect into view. If we decide to bring in end because start is already
   9544             // visible, limit the scroll such that start won't go out of bounds.
   9545             final int dx;
   9546             if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
   9547                 dx = offScreenRight != 0 ? offScreenRight
   9548                         : Math.max(offScreenLeft, childRight - parentRight);
   9549             } else {
   9550                 dx = offScreenLeft != 0 ? offScreenLeft
   9551                         : Math.min(childLeft - parentLeft, offScreenRight);
   9552             }
   9553 
   9554             // Favor bringing the top into view over the bottom. If top is already visible and
   9555             // we should scroll to make bottom visible, make sure top does not go out of bounds.
   9556             final int dy = offScreenTop != 0 ? offScreenTop
   9557                     : Math.min(childTop - parentTop, offScreenBottom);
   9558             out[0] = dx;
   9559             out[1] = dy;
   9560             return out;
   9561         }
   9562         /**
   9563          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
   9564          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
   9565          * android.graphics.Rect, boolean)} for more details.
   9566          *
   9567          * <p>The base implementation will attempt to perform a standard programmatic scroll
   9568          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
   9569          *
   9570          * @param child The direct child making the request.
   9571          * @param rect  The rectangle in the child's coordinates the child
   9572          *              wishes to be on the screen.
   9573          * @param immediate True to forbid animated or delayed scrolling,
   9574          *                  false otherwise
   9575          * @return Whether the group scrolled to handle the operation
   9576          */
   9577         public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
   9578                 @NonNull View child, @NonNull Rect rect, boolean immediate) {
   9579             return requestChildRectangleOnScreen(parent, child, rect, immediate, false);
   9580         }
   9581 
   9582         /**
   9583          * Requests that the given child of the RecyclerView be positioned onto the screen. This
   9584          * method can be called for both unfocusable and focusable child views. For unfocusable
   9585          * child views, focusedChildVisible is typically true in which case, layout manager
   9586          * makes the child view visible only if the currently focused child stays in-bounds of RV.
   9587          * @param parent The parent RecyclerView.
   9588          * @param child The direct child making the request.
   9589          * @param rect The rectangle in the child's coordinates the child
   9590          *              wishes to be on the screen.
   9591          * @param immediate True to forbid animated or delayed scrolling,
   9592          *                  false otherwise
   9593          * @param focusedChildVisible Whether the currently focused view must stay visible.
   9594          * @return Whether the group scrolled to handle the operation
   9595          */
   9596         public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
   9597                 @NonNull View child, @NonNull Rect rect, boolean immediate,
   9598                 boolean focusedChildVisible) {
   9599             int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent, child, rect,
   9600                     immediate);
   9601             int dx = scrollAmount[0];
   9602             int dy = scrollAmount[1];
   9603             if (!focusedChildVisible || isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
   9604                 if (dx != 0 || dy != 0) {
   9605                     if (immediate) {
   9606                         parent.scrollBy(dx, dy);
   9607                     } else {
   9608                         parent.smoothScrollBy(dx, dy);
   9609                     }
   9610                     return true;
   9611                 }
   9612             }
   9613             return false;
   9614         }
   9615 
   9616         /**
   9617          * Returns whether the given child view is partially or fully visible within the padded
   9618          * bounded area of RecyclerView, depending on the input parameters.
   9619          * A view is partially visible if it has non-zero overlap with RV's padded bounded area.
   9620          * If acceptEndPointInclusion flag is set to true, it's also considered partially
   9621          * visible if it's located outside RV's bounds and it's hitting either RV's start or end
   9622          * bounds.
   9623          *
   9624          * @param child The child view to be examined.
   9625          * @param completelyVisible If true, the method returns true if and only if the child is
   9626          *                          completely visible. If false, the method returns true if and
   9627          *                          only if the child is only partially visible (that is it will
   9628          *                          return false if the child is either completely visible or out
   9629          *                          of RV's bounds).
   9630          * @param acceptEndPointInclusion If the view's endpoint intersection with RV's start of end
   9631          *                                bounds is enough to consider it partially visible,
   9632          *                                false otherwise.
   9633          * @return True if the given child is partially or fully visible, false otherwise.
   9634          */
   9635         public boolean isViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
   9636                 boolean acceptEndPointInclusion) {
   9637             int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS | ViewBoundsCheck.FLAG_CVS_EQ_PVS
   9638                     | ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
   9639             boolean isViewFullyVisible = mHorizontalBoundCheck.isViewWithinBoundFlags(child,
   9640                     boundsFlag)
   9641                     && mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
   9642             if (completelyVisible) {
   9643                 return isViewFullyVisible;
   9644             } else {
   9645                 return !isViewFullyVisible;
   9646             }
   9647         }
   9648 
   9649         /**
   9650          * Returns whether the currently focused child stays within RV's bounds with the given
   9651          * amount of scrolling.
   9652          * @param parent The parent RecyclerView.
   9653          * @param dx The scrolling in x-axis direction to be performed.
   9654          * @param dy The scrolling in y-axis direction to be performed.
   9655          * @return {@code false} if the focused child is not at least partially visible after
   9656          *         scrolling or no focused child exists, {@code true} otherwise.
   9657          */
   9658         private boolean isFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
   9659             final View focusedChild = parent.getFocusedChild();
   9660             if (focusedChild == null) {
   9661                 return false;
   9662             }
   9663             final int parentLeft = getPaddingLeft();
   9664             final int parentTop = getPaddingTop();
   9665             final int parentRight = getWidth() - getPaddingRight();
   9666             final int parentBottom = getHeight() - getPaddingBottom();
   9667             final Rect bounds = mRecyclerView.mTempRect;
   9668             getDecoratedBoundsWithMargins(focusedChild, bounds);
   9669 
   9670             if (bounds.left - dx >= parentRight || bounds.right - dx <= parentLeft
   9671                     || bounds.top - dy >= parentBottom || bounds.bottom - dy <= parentTop) {
   9672                 return false;
   9673             }
   9674             return true;
   9675         }
   9676 
   9677         /**
   9678          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
   9679          */
   9680         @Deprecated
   9681         public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull View child,
   9682                 @Nullable View focused) {
   9683             // eat the request if we are in the middle of a scroll or layout
   9684             return isSmoothScrolling() || parent.isComputingLayout();
   9685         }
   9686 
   9687         /**
   9688          * Called when a descendant view of the RecyclerView requests focus.
   9689          *
   9690          * <p>A LayoutManager wishing to keep focused views aligned in a specific
   9691          * portion of the view may implement that behavior in an override of this method.</p>
   9692          *
   9693          * <p>If the LayoutManager executes different behavior that should override the default
   9694          * behavior of scrolling the focused child on screen instead of running alongside it,
   9695          * this method should return true.</p>
   9696          *
   9697          * @param parent  The RecyclerView hosting this LayoutManager
   9698          * @param state   Current state of RecyclerView
   9699          * @param child   Direct child of the RecyclerView containing the newly focused view
   9700          * @param focused The newly focused view. This may be the same view as child or it may be
   9701          *                null
   9702          * @return true if the default scroll behavior should be suppressed
   9703          */
   9704         public boolean onRequestChildFocus(@NonNull RecyclerView parent, @NonNull State state,
   9705                 @NonNull View child, @Nullable View focused) {
   9706             return onRequestChildFocus(parent, child, focused);
   9707         }
   9708 
   9709         /**
   9710          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set via
   9711          * {@link RecyclerView#setAdapter(Adapter)} or
   9712          * {@link RecyclerView#swapAdapter(Adapter, boolean)}. The LayoutManager may use this
   9713          * opportunity to clear caches and configure state such that it can relayout appropriately
   9714          * with the new data and potentially new view types.
   9715          *
   9716          * <p>The default implementation removes all currently attached views.</p>
   9717          *
   9718          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
   9719          *                   adapter.
   9720          * @param newAdapter The new adapter instance. Might be null if
   9721          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
   9722          */
   9723         public void onAdapterChanged(@Nullable Adapter oldAdapter, @Nullable Adapter newAdapter) {
   9724         }
   9725 
   9726         /**
   9727          * Called to populate focusable views within the RecyclerView.
   9728          *
   9729          * <p>The LayoutManager implementation should return <code>true</code> if the default
   9730          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
   9731          * suppressed.</p>
   9732          *
   9733          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
   9734          * to fall back to the default ViewGroup behavior.</p>
   9735          *
   9736          * @param recyclerView The RecyclerView hosting this LayoutManager
   9737          * @param views List of output views. This method should add valid focusable views
   9738          *              to this list.
   9739          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   9740          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   9741          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   9742          * @param focusableMode The type of focusables to be added.
   9743          *
   9744          * @return true to suppress the default behavior, false to add default focusables after
   9745          *         this method returns.
   9746          *
   9747          * @see #FOCUSABLES_ALL
   9748          * @see #FOCUSABLES_TOUCH_MODE
   9749          */
   9750         public boolean onAddFocusables(@NonNull RecyclerView recyclerView,
   9751                 @NonNull ArrayList<View> views, int direction, int focusableMode) {
   9752             return false;
   9753         }
   9754 
   9755         /**
   9756          * Called in response to a call to {@link Adapter#notifyDataSetChanged()} or
   9757          * {@link RecyclerView#swapAdapter(Adapter, boolean)} ()} and signals that the the entire
   9758          * data set has changed.
   9759          *
   9760          * @param recyclerView
   9761          */
   9762         public void onItemsChanged(@NonNull RecyclerView recyclerView) {
   9763         }
   9764 
   9765         /**
   9766          * Called when items have been added to the adapter. The LayoutManager may choose to
   9767          * requestLayout if the inserted items would require refreshing the currently visible set
   9768          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
   9769          *
   9770          * @param recyclerView
   9771          * @param positionStart
   9772          * @param itemCount
   9773          */
   9774         public void onItemsAdded(@NonNull RecyclerView recyclerView, int positionStart,
   9775                 int itemCount) {
   9776         }
   9777 
   9778         /**
   9779          * Called when items have been removed from the adapter.
   9780          *
   9781          * @param recyclerView
   9782          * @param positionStart
   9783          * @param itemCount
   9784          */
   9785         public void onItemsRemoved(@NonNull RecyclerView recyclerView, int positionStart,
   9786                 int itemCount) {
   9787         }
   9788 
   9789         /**
   9790          * Called when items have been changed in the adapter.
   9791          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
   9792          * instead, then this callback will not be invoked.
   9793          *
   9794          * @param recyclerView
   9795          * @param positionStart
   9796          * @param itemCount
   9797          */
   9798         public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
   9799                 int itemCount) {
   9800         }
   9801 
   9802         /**
   9803          * Called when items have been changed in the adapter and with optional payload.
   9804          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
   9805          *
   9806          * @param recyclerView
   9807          * @param positionStart
   9808          * @param itemCount
   9809          * @param payload
   9810          */
   9811         public void onItemsUpdated(@NonNull RecyclerView recyclerView, int positionStart,
   9812                 int itemCount, @Nullable Object payload) {
   9813             onItemsUpdated(recyclerView, positionStart, itemCount);
   9814         }
   9815 
   9816         /**
   9817          * Called when an item is moved withing the adapter.
   9818          * <p>
   9819          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
   9820          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
   9821          * is called.
   9822          *
   9823          * @param recyclerView
   9824          * @param from
   9825          * @param to
   9826          * @param itemCount
   9827          */
   9828         public void onItemsMoved(@NonNull RecyclerView recyclerView, int from, int to,
   9829                 int itemCount) {
   9830 
   9831         }
   9832 
   9833 
   9834         /**
   9835          * <p>Override this method if you want to support scroll bars.</p>
   9836          *
   9837          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
   9838          *
   9839          * <p>Default implementation returns 0.</p>
   9840          *
   9841          * @param state Current state of RecyclerView
   9842          * @return The horizontal extent of the scrollbar's thumb
   9843          * @see RecyclerView#computeHorizontalScrollExtent()
   9844          */
   9845         public int computeHorizontalScrollExtent(@NonNull State state) {
   9846             return 0;
   9847         }
   9848 
   9849         /**
   9850          * <p>Override this method if you want to support scroll bars.</p>
   9851          *
   9852          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
   9853          *
   9854          * <p>Default implementation returns 0.</p>
   9855          *
   9856          * @param state Current State of RecyclerView where you can find total item count
   9857          * @return The horizontal offset of the scrollbar's thumb
   9858          * @see RecyclerView#computeHorizontalScrollOffset()
   9859          */
   9860         public int computeHorizontalScrollOffset(@NonNull State state) {
   9861             return 0;
   9862         }
   9863 
   9864         /**
   9865          * <p>Override this method if you want to support scroll bars.</p>
   9866          *
   9867          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
   9868          *
   9869          * <p>Default implementation returns 0.</p>
   9870          *
   9871          * @param state Current State of RecyclerView where you can find total item count
   9872          * @return The total horizontal range represented by the vertical scrollbar
   9873          * @see RecyclerView#computeHorizontalScrollRange()
   9874          */
   9875         public int computeHorizontalScrollRange(@NonNull State state) {
   9876             return 0;
   9877         }
   9878 
   9879         /**
   9880          * <p>Override this method if you want to support scroll bars.</p>
   9881          *
   9882          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
   9883          *
   9884          * <p>Default implementation returns 0.</p>
   9885          *
   9886          * @param state Current state of RecyclerView
   9887          * @return The vertical extent of the scrollbar's thumb
   9888          * @see RecyclerView#computeVerticalScrollExtent()
   9889          */
   9890         public int computeVerticalScrollExtent(@NonNull State state) {
   9891             return 0;
   9892         }
   9893 
   9894         /**
   9895          * <p>Override this method if you want to support scroll bars.</p>
   9896          *
   9897          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
   9898          *
   9899          * <p>Default implementation returns 0.</p>
   9900          *
   9901          * @param state Current State of RecyclerView where you can find total item count
   9902          * @return The vertical offset of the scrollbar's thumb
   9903          * @see RecyclerView#computeVerticalScrollOffset()
   9904          */
   9905         public int computeVerticalScrollOffset(@NonNull State state) {
   9906             return 0;
   9907         }
   9908 
   9909         /**
   9910          * <p>Override this method if you want to support scroll bars.</p>
   9911          *
   9912          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
   9913          *
   9914          * <p>Default implementation returns 0.</p>
   9915          *
   9916          * @param state Current State of RecyclerView where you can find total item count
   9917          * @return The total vertical range represented by the vertical scrollbar
   9918          * @see RecyclerView#computeVerticalScrollRange()
   9919          */
   9920         public int computeVerticalScrollRange(@NonNull State state) {
   9921             return 0;
   9922         }
   9923 
   9924         /**
   9925          * Measure the attached RecyclerView. Implementations must call
   9926          * {@link #setMeasuredDimension(int, int)} before returning.
   9927          * <p>
   9928          * It is strongly advised to use the AutoMeasure mechanism by overriding
   9929          * {@link #isAutoMeasureEnabled()} to return true as AutoMeasure handles all the standard
   9930          * measure cases including when the RecyclerView's layout_width or layout_height have been
   9931          * set to wrap_content.  If {@link #isAutoMeasureEnabled()} is overridden to return true,
   9932          * this method should not be overridden.
   9933          * <p>
   9934          * The default implementation will handle EXACTLY measurements and respect
   9935          * the minimum width and height properties of the host RecyclerView if measured
   9936          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
   9937          * will consume all available space.
   9938          *
   9939          * @param recycler Recycler
   9940          * @param state Transient state of RecyclerView
   9941          * @param widthSpec Width {@link android.view.View.MeasureSpec}
   9942          * @param heightSpec Height {@link android.view.View.MeasureSpec}
   9943          *
   9944          * @see #isAutoMeasureEnabled()
   9945          * @see #setMeasuredDimension(int, int)
   9946          */
   9947         public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
   9948                 int heightSpec) {
   9949             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
   9950         }
   9951 
   9952         /**
   9953          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
   9954          * host RecyclerView.
   9955          *
   9956          * @param widthSize Measured width
   9957          * @param heightSize Measured height
   9958          */
   9959         public void setMeasuredDimension(int widthSize, int heightSize) {
   9960             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
   9961         }
   9962 
   9963         /**
   9964          * @return The host RecyclerView's {@link View#getMinimumWidth()}
   9965          */
   9966         @Px
   9967         public int getMinimumWidth() {
   9968             return ViewCompat.getMinimumWidth(mRecyclerView);
   9969         }
   9970 
   9971         /**
   9972          * @return The host RecyclerView's {@link View#getMinimumHeight()}
   9973          */
   9974         @Px
   9975         public int getMinimumHeight() {
   9976             return ViewCompat.getMinimumHeight(mRecyclerView);
   9977         }
   9978         /**
   9979          * <p>Called when the LayoutManager should save its state. This is a good time to save your
   9980          * scroll position, configuration and anything else that may be required to restore the same
   9981          * layout state if the LayoutManager is recreated.</p>
   9982          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
   9983          * restore. This will let you share information between your LayoutManagers but it is also
   9984          * your responsibility to make sure they use the same parcelable class.</p>
   9985          *
   9986          * @return Necessary information for LayoutManager to be able to restore its state
   9987          */
   9988         @Nullable
   9989         public Parcelable onSaveInstanceState() {
   9990             return null;
   9991         }
   9992 
   9993 
   9994         public void onRestoreInstanceState(Parcelable state) {
   9995 
   9996         }
   9997 
   9998         void stopSmoothScroller() {
   9999             if (mSmoothScroller != null) {
   10000                 mSmoothScroller.stop();
   10001             }
   10002         }
   10003 
   10004         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
   10005             if (mSmoothScroller == smoothScroller) {
   10006                 mSmoothScroller = null;
   10007             }
   10008         }
   10009 
   10010         /**
   10011          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
   10012          *
   10013          * @param state The new scroll state for RecyclerView
   10014          */
   10015         public void onScrollStateChanged(int state) {
   10016         }
   10017 
   10018         /**
   10019          * Removes all views and recycles them using the given recycler.
   10020          * <p>
   10021          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
   10022          * <p>
   10023          * If a View is marked as "ignored", it is not removed nor recycled.
   10024          *
   10025          * @param recycler Recycler to use to recycle children
   10026          * @see #removeAndRecycleView(View, Recycler)
   10027          * @see #removeAndRecycleViewAt(int, Recycler)
   10028          * @see #ignoreView(View)
   10029          */
   10030         public void removeAndRecycleAllViews(@NonNull Recycler recycler) {
   10031             for (int i = getChildCount() - 1; i >= 0; i--) {
   10032                 final View view = getChildAt(i);
   10033                 if (!getChildViewHolderInt(view).shouldIgnore()) {
   10034                     removeAndRecycleViewAt(i, recycler);
   10035                 }
   10036             }
   10037         }
   10038 
   10039         // called by accessibility delegate
   10040         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
   10041             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
   10042         }
   10043 
   10044         /**
   10045          * Called by the AccessibilityDelegate when the information about the current layout should
   10046          * be populated.
   10047          * <p>
   10048          * Default implementation adds a {@link
   10049          * androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
   10050          * <p>
   10051          * You should override
   10052          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   10053          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   10054          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
   10055          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
   10056          * more accurate accessibility information.
   10057          *
   10058          * @param recycler The Recycler that can be used to convert view positions into adapter
   10059          *                 positions
   10060          * @param state    The current state of RecyclerView
   10061          * @param info     The info that should be filled by the LayoutManager
   10062          * @see View#onInitializeAccessibilityNodeInfo(
   10063          *android.view.accessibility.AccessibilityNodeInfo)
   10064          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   10065          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   10066          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
   10067          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   10068          */
   10069         public void onInitializeAccessibilityNodeInfo(@NonNull Recycler recycler,
   10070                 @NonNull State state, @NonNull AccessibilityNodeInfoCompat info) {
   10071             if (mRecyclerView.canScrollVertically(-1) || mRecyclerView.canScrollHorizontally(-1)) {
   10072                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
   10073                 info.setScrollable(true);
   10074             }
   10075             if (mRecyclerView.canScrollVertically(1) || mRecyclerView.canScrollHorizontally(1)) {
   10076                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
   10077                 info.setScrollable(true);
   10078             }
   10079             final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
   10080                     AccessibilityNodeInfoCompat.CollectionInfoCompat
   10081                             .obtain(getRowCountForAccessibility(recycler, state),
   10082                                     getColumnCountForAccessibility(recycler, state),
   10083                                     isLayoutHierarchical(recycler, state),
   10084                                     getSelectionModeForAccessibility(recycler, state));
   10085             info.setCollectionInfo(collectionInfo);
   10086         }
   10087 
   10088         // called by accessibility delegate
   10089         public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
   10090             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
   10091         }
   10092 
   10093         /**
   10094          * Called by the accessibility delegate to initialize an accessibility event.
   10095          * <p>
   10096          * Default implementation adds item count and scroll information to the event.
   10097          *
   10098          * @param recycler The Recycler that can be used to convert view positions into adapter
   10099          *                 positions
   10100          * @param state    The current state of RecyclerView
   10101          * @param event    The event instance to initialize
   10102          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
   10103          */
   10104         public void onInitializeAccessibilityEvent(@NonNull Recycler recycler, @NonNull State state,
   10105                 @NonNull AccessibilityEvent event) {
   10106             if (mRecyclerView == null || event == null) {
   10107                 return;
   10108             }
   10109             event.setScrollable(mRecyclerView.canScrollVertically(1)
   10110                     || mRecyclerView.canScrollVertically(-1)
   10111                     || mRecyclerView.canScrollHorizontally(-1)
   10112                     || mRecyclerView.canScrollHorizontally(1));
   10113 
   10114             if (mRecyclerView.mAdapter != null) {
   10115                 event.setItemCount(mRecyclerView.mAdapter.getItemCount());
   10116             }
   10117         }
   10118 
   10119         // called by accessibility delegate
   10120         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
   10121             final ViewHolder vh = getChildViewHolderInt(host);
   10122             // avoid trying to create accessibility node info for removed children
   10123             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
   10124                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
   10125                         mRecyclerView.mState, host, info);
   10126             }
   10127         }
   10128 
   10129         /**
   10130          * Called by the AccessibilityDelegate when the accessibility information for a specific
   10131          * item should be populated.
   10132          * <p>
   10133          * Default implementation adds basic positioning information about the item.
   10134          *
   10135          * @param recycler The Recycler that can be used to convert view positions into adapter
   10136          *                 positions
   10137          * @param state    The current state of RecyclerView
   10138          * @param host     The child for which accessibility node info should be populated
   10139          * @param info     The info to fill out about the item
   10140          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
   10141          * android.view.accessibility.AccessibilityNodeInfo)
   10142          */
   10143         public void onInitializeAccessibilityNodeInfoForItem(@NonNull Recycler recycler,
   10144                 @NonNull State state, @NonNull View host,
   10145                 @NonNull AccessibilityNodeInfoCompat info) {
   10146             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
   10147             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
   10148             final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo =
   10149                     AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
   10150                             columnIndexGuess, 1, false, false);
   10151             info.setCollectionItemInfo(itemInfo);
   10152         }
   10153 
   10154         /**
   10155          * A LayoutManager can call this method to force RecyclerView to run simple animations in
   10156          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
   10157          * change).
   10158          * <p>
   10159          * Note that, calling this method will not guarantee that RecyclerView will run animations
   10160          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
   10161          * not run any animations but will still clear this flag after the layout is complete.
   10162          *
   10163          */
   10164         public void requestSimpleAnimationsInNextLayout() {
   10165             mRequestedSimpleAnimations = true;
   10166         }
   10167 
   10168         /**
   10169          * Returns the selection mode for accessibility. Should be
   10170          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
   10171          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
   10172          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
   10173          * <p>
   10174          * Default implementation returns
   10175          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
   10176          *
   10177          * @param recycler The Recycler that can be used to convert view positions into adapter
   10178          *                 positions
   10179          * @param state    The current state of RecyclerView
   10180          * @return Selection mode for accessibility. Default implementation returns
   10181          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
   10182          */
   10183         public int getSelectionModeForAccessibility(@NonNull Recycler recycler,
   10184                 @NonNull State state) {
   10185             return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
   10186         }
   10187 
   10188         /**
   10189          * Returns the number of rows for accessibility.
   10190          * <p>
   10191          * Default implementation returns the number of items in the adapter if LayoutManager
   10192          * supports vertical scrolling or 1 if LayoutManager does not support vertical
   10193          * scrolling.
   10194          *
   10195          * @param recycler The Recycler that can be used to convert view positions into adapter
   10196          *                 positions
   10197          * @param state    The current state of RecyclerView
   10198          * @return The number of rows in LayoutManager for accessibility.
   10199          */
   10200         public int getRowCountForAccessibility(@NonNull Recycler recycler, @NonNull State state) {
   10201             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   10202                 return 1;
   10203             }
   10204             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
   10205         }
   10206 
   10207         /**
   10208          * Returns the number of columns for accessibility.
   10209          * <p>
   10210          * Default implementation returns the number of items in the adapter if LayoutManager
   10211          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
   10212          * scrolling.
   10213          *
   10214          * @param recycler The Recycler that can be used to convert view positions into adapter
   10215          *                 positions
   10216          * @param state    The current state of RecyclerView
   10217          * @return The number of rows in LayoutManager for accessibility.
   10218          */
   10219         public int getColumnCountForAccessibility(@NonNull Recycler recycler,
   10220                 @NonNull State state) {
   10221             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   10222                 return 1;
   10223             }
   10224             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
   10225         }
   10226 
   10227         /**
   10228          * Returns whether layout is hierarchical or not to be used for accessibility.
   10229          * <p>
   10230          * Default implementation returns false.
   10231          *
   10232          * @param recycler The Recycler that can be used to convert view positions into adapter
   10233          *                 positions
   10234          * @param state    The current state of RecyclerView
   10235          * @return True if layout is hierarchical.
   10236          */
   10237         public boolean isLayoutHierarchical(@NonNull Recycler recycler, @NonNull State state) {
   10238             return false;
   10239         }
   10240 
   10241         // called by accessibility delegate
   10242         boolean performAccessibilityAction(int action, @Nullable Bundle args) {
   10243             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
   10244                     action, args);
   10245         }
   10246 
   10247         /**
   10248          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
   10249          *
   10250          * @param recycler  The Recycler that can be used to convert view positions into adapter
   10251          *                  positions
   10252          * @param state     The current state of RecyclerView
   10253          * @param action    The action to perform
   10254          * @param args      Optional action arguments
   10255          * @see View#performAccessibilityAction(int, android.os.Bundle)
   10256          */
   10257         public boolean performAccessibilityAction(@NonNull Recycler recycler, @NonNull State state,
   10258                 int action, @Nullable Bundle args) {
   10259             if (mRecyclerView == null) {
   10260                 return false;
   10261             }
   10262             int vScroll = 0, hScroll = 0;
   10263             switch (action) {
   10264                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
   10265                     if (mRecyclerView.canScrollVertically(-1)) {
   10266                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
   10267                     }
   10268                     if (mRecyclerView.canScrollHorizontally(-1)) {
   10269                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
   10270                     }
   10271                     break;
   10272                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
   10273                     if (mRecyclerView.canScrollVertically(1)) {
   10274                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
   10275                     }
   10276                     if (mRecyclerView.canScrollHorizontally(1)) {
   10277                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
   10278                     }
   10279                     break;
   10280             }
   10281             if (vScroll == 0 && hScroll == 0) {
   10282                 return false;
   10283             }
   10284             mRecyclerView.smoothScrollBy(hScroll, vScroll);
   10285             return true;
   10286         }
   10287 
   10288         // called by accessibility delegate
   10289         boolean performAccessibilityActionForItem(@NonNull View view, int action,
   10290                 @Nullable Bundle args) {
   10291             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
   10292                     view, action, args);
   10293         }
   10294 
   10295         /**
   10296          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
   10297          * children of LayoutManager.
   10298          * <p>
   10299          * Default implementation does not do anything.
   10300          *
   10301          * @param recycler The Recycler that can be used to convert view positions into adapter
   10302          *                 positions
   10303          * @param state    The current state of RecyclerView
   10304          * @param view     The child view on which the action is performed
   10305          * @param action   The action to perform
   10306          * @param args     Optional action arguments
   10307          * @return true if action is handled
   10308          * @see View#performAccessibilityAction(int, android.os.Bundle)
   10309          */
   10310         public boolean performAccessibilityActionForItem(@NonNull Recycler recycler,
   10311                 @NonNull State state, @NonNull View view, int action, @Nullable Bundle args) {
   10312             return false;
   10313         }
   10314 
   10315         /**
   10316          * Parse the xml attributes to get the most common properties used by layout managers.
   10317          *
   10318          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation
   10319          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount
   10320          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout
   10321          * @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd
   10322          *
   10323          * @return an object containing the properties as specified in the attrs.
   10324          */
   10325         public static Properties getProperties(@NonNull Context context,
   10326                 @Nullable AttributeSet attrs,
   10327                 int defStyleAttr, int defStyleRes) {
   10328             Properties properties = new Properties();
   10329             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
   10330                     defStyleAttr, defStyleRes);
   10331             properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation,
   10332                     DEFAULT_ORIENTATION);
   10333             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
   10334             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
   10335             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
   10336             a.recycle();
   10337             return properties;
   10338         }
   10339 
   10340         void setExactMeasureSpecsFrom(RecyclerView recyclerView) {
   10341             setMeasureSpecs(
   10342                     MeasureSpec.makeMeasureSpec(recyclerView.getWidth(), MeasureSpec.EXACTLY),
   10343                     MeasureSpec.makeMeasureSpec(recyclerView.getHeight(), MeasureSpec.EXACTLY)
   10344             );
   10345         }
   10346 
   10347         /**
   10348          * Internal API to allow LayoutManagers to be measured twice.
   10349          * <p>
   10350          * This is not public because LayoutManagers should be able to handle their layouts in one
   10351          * pass but it is very convenient to make existing LayoutManagers support wrapping content
   10352          * when both orientations are undefined.
   10353          * <p>
   10354          * This API will be removed after default LayoutManagers properly implement wrap content in
   10355          * non-scroll orientation.
   10356          */
   10357         boolean shouldMeasureTwice() {
   10358             return false;
   10359         }
   10360 
   10361         boolean hasFlexibleChildInBothOrientations() {
   10362             final int childCount = getChildCount();
   10363             for (int i = 0; i < childCount; i++) {
   10364                 final View child = getChildAt(i);
   10365                 final ViewGroup.LayoutParams lp = child.getLayoutParams();
   10366                 if (lp.width < 0 && lp.height < 0) {
   10367                     return true;
   10368                 }
   10369             }
   10370             return false;
   10371         }
   10372 
   10373         /**
   10374          * Some general properties that a LayoutManager may want to use.
   10375          */
   10376         public static class Properties {
   10377             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_android_orientation */
   10378             public int orientation;
   10379             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_spanCount */
   10380             public int spanCount;
   10381             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_reverseLayout */
   10382             public boolean reverseLayout;
   10383             /** @attr ref androidx.recyclerview.R.styleable#RecyclerView_stackFromEnd */
   10384             public boolean stackFromEnd;
   10385         }
   10386     }
   10387 
   10388     /**
   10389      * An ItemDecoration allows the application to add a special drawing and layout offset
   10390      * to specific item views from the adapter's data set. This can be useful for drawing dividers
   10391      * between items, highlights, visual grouping boundaries and more.
   10392      *
   10393      * <p>All ItemDecorations are drawn in the order they were added, before the item
   10394      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
   10395      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
   10396      * RecyclerView.State)}.</p>
   10397      */
   10398     public abstract static class ItemDecoration {
   10399         /**
   10400          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   10401          * Any content drawn by this method will be drawn before the item views are drawn,
   10402          * and will thus appear underneath the views.
   10403          *
   10404          * @param c Canvas to draw into
   10405          * @param parent RecyclerView this ItemDecoration is drawing into
   10406          * @param state The current state of RecyclerView
   10407          */
   10408         public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
   10409             onDraw(c, parent);
   10410         }
   10411 
   10412         /**
   10413          * @deprecated
   10414          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
   10415          */
   10416         @Deprecated
   10417         public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent) {
   10418         }
   10419 
   10420         /**
   10421          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   10422          * Any content drawn by this method will be drawn after the item views are drawn
   10423          * and will thus appear over the views.
   10424          *
   10425          * @param c Canvas to draw into
   10426          * @param parent RecyclerView this ItemDecoration is drawing into
   10427          * @param state The current state of RecyclerView.
   10428          */
   10429         public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
   10430                 @NonNull State state) {
   10431             onDrawOver(c, parent);
   10432         }
   10433 
   10434         /**
   10435          * @deprecated
   10436          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
   10437          */
   10438         @Deprecated
   10439         public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent) {
   10440         }
   10441 
   10442 
   10443         /**
   10444          * @deprecated
   10445          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
   10446          */
   10447         @Deprecated
   10448         public void getItemOffsets(@NonNull Rect outRect, int itemPosition,
   10449                 @NonNull RecyclerView parent) {
   10450             outRect.set(0, 0, 0, 0);
   10451         }
   10452 
   10453         /**
   10454          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
   10455          * the number of pixels that the item view should be inset by, similar to padding or margin.
   10456          * The default implementation sets the bounds of outRect to 0 and returns.
   10457          *
   10458          * <p>
   10459          * If this ItemDecoration does not affect the positioning of item views, it should set
   10460          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
   10461          * before returning.
   10462          *
   10463          * <p>
   10464          * If you need to access Adapter for additional data, you can call
   10465          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
   10466          * View.
   10467          *
   10468          * @param outRect Rect to receive the output.
   10469          * @param view    The child view to decorate
   10470          * @param parent  RecyclerView this ItemDecoration is decorating
   10471          * @param state   The current state of RecyclerView.
   10472          */
   10473         public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
   10474                 @NonNull RecyclerView parent, @NonNull State state) {
   10475             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
   10476                     parent);
   10477         }
   10478     }
   10479 
   10480     /**
   10481      * An OnItemTouchListener allows the application to intercept touch events in progress at the
   10482      * view hierarchy level of the RecyclerView before those touch events are considered for
   10483      * RecyclerView's own scrolling behavior.
   10484      *
   10485      * <p>This can be useful for applications that wish to implement various forms of gestural
   10486      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
   10487      * a touch interaction already in progress even if the RecyclerView is already handling that
   10488      * gesture stream itself for the purposes of scrolling.</p>
   10489      *
   10490      * @see SimpleOnItemTouchListener
   10491      */
   10492     public interface OnItemTouchListener {
   10493         /**
   10494          * Silently observe and/or take over touch events sent to the RecyclerView
   10495          * before they are handled by either the RecyclerView itself or its child views.
   10496          *
   10497          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
   10498          * in the order in which each listener was added, before any other touch processing
   10499          * by the RecyclerView itself or child views occurs.</p>
   10500          *
   10501          * @param e MotionEvent describing the touch event. All coordinates are in
   10502          *          the RecyclerView's coordinate system.
   10503          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
   10504          *         to continue with the current behavior and continue observing future events in
   10505          *         the gesture.
   10506          */
   10507         boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
   10508 
   10509         /**
   10510          * Process a touch event as part of a gesture that was claimed by returning true from
   10511          * a previous call to {@link #onInterceptTouchEvent}.
   10512          *
   10513          * @param e MotionEvent describing the touch event. All coordinates are in
   10514          *          the RecyclerView's coordinate system.
   10515          */
   10516         void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
   10517 
   10518         /**
   10519          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
   10520          * intercept touch events with
   10521          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
   10522          *
   10523          * @param disallowIntercept True if the child does not want the parent to
   10524          *            intercept touch events.
   10525          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
   10526          */
   10527         void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
   10528     }
   10529 
   10530     /**
   10531      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies
   10532      * and default return values.
   10533      * <p>
   10534      * You may prefer to extend this class if you don't need to override all methods. Another
   10535      * benefit of using this class is future compatibility. As the interface may change, we'll
   10536      * always provide a default implementation on this class so that your code won't break when
   10537      * you update to a new version of the support library.
   10538      */
   10539     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
   10540         @Override
   10541         public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
   10542             return false;
   10543         }
   10544 
   10545         @Override
   10546         public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
   10547         }
   10548 
   10549         @Override
   10550         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   10551         }
   10552     }
   10553 
   10554 
   10555     /**
   10556      * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
   10557      * has occurred on that RecyclerView.
   10558      * <p>
   10559      * @see RecyclerView#addOnScrollListener(OnScrollListener)
   10560      * @see RecyclerView#clearOnChildAttachStateChangeListeners()
   10561      *
   10562      */
   10563     public abstract static class OnScrollListener {
   10564         /**
   10565          * Callback method to be invoked when RecyclerView's scroll state changes.
   10566          *
   10567          * @param recyclerView The RecyclerView whose scroll state has changed.
   10568          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
   10569          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
   10570          */
   10571         public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){}
   10572 
   10573         /**
   10574          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
   10575          * called after the scroll has completed.
   10576          * <p>
   10577          * This callback will also be called if visible item range changes after a layout
   10578          * calculation. In that case, dx and dy will be 0.
   10579          *
   10580          * @param recyclerView The RecyclerView which scrolled.
   10581          * @param dx The amount of horizontal scroll.
   10582          * @param dy The amount of vertical scroll.
   10583          */
   10584         public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){}
   10585     }
   10586 
   10587     /**
   10588      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
   10589      * a view is recycled.
   10590      *
   10591      * @see RecyclerView#setRecyclerListener(RecyclerListener)
   10592      */
   10593     public interface RecyclerListener {
   10594 
   10595         /**
   10596          * This method is called whenever the view in the ViewHolder is recycled.
   10597          *
   10598          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   10599          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   10600          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   10601          * its adapter position.
   10602          *
   10603          * @param holder The ViewHolder containing the view that was recycled
   10604          */
   10605         void onViewRecycled(@NonNull ViewHolder holder);
   10606     }
   10607 
   10608     /**
   10609      * A Listener interface that can be attached to a RecylcerView to get notified
   10610      * whenever a ViewHolder is attached to or detached from RecyclerView.
   10611      */
   10612     public interface OnChildAttachStateChangeListener {
   10613 
   10614         /**
   10615          * Called when a view is attached to the RecyclerView.
   10616          *
   10617          * @param view The View which is attached to the RecyclerView
   10618          */
   10619         void onChildViewAttachedToWindow(@NonNull View view);
   10620 
   10621         /**
   10622          * Called when a view is detached from RecyclerView.
   10623          *
   10624          * @param view The View which is being detached from the RecyclerView
   10625          */
   10626         void onChildViewDetachedFromWindow(@NonNull View view);
   10627     }
   10628 
   10629     /**
   10630      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
   10631      *
   10632      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
   10633      * potentially expensive {@link View#findViewById(int)} results.</p>
   10634      *
   10635      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
   10636      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
   10637      * their own custom ViewHolder implementations to store data that makes binding view contents
   10638      * easier. Implementations should assume that individual item views will hold strong references
   10639      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
   10640      * strong references to extra off-screen item views for caching purposes</p>
   10641      */
   10642     public abstract static class ViewHolder {
   10643         @NonNull
   10644         public final View itemView;
   10645         WeakReference<RecyclerView> mNestedRecyclerView;
   10646         int mPosition = NO_POSITION;
   10647         int mOldPosition = NO_POSITION;
   10648         long mItemId = NO_ID;
   10649         int mItemViewType = INVALID_TYPE;
   10650         int mPreLayoutPosition = NO_POSITION;
   10651 
   10652         // The item that this holder is shadowing during an item change event/animation
   10653         ViewHolder mShadowedHolder = null;
   10654         // The item that is shadowing this holder during an item change event/animation
   10655         ViewHolder mShadowingHolder = null;
   10656 
   10657         /**
   10658          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
   10659          * are all valid.
   10660          */
   10661         static final int FLAG_BOUND = 1 << 0;
   10662 
   10663         /**
   10664          * The data this ViewHolder's view reflects is stale and needs to be rebound
   10665          * by the adapter. mPosition and mItemId are consistent.
   10666          */
   10667         static final int FLAG_UPDATE = 1 << 1;
   10668 
   10669         /**
   10670          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
   10671          * are not to be trusted and may no longer match the item view type.
   10672          * This ViewHolder must be fully rebound to different data.
   10673          */
   10674         static final int FLAG_INVALID = 1 << 2;
   10675 
   10676         /**
   10677          * This ViewHolder points at data that represents an item previously removed from the
   10678          * data set. Its view may still be used for things like outgoing animations.
   10679          */
   10680         static final int FLAG_REMOVED = 1 << 3;
   10681 
   10682         /**
   10683          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
   10684          * and is intended to keep views around during animations.
   10685          */
   10686         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
   10687 
   10688         /**
   10689          * This ViewHolder is returned from scrap which means we are expecting an addView call
   10690          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
   10691          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
   10692          * the RecyclerView.
   10693          */
   10694         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
   10695 
   10696         /**
   10697          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
   10698          * it unless LayoutManager is replaced.
   10699          * It is still fully visible to the LayoutManager.
   10700          */
   10701         static final int FLAG_IGNORE = 1 << 7;
   10702 
   10703         /**
   10704          * When the View is detached form the parent, we set this flag so that we can take correct
   10705          * action when we need to remove it or add it back.
   10706          */
   10707         static final int FLAG_TMP_DETACHED = 1 << 8;
   10708 
   10709         /**
   10710          * Set when we can no longer determine the adapter position of this ViewHolder until it is
   10711          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
   10712          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
   10713          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
   10714          * re-calculated.
   10715          */
   10716         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
   10717 
   10718         /**
   10719          * Set when a addChangePayload(null) is called
   10720          */
   10721         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
   10722 
   10723         /**
   10724          * Used by ItemAnimator when a ViewHolder's position changes
   10725          */
   10726         static final int FLAG_MOVED = 1 << 11;
   10727 
   10728         /**
   10729          * Used by ItemAnimator when a ViewHolder appears in pre-layout
   10730          */
   10731         static final int FLAG_APPEARED_IN_PRE_LAYOUT = 1 << 12;
   10732 
   10733         static final int PENDING_ACCESSIBILITY_STATE_NOT_SET = -1;
   10734 
   10735         /**
   10736          * Used when a ViewHolder starts the layout pass as a hidden ViewHolder but is re-used from
   10737          * hidden list (as if it was scrap) without being recycled in between.
   10738          *
   10739          * When a ViewHolder is hidden, there are 2 paths it can be re-used:
   10740          *   a) Animation ends, view is recycled and used from the recycle pool.
   10741          *   b) LayoutManager asks for the View for that position while the ViewHolder is hidden.
   10742          *
   10743          * This flag is used to represent "case b" where the ViewHolder is reused without being
   10744          * recycled (thus "bounced" from the hidden list). This state requires special handling
   10745          * because the ViewHolder must be added to pre layout maps for animations as if it was
   10746          * already there.
   10747          */
   10748         static final int FLAG_BOUNCED_FROM_HIDDEN_LIST = 1 << 13;
   10749 
   10750         /**
   10751          * Flags that RecyclerView assigned {@link RecyclerViewAccessibilityDelegate
   10752          * #getItemDelegate()} in onBindView when app does not provide a delegate.
   10753          */
   10754         static final int FLAG_SET_A11Y_ITEM_DELEGATE = 1 << 14;
   10755 
   10756         private int mFlags;
   10757 
   10758         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
   10759 
   10760         List<Object> mPayloads = null;
   10761         List<Object> mUnmodifiedPayloads = null;
   10762 
   10763         private int mIsRecyclableCount = 0;
   10764 
   10765         // If non-null, view is currently considered scrap and may be reused for other data by the
   10766         // scrap container.
   10767         private Recycler mScrapContainer = null;
   10768         // Keeps whether this ViewHolder lives in Change scrap or Attached scrap
   10769         private boolean mInChangeScrap = false;
   10770 
   10771         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
   10772         // marked as unimportant for accessibility.
   10773         private int mWasImportantForAccessibilityBeforeHidden =
   10774                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   10775         // set if we defer the accessibility state change of the view holder
   10776         @VisibleForTesting
   10777         int mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
   10778 
   10779         /**
   10780          * Is set when VH is bound from the adapter and cleaned right before it is sent to
   10781          * {@link RecycledViewPool}.
   10782          */
   10783         RecyclerView mOwnerRecyclerView;
   10784 
   10785         public ViewHolder(@NonNull View itemView) {
   10786             if (itemView == null) {
   10787                 throw new IllegalArgumentException("itemView may not be null");
   10788             }
   10789             this.itemView = itemView;
   10790         }
   10791 
   10792         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
   10793             addFlags(ViewHolder.FLAG_REMOVED);
   10794             offsetPosition(offset, applyToPreLayout);
   10795             mPosition = mNewPosition;
   10796         }
   10797 
   10798         void offsetPosition(int offset, boolean applyToPreLayout) {
   10799             if (mOldPosition == NO_POSITION) {
   10800                 mOldPosition = mPosition;
   10801             }
   10802             if (mPreLayoutPosition == NO_POSITION) {
   10803                 mPreLayoutPosition = mPosition;
   10804             }
   10805             if (applyToPreLayout) {
   10806                 mPreLayoutPosition += offset;
   10807             }
   10808             mPosition += offset;
   10809             if (itemView.getLayoutParams() != null) {
   10810                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
   10811             }
   10812         }
   10813 
   10814         void clearOldPosition() {
   10815             mOldPosition = NO_POSITION;
   10816             mPreLayoutPosition = NO_POSITION;
   10817         }
   10818 
   10819         void saveOldPosition() {
   10820             if (mOldPosition == NO_POSITION) {
   10821                 mOldPosition = mPosition;
   10822             }
   10823         }
   10824 
   10825         boolean shouldIgnore() {
   10826             return (mFlags & FLAG_IGNORE) != 0;
   10827         }
   10828 
   10829         /**
   10830          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
   10831          * handling of adapter updates. You should use {@link #getLayoutPosition()} or
   10832          * {@link #getAdapterPosition()} depending on your use case.
   10833          *
   10834          * @see #getLayoutPosition()
   10835          * @see #getAdapterPosition()
   10836          */
   10837         @Deprecated
   10838         public final int getPosition() {
   10839             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   10840         }
   10841 
   10842         /**
   10843          * Returns the position of the ViewHolder in terms of the latest layout pass.
   10844          * <p>
   10845          * This position is mostly used by RecyclerView components to be consistent while
   10846          * RecyclerView lazily processes adapter updates.
   10847          * <p>
   10848          * For performance and animation reasons, RecyclerView batches all adapter updates until the
   10849          * next layout pass. This may cause mismatches between the Adapter position of the item and
   10850          * the position it had in the latest layout calculations.
   10851          * <p>
   10852          * LayoutManagers should always call this method while doing calculations based on item
   10853          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
   10854          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
   10855          * of the item.
   10856          * <p>
   10857          * If LayoutManager needs to call an external method that requires the adapter position of
   10858          * the item, it can use {@link #getAdapterPosition()} or
   10859          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
   10860          *
   10861          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
   10862          * @see #getAdapterPosition()
   10863          */
   10864         public final int getLayoutPosition() {
   10865             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   10866         }
   10867 
   10868         /**
   10869          * Returns the Adapter position of the item represented by this ViewHolder.
   10870          * <p>
   10871          * Note that this might be different than the {@link #getLayoutPosition()} if there are
   10872          * pending adapter updates but a new layout pass has not happened yet.
   10873          * <p>
   10874          * RecyclerView does not handle any adapter updates until the next layout traversal. This
   10875          * may create temporary inconsistencies between what user sees on the screen and what
   10876          * adapter contents have. This inconsistency is not important since it will be less than
   10877          * 16ms but it might be a problem if you want to use ViewHolder position to access the
   10878          * adapter. Sometimes, you may need to get the exact adapter position to do
   10879          * some actions in response to user events. In that case, you should use this method which
   10880          * will calculate the Adapter position of the ViewHolder.
   10881          * <p>
   10882          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
   10883          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
   10884          *
   10885          * @return The adapter position of the item if it still exists in the adapter.
   10886          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
   10887          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
   10888          * layout pass or the ViewHolder has already been recycled.
   10889          */
   10890         public final int getAdapterPosition() {
   10891             if (mOwnerRecyclerView == null) {
   10892                 return NO_POSITION;
   10893             }
   10894             return mOwnerRecyclerView.getAdapterPositionFor(this);
   10895         }
   10896 
   10897         /**
   10898          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
   10899          * to perform animations.
   10900          * <p>
   10901          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
   10902          * adapter index in the previous layout.
   10903          *
   10904          * @return The previous adapter index of the Item represented by this ViewHolder or
   10905          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
   10906          * complete).
   10907          */
   10908         public final int getOldPosition() {
   10909             return mOldPosition;
   10910         }
   10911 
   10912         /**
   10913          * Returns The itemId represented by this ViewHolder.
   10914          *
   10915          * @return The item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
   10916          * otherwise
   10917          */
   10918         public final long getItemId() {
   10919             return mItemId;
   10920         }
   10921 
   10922         /**
   10923          * @return The view type of this ViewHolder.
   10924          */
   10925         public final int getItemViewType() {
   10926             return mItemViewType;
   10927         }
   10928 
   10929         boolean isScrap() {
   10930             return mScrapContainer != null;
   10931         }
   10932 
   10933         void unScrap() {
   10934             mScrapContainer.unscrapView(this);
   10935         }
   10936 
   10937         boolean wasReturnedFromScrap() {
   10938             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
   10939         }
   10940 
   10941         void clearReturnedFromScrapFlag() {
   10942             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
   10943         }
   10944 
   10945         void clearTmpDetachFlag() {
   10946             mFlags = mFlags & ~FLAG_TMP_DETACHED;
   10947         }
   10948 
   10949         void stopIgnoring() {
   10950             mFlags = mFlags & ~FLAG_IGNORE;
   10951         }
   10952 
   10953         void setScrapContainer(Recycler recycler, boolean isChangeScrap) {
   10954             mScrapContainer = recycler;
   10955             mInChangeScrap = isChangeScrap;
   10956         }
   10957 
   10958         boolean isInvalid() {
   10959             return (mFlags & FLAG_INVALID) != 0;
   10960         }
   10961 
   10962         boolean needsUpdate() {
   10963             return (mFlags & FLAG_UPDATE) != 0;
   10964         }
   10965 
   10966         boolean isBound() {
   10967             return (mFlags & FLAG_BOUND) != 0;
   10968         }
   10969 
   10970         boolean isRemoved() {
   10971             return (mFlags & FLAG_REMOVED) != 0;
   10972         }
   10973 
   10974         boolean hasAnyOfTheFlags(int flags) {
   10975             return (mFlags & flags) != 0;
   10976         }
   10977 
   10978         boolean isTmpDetached() {
   10979             return (mFlags & FLAG_TMP_DETACHED) != 0;
   10980         }
   10981 
   10982         boolean isAdapterPositionUnknown() {
   10983             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
   10984         }
   10985 
   10986         void setFlags(int flags, int mask) {
   10987             mFlags = (mFlags & ~mask) | (flags & mask);
   10988         }
   10989 
   10990         void addFlags(int flags) {
   10991             mFlags |= flags;
   10992         }
   10993 
   10994         void addChangePayload(Object payload) {
   10995             if (payload == null) {
   10996                 addFlags(FLAG_ADAPTER_FULLUPDATE);
   10997             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   10998                 createPayloadsIfNeeded();
   10999                 mPayloads.add(payload);
   11000             }
   11001         }
   11002 
   11003         private void createPayloadsIfNeeded() {
   11004             if (mPayloads == null) {
   11005                 mPayloads = new ArrayList<Object>();
   11006                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
   11007             }
   11008         }
   11009 
   11010         void clearPayload() {
   11011             if (mPayloads != null) {
   11012                 mPayloads.clear();
   11013             }
   11014             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
   11015         }
   11016 
   11017         List<Object> getUnmodifiedPayloads() {
   11018             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   11019                 if (mPayloads == null || mPayloads.size() == 0) {
   11020                     // Initial state,  no update being called.
   11021                     return FULLUPDATE_PAYLOADS;
   11022                 }
   11023                 // there are none-null payloads
   11024                 return mUnmodifiedPayloads;
   11025             } else {
   11026                 // a full update has been called.
   11027                 return FULLUPDATE_PAYLOADS;
   11028             }
   11029         }
   11030 
   11031         void resetInternal() {
   11032             mFlags = 0;
   11033             mPosition = NO_POSITION;
   11034             mOldPosition = NO_POSITION;
   11035             mItemId = NO_ID;
   11036             mPreLayoutPosition = NO_POSITION;
   11037             mIsRecyclableCount = 0;
   11038             mShadowedHolder = null;
   11039             mShadowingHolder = null;
   11040             clearPayload();
   11041             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   11042             mPendingAccessibilityState = PENDING_ACCESSIBILITY_STATE_NOT_SET;
   11043             clearNestedRecyclerViewIfNotNested(this);
   11044         }
   11045 
   11046         /**
   11047          * Called when the child view enters the hidden state
   11048          */
   11049         private void onEnteredHiddenState(RecyclerView parent) {
   11050             // While the view item is in hidden state, make it invisible for the accessibility.
   11051             if (mPendingAccessibilityState != PENDING_ACCESSIBILITY_STATE_NOT_SET) {
   11052                 mWasImportantForAccessibilityBeforeHidden = mPendingAccessibilityState;
   11053             } else {
   11054                 mWasImportantForAccessibilityBeforeHidden =
   11055                         ViewCompat.getImportantForAccessibility(itemView);
   11056             }
   11057             parent.setChildImportantForAccessibilityInternal(this,
   11058                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
   11059         }
   11060 
   11061         /**
   11062          * Called when the child view leaves the hidden state
   11063          */
   11064         private void onLeftHiddenState(RecyclerView parent) {
   11065             parent.setChildImportantForAccessibilityInternal(this,
   11066                     mWasImportantForAccessibilityBeforeHidden);
   11067             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   11068         }
   11069 
   11070         @Override
   11071         public String toString() {
   11072             final StringBuilder sb = new StringBuilder("ViewHolder{"
   11073                     + Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId
   11074                     + ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
   11075             if (isScrap()) {
   11076                 sb.append(" scrap ")
   11077                         .append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
   11078             }
   11079             if (isInvalid()) sb.append(" invalid");
   11080             if (!isBound()) sb.append(" unbound");
   11081             if (needsUpdate()) sb.append(" update");
   11082             if (isRemoved()) sb.append(" removed");
   11083             if (shouldIgnore()) sb.append(" ignored");
   11084             if (isTmpDetached()) sb.append(" tmpDetached");
   11085             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
   11086             if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
   11087 
   11088             if (itemView.getParent() == null) sb.append(" no parent");
   11089             sb.append("}");
   11090             return sb.toString();
   11091         }
   11092 
   11093         /**
   11094          * Informs the recycler whether this item can be recycled. Views which are not
   11095          * recyclable will not be reused for other items until setIsRecyclable() is
   11096          * later set to true. Calls to setIsRecyclable() should always be paired (one
   11097          * call to setIsRecyclabe(false) should always be matched with a later call to
   11098          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
   11099          * reference-counted.
   11100          *
   11101          * @param recyclable Whether this item is available to be recycled. Default value
   11102          * is true.
   11103          *
   11104          * @see #isRecyclable()
   11105          */
   11106         public final void setIsRecyclable(boolean recyclable) {
   11107             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
   11108             if (mIsRecyclableCount < 0) {
   11109                 mIsRecyclableCount = 0;
   11110                 if (DEBUG) {
   11111                     throw new RuntimeException("isRecyclable decremented below 0: "
   11112                             + "unmatched pair of setIsRecyable() calls for " + this);
   11113                 }
   11114                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: "
   11115                         + "unmatched pair of setIsRecyable() calls for " + this);
   11116             } else if (!recyclable && mIsRecyclableCount == 1) {
   11117                 mFlags |= FLAG_NOT_RECYCLABLE;
   11118             } else if (recyclable && mIsRecyclableCount == 0) {
   11119                 mFlags &= ~FLAG_NOT_RECYCLABLE;
   11120             }
   11121             if (DEBUG) {
   11122                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
   11123             }
   11124         }
   11125 
   11126         /**
   11127          * @return true if this item is available to be recycled, false otherwise.
   11128          *
   11129          * @see #setIsRecyclable(boolean)
   11130          */
   11131         public final boolean isRecyclable() {
   11132             return (mFlags & FLAG_NOT_RECYCLABLE) == 0
   11133                     && !ViewCompat.hasTransientState(itemView);
   11134         }
   11135 
   11136         /**
   11137          * Returns whether we have animations referring to this view holder or not.
   11138          * This is similar to isRecyclable flag but does not check transient state.
   11139          */
   11140         private boolean shouldBeKeptAsChild() {
   11141             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
   11142         }
   11143 
   11144         /**
   11145          * @return True if ViewHolder is not referenced by RecyclerView animations but has
   11146          * transient state which will prevent it from being recycled.
   11147          */
   11148         private boolean doesTransientStatePreventRecycling() {
   11149             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
   11150         }
   11151 
   11152         boolean isUpdated() {
   11153             return (mFlags & FLAG_UPDATE) != 0;
   11154         }
   11155     }
   11156 
   11157     /**
   11158      * This method is here so that we can control the important for a11y changes and test it.
   11159      */
   11160     @VisibleForTesting
   11161     boolean setChildImportantForAccessibilityInternal(ViewHolder viewHolder,
   11162             int importantForAccessibility) {
   11163         if (isComputingLayout()) {
   11164             viewHolder.mPendingAccessibilityState = importantForAccessibility;
   11165             mPendingAccessibilityImportanceChange.add(viewHolder);
   11166             return false;
   11167         }
   11168         ViewCompat.setImportantForAccessibility(viewHolder.itemView, importantForAccessibility);
   11169         return true;
   11170     }
   11171 
   11172     void dispatchPendingImportantForAccessibilityChanges() {
   11173         for (int i = mPendingAccessibilityImportanceChange.size() - 1; i >= 0; i--) {
   11174             ViewHolder viewHolder = mPendingAccessibilityImportanceChange.get(i);
   11175             if (viewHolder.itemView.getParent() != this || viewHolder.shouldIgnore()) {
   11176                 continue;
   11177             }
   11178             int state = viewHolder.mPendingAccessibilityState;
   11179             if (state != ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET) {
   11180                 //noinspection WrongConstant
   11181                 ViewCompat.setImportantForAccessibility(viewHolder.itemView, state);
   11182                 viewHolder.mPendingAccessibilityState =
   11183                         ViewHolder.PENDING_ACCESSIBILITY_STATE_NOT_SET;
   11184             }
   11185         }
   11186         mPendingAccessibilityImportanceChange.clear();
   11187     }
   11188 
   11189     int getAdapterPositionFor(ViewHolder viewHolder) {
   11190         if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
   11191                 | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
   11192                 || !viewHolder.isBound()) {
   11193             return RecyclerView.NO_POSITION;
   11194         }
   11195         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
   11196     }
   11197 
   11198     @VisibleForTesting
   11199     void initFastScroller(StateListDrawable verticalThumbDrawable,
   11200             Drawable verticalTrackDrawable, StateListDrawable horizontalThumbDrawable,
   11201             Drawable horizontalTrackDrawable) {
   11202         if (verticalThumbDrawable == null || verticalTrackDrawable == null
   11203                 || horizontalThumbDrawable == null || horizontalTrackDrawable == null) {
   11204             throw new IllegalArgumentException(
   11205                 "Trying to set fast scroller without both required drawables." + exceptionLabel());
   11206         }
   11207 
   11208         Resources resources = getContext().getResources();
   11209         new FastScroller(this, verticalThumbDrawable, verticalTrackDrawable,
   11210                 horizontalThumbDrawable, horizontalTrackDrawable,
   11211                 resources.getDimensionPixelSize(R.dimen.fastscroll_default_thickness),
   11212                 resources.getDimensionPixelSize(R.dimen.fastscroll_minimum_range),
   11213                 resources.getDimensionPixelOffset(R.dimen.fastscroll_margin));
   11214     }
   11215 
   11216     // NestedScrollingChild
   11217 
   11218     @Override
   11219     public void setNestedScrollingEnabled(boolean enabled) {
   11220         getScrollingChildHelper().setNestedScrollingEnabled(enabled);
   11221     }
   11222 
   11223     @Override
   11224     public boolean isNestedScrollingEnabled() {
   11225         return getScrollingChildHelper().isNestedScrollingEnabled();
   11226     }
   11227 
   11228     @Override
   11229     public boolean startNestedScroll(int axes) {
   11230         return getScrollingChildHelper().startNestedScroll(axes);
   11231     }
   11232 
   11233     @Override
   11234     public boolean startNestedScroll(int axes, int type) {
   11235         return getScrollingChildHelper().startNestedScroll(axes, type);
   11236     }
   11237 
   11238     @Override
   11239     public void stopNestedScroll() {
   11240         getScrollingChildHelper().stopNestedScroll();
   11241     }
   11242 
   11243     @Override
   11244     public void stopNestedScroll(int type) {
   11245         getScrollingChildHelper().stopNestedScroll(type);
   11246     }
   11247 
   11248     @Override
   11249     public boolean hasNestedScrollingParent() {
   11250         return getScrollingChildHelper().hasNestedScrollingParent();
   11251     }
   11252 
   11253     @Override
   11254     public boolean hasNestedScrollingParent(int type) {
   11255         return getScrollingChildHelper().hasNestedScrollingParent(type);
   11256     }
   11257 
   11258     @Override
   11259     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
   11260             int dyUnconsumed, int[] offsetInWindow) {
   11261         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
   11262                 dxUnconsumed, dyUnconsumed, offsetInWindow);
   11263     }
   11264 
   11265     @Override
   11266     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
   11267             int dyUnconsumed, int[] offsetInWindow, int type) {
   11268         return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
   11269                 dxUnconsumed, dyUnconsumed, offsetInWindow, type);
   11270     }
   11271 
   11272     @Override
   11273     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
   11274         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
   11275     }
   11276 
   11277     @Override
   11278     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
   11279             int type) {
   11280         return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
   11281                 type);
   11282     }
   11283 
   11284     @Override
   11285     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
   11286         return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
   11287     }
   11288 
   11289     @Override
   11290     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
   11291         return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
   11292     }
   11293 
   11294     /**
   11295      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
   11296      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
   11297      * to create their own subclass of this <code>LayoutParams</code> class
   11298      * to store any additional required per-child view metadata about the layout.
   11299      */
   11300     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
   11301         ViewHolder mViewHolder;
   11302         final Rect mDecorInsets = new Rect();
   11303         boolean mInsetsDirty = true;
   11304         // Flag is set to true if the view is bound while it is detached from RV.
   11305         // In this case, we need to manually call invalidate after view is added to guarantee that
   11306         // invalidation is populated through the View hierarchy
   11307         boolean mPendingInvalidate = false;
   11308 
   11309         public LayoutParams(Context c, AttributeSet attrs) {
   11310             super(c, attrs);
   11311         }
   11312 
   11313         public LayoutParams(int width, int height) {
   11314             super(width, height);
   11315         }
   11316 
   11317         public LayoutParams(MarginLayoutParams source) {
   11318             super(source);
   11319         }
   11320 
   11321         public LayoutParams(ViewGroup.LayoutParams source) {
   11322             super(source);
   11323         }
   11324 
   11325         public LayoutParams(LayoutParams source) {
   11326             super((ViewGroup.LayoutParams) source);
   11327         }
   11328 
   11329         /**
   11330          * Returns true if the view this LayoutParams is attached to needs to have its content
   11331          * updated from the corresponding adapter.
   11332          *
   11333          * @return true if the view should have its content updated
   11334          */
   11335         public boolean viewNeedsUpdate() {
   11336             return mViewHolder.needsUpdate();
   11337         }
   11338 
   11339         /**
   11340          * Returns true if the view this LayoutParams is attached to is now representing
   11341          * potentially invalid data. A LayoutManager should scrap/recycle it.
   11342          *
   11343          * @return true if the view is invalid
   11344          */
   11345         public boolean isViewInvalid() {
   11346             return mViewHolder.isInvalid();
   11347         }
   11348 
   11349         /**
   11350          * Returns true if the adapter data item corresponding to the view this LayoutParams
   11351          * is attached to has been removed from the data set. A LayoutManager may choose to
   11352          * treat it differently in order to animate its outgoing or disappearing state.
   11353          *
   11354          * @return true if the item the view corresponds to was removed from the data set
   11355          */
   11356         public boolean isItemRemoved() {
   11357             return mViewHolder.isRemoved();
   11358         }
   11359 
   11360         /**
   11361          * Returns true if the adapter data item corresponding to the view this LayoutParams
   11362          * is attached to has been changed in the data set. A LayoutManager may choose to
   11363          * treat it differently in order to animate its changing state.
   11364          *
   11365          * @return true if the item the view corresponds to was changed in the data set
   11366          */
   11367         public boolean isItemChanged() {
   11368             return mViewHolder.isUpdated();
   11369         }
   11370 
   11371         /**
   11372          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
   11373          */
   11374         @Deprecated
   11375         public int getViewPosition() {
   11376             return mViewHolder.getPosition();
   11377         }
   11378 
   11379         /**
   11380          * Returns the adapter position that the view this LayoutParams is attached to corresponds
   11381          * to as of latest layout calculation.
   11382          *
   11383          * @return the adapter position this view as of latest layout pass
   11384          */
   11385         public int getViewLayoutPosition() {
   11386             return mViewHolder.getLayoutPosition();
   11387         }
   11388 
   11389         /**
   11390          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
   11391          * corresponds to.
   11392          *
   11393          * @return the up-to-date adapter position this view. It may return
   11394          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
   11395          * its up-to-date position cannot be calculated.
   11396          */
   11397         public int getViewAdapterPosition() {
   11398             return mViewHolder.getAdapterPosition();
   11399         }
   11400     }
   11401 
   11402     /**
   11403      * Observer base class for watching changes to an {@link Adapter}.
   11404      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
   11405      */
   11406     public abstract static class AdapterDataObserver {
   11407         public void onChanged() {
   11408             // Do nothing
   11409         }
   11410 
   11411         public void onItemRangeChanged(int positionStart, int itemCount) {
   11412             // do nothing
   11413         }
   11414 
   11415         public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
   11416             // fallback to onItemRangeChanged(positionStart, itemCount) if app
   11417             // does not override this method.
   11418             onItemRangeChanged(positionStart, itemCount);
   11419         }
   11420 
   11421         public void onItemRangeInserted(int positionStart, int itemCount) {
   11422             // do nothing
   11423         }
   11424 
   11425         public void onItemRangeRemoved(int positionStart, int itemCount) {
   11426             // do nothing
   11427         }
   11428 
   11429         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   11430             // do nothing
   11431         }
   11432     }
   11433 
   11434     /**
   11435      * Base class for smooth scrolling. Handles basic tracking of the target view position and
   11436      * provides methods to trigger a programmatic scroll.
   11437      *
   11438      * <p>An instance of SmoothScroller is only intended to be used once.  You should create a new
   11439      * instance for each call to {@link LayoutManager#startSmoothScroll(SmoothScroller)}.
   11440      *
   11441      * @see LinearSmoothScroller
   11442      */
   11443     public abstract static class SmoothScroller {
   11444 
   11445         private int mTargetPosition = RecyclerView.NO_POSITION;
   11446 
   11447         private RecyclerView mRecyclerView;
   11448 
   11449         private LayoutManager mLayoutManager;
   11450 
   11451         private boolean mPendingInitialRun;
   11452 
   11453         private boolean mRunning;
   11454 
   11455         private View mTargetView;
   11456 
   11457         private final Action mRecyclingAction;
   11458 
   11459         private boolean mStarted;
   11460 
   11461         public SmoothScroller() {
   11462             mRecyclingAction = new Action(0, 0);
   11463         }
   11464 
   11465         /**
   11466          * Starts a smooth scroll for the given target position.
   11467          * <p>In each animation step, {@link RecyclerView} will check
   11468          * for the target view and call either
   11469          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   11470          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
   11471          * SmoothScroller is stopped.</p>
   11472          *
   11473          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
   11474          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
   11475          * stop calling SmoothScroller in each animation step.</p>
   11476          */
   11477         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
   11478             if (mStarted) {
   11479                 Log.w(TAG, "An instance of " + this.getClass().getSimpleName() + " was started "
   11480                         + "more than once. Each instance of" + this.getClass().getSimpleName() + " "
   11481                         + "is intended to only be used once. You should create a new instance for "
   11482                         + "each use.");
   11483             }
   11484 
   11485             mRecyclerView = recyclerView;
   11486             mLayoutManager = layoutManager;
   11487             if (mTargetPosition == RecyclerView.NO_POSITION) {
   11488                 throw new IllegalArgumentException("Invalid target position");
   11489             }
   11490             mRecyclerView.mState.mTargetPosition = mTargetPosition;
   11491             mRunning = true;
   11492             mPendingInitialRun = true;
   11493             mTargetView = findViewByPosition(getTargetPosition());
   11494             onStart();
   11495             mRecyclerView.mViewFlinger.postOnAnimation();
   11496 
   11497             mStarted = true;
   11498         }
   11499 
   11500         public void setTargetPosition(int targetPosition) {
   11501             mTargetPosition = targetPosition;
   11502         }
   11503 
   11504         /**
   11505          * Compute the scroll vector for a given target position.
   11506          * <p>
   11507          * This method can return null if the layout manager cannot calculate a scroll vector
   11508          * for the given position (e.g. it has no current scroll position).
   11509          *
   11510          * @param targetPosition the position to which the scroller is scrolling
   11511          *
   11512          * @return the scroll vector for a given target position
   11513          */
   11514         @Nullable
   11515         public PointF computeScrollVectorForPosition(int targetPosition) {
   11516             LayoutManager layoutManager = getLayoutManager();
   11517             if (layoutManager instanceof ScrollVectorProvider) {
   11518                 return ((ScrollVectorProvider) layoutManager)
   11519                         .computeScrollVectorForPosition(targetPosition);
   11520             }
   11521             Log.w(TAG, "You should override computeScrollVectorForPosition when the LayoutManager"
   11522                     + " does not implement " + ScrollVectorProvider.class.getCanonicalName());
   11523             return null;
   11524         }
   11525 
   11526         /**
   11527          * @return The LayoutManager to which this SmoothScroller is attached. Will return
   11528          * <code>null</code> after the SmoothScroller is stopped.
   11529          */
   11530         @Nullable
   11531         public LayoutManager getLayoutManager() {
   11532             return mLayoutManager;
   11533         }
   11534 
   11535         /**
   11536          * Stops running the SmoothScroller in each animation callback. Note that this does not
   11537          * cancel any existing {@link Action} updated by
   11538          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   11539          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
   11540          */
   11541         protected final void stop() {
   11542             if (!mRunning) {
   11543                 return;
   11544             }
   11545             mRunning = false;
   11546             onStop();
   11547             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
   11548             mTargetView = null;
   11549             mTargetPosition = RecyclerView.NO_POSITION;
   11550             mPendingInitialRun = false;
   11551             // trigger a cleanup
   11552             mLayoutManager.onSmoothScrollerStopped(this);
   11553             // clear references to avoid any potential leak by a custom smooth scroller
   11554             mLayoutManager = null;
   11555             mRecyclerView = null;
   11556         }
   11557 
   11558         /**
   11559          * Returns true if SmoothScroller has been started but has not received the first
   11560          * animation
   11561          * callback yet.
   11562          *
   11563          * @return True if this SmoothScroller is waiting to start
   11564          */
   11565         public boolean isPendingInitialRun() {
   11566             return mPendingInitialRun;
   11567         }
   11568 
   11569 
   11570         /**
   11571          * @return True if SmoothScroller is currently active
   11572          */
   11573         public boolean isRunning() {
   11574             return mRunning;
   11575         }
   11576 
   11577         /**
   11578          * Returns the adapter position of the target item
   11579          *
   11580          * @return Adapter position of the target item or
   11581          * {@link RecyclerView#NO_POSITION} if no target view is set.
   11582          */
   11583         public int getTargetPosition() {
   11584             return mTargetPosition;
   11585         }
   11586 
   11587         private void onAnimation(int dx, int dy) {
   11588             // TODO(b/72745539): If mRunning is false, we call stop, which is a no op if mRunning
   11589             // is false. Also, if recyclerView is null, we call stop, and stop assumes recyclerView
   11590             // is not null (as does the code following this block).  This should be cleaned up.
   11591             final RecyclerView recyclerView = mRecyclerView;
   11592             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
   11593                 stop();
   11594             }
   11595 
   11596             // The following if block exists to have the LayoutManager scroll 1 pixel in the correct
   11597             // direction in order to cause the LayoutManager to draw two pages worth of views so
   11598             // that the target view may be found before scrolling any further.  This is done to
   11599             // prevent an initial scroll distance from scrolling past the view, which causes a
   11600             // jittery looking animation. (This block also necessarily sets mPendingInitialRun to
   11601             // false if it was true).
   11602             if (mPendingInitialRun && mTargetView == null && mLayoutManager != null) {
   11603                 PointF pointF = computeScrollVectorForPosition(mTargetPosition);
   11604                 if (pointF != null && (pointF.x != 0 || pointF.y != 0)) {
   11605                     recyclerView.scrollStep(
   11606                             (int) Math.signum(pointF.x),
   11607                             (int) Math.signum(pointF.y),
   11608                             null);
   11609                 }
   11610             }
   11611 
   11612             mPendingInitialRun = false;
   11613 
   11614             if (mTargetView != null) {
   11615                 // verify target position
   11616                 if (getChildPosition(mTargetView) == mTargetPosition) {
   11617                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
   11618                     mRecyclingAction.runIfNecessary(recyclerView);
   11619                     stop();
   11620                 } else {
   11621                     Log.e(TAG, "Passed over target position while smooth scrolling.");
   11622                     mTargetView = null;
   11623                 }
   11624             }
   11625             if (mRunning) {
   11626                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
   11627                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
   11628                 mRecyclingAction.runIfNecessary(recyclerView);
   11629                 if (hadJumpTarget) {
   11630                     // It is not stopped so needs to be restarted
   11631                     if (mRunning) {
   11632                         mPendingInitialRun = true;
   11633                         recyclerView.mViewFlinger.postOnAnimation();
   11634                     } else {
   11635                         // TODO(b/72745539): stop() is a no-op if mRunning is false, so this can be
   11636                         // removed.
   11637                         stop(); // done
   11638                     }
   11639                 }
   11640             }
   11641         }
   11642 
   11643         /**
   11644          * @see RecyclerView#getChildLayoutPosition(android.view.View)
   11645          */
   11646         public int getChildPosition(View view) {
   11647             return mRecyclerView.getChildLayoutPosition(view);
   11648         }
   11649 
   11650         /**
   11651          * @see RecyclerView.LayoutManager#getChildCount()
   11652          */
   11653         public int getChildCount() {
   11654             return mRecyclerView.mLayout.getChildCount();
   11655         }
   11656 
   11657         /**
   11658          * @see RecyclerView.LayoutManager#findViewByPosition(int)
   11659          */
   11660         public View findViewByPosition(int position) {
   11661             return mRecyclerView.mLayout.findViewByPosition(position);
   11662         }
   11663 
   11664         /**
   11665          * @see RecyclerView#scrollToPosition(int)
   11666          * @deprecated Use {@link Action#jumpTo(int)}.
   11667          */
   11668         @Deprecated
   11669         public void instantScrollToPosition(int position) {
   11670             mRecyclerView.scrollToPosition(position);
   11671         }
   11672 
   11673         protected void onChildAttachedToWindow(View child) {
   11674             if (getChildPosition(child) == getTargetPosition()) {
   11675                 mTargetView = child;
   11676                 if (DEBUG) {
   11677                     Log.d(TAG, "smooth scroll target view has been attached");
   11678                 }
   11679             }
   11680         }
   11681 
   11682         /**
   11683          * Normalizes the vector.
   11684          * @param scrollVector The vector that points to the target scroll position
   11685          */
   11686         protected void normalize(@NonNull PointF scrollVector) {
   11687             final float magnitude = (float) Math.sqrt(scrollVector.x * scrollVector.x
   11688                     + scrollVector.y * scrollVector.y);
   11689             scrollVector.x /= magnitude;
   11690             scrollVector.y /= magnitude;
   11691         }
   11692 
   11693         /**
   11694          * Called when smooth scroll is started. This might be a good time to do setup.
   11695          */
   11696         protected abstract void onStart();
   11697 
   11698         /**
   11699          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
   11700          * @see #stop()
   11701          */
   11702         protected abstract void onStop();
   11703 
   11704         /**
   11705          * <p>RecyclerView will call this method each time it scrolls until it can find the target
   11706          * position in the layout.</p>
   11707          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
   11708          * provided {@link Action} to define the next scroll.</p>
   11709          *
   11710          * @param dx        Last scroll amount horizontally
   11711          * @param dy        Last scroll amount vertically
   11712          * @param state     Transient state of RecyclerView
   11713          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
   11714          *                  update this object.
   11715          */
   11716         protected abstract void onSeekTargetStep(@Px int dx, @Px int dy, @NonNull State state,
   11717                 @NonNull Action action);
   11718 
   11719         /**
   11720          * Called when the target position is laid out. This is the last callback SmoothScroller
   11721          * will receive and it should update the provided {@link Action} to define the scroll
   11722          * details towards the target view.
   11723          * @param targetView    The view element which render the target position.
   11724          * @param state         Transient state of RecyclerView
   11725          * @param action        Action instance that you should update to define final scroll action
   11726          *                      towards the targetView
   11727          */
   11728         protected abstract void onTargetFound(@NonNull View targetView, @NonNull State state,
   11729                 @NonNull Action action);
   11730 
   11731         /**
   11732          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
   11733          */
   11734         public static class Action {
   11735 
   11736             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
   11737 
   11738             private int mDx;
   11739 
   11740             private int mDy;
   11741 
   11742             private int mDuration;
   11743 
   11744             private int mJumpToPosition = NO_POSITION;
   11745 
   11746             private Interpolator mInterpolator;
   11747 
   11748             private boolean mChanged = false;
   11749 
   11750             // we track this variable to inform custom implementer if they are updating the action
   11751             // in every animation callback
   11752             private int mConsecutiveUpdates = 0;
   11753 
   11754             /**
   11755              * @param dx Pixels to scroll horizontally
   11756              * @param dy Pixels to scroll vertically
   11757              */
   11758             public Action(@Px int dx, @Px int dy) {
   11759                 this(dx, dy, UNDEFINED_DURATION, null);
   11760             }
   11761 
   11762             /**
   11763              * @param dx       Pixels to scroll horizontally
   11764              * @param dy       Pixels to scroll vertically
   11765              * @param duration Duration of the animation in milliseconds
   11766              */
   11767             public Action(@Px int dx, @Px int dy, int duration) {
   11768                 this(dx, dy, duration, null);
   11769             }
   11770 
   11771             /**
   11772              * @param dx           Pixels to scroll horizontally
   11773              * @param dy           Pixels to scroll vertically
   11774              * @param duration     Duration of the animation in milliseconds
   11775              * @param interpolator Interpolator to be used when calculating scroll position in each
   11776              *                     animation step
   11777              */
   11778             public Action(@Px int dx, @Px int dy, int duration,
   11779                     @Nullable Interpolator interpolator) {
   11780                 mDx = dx;
   11781                 mDy = dy;
   11782                 mDuration = duration;
   11783                 mInterpolator = interpolator;
   11784             }
   11785 
   11786             /**
   11787              * Instead of specifying pixels to scroll, use the target position to jump using
   11788              * {@link RecyclerView#scrollToPosition(int)}.
   11789              * <p>
   11790              * You may prefer using this method if scroll target is really far away and you prefer
   11791              * to jump to a location and smooth scroll afterwards.
   11792              * <p>
   11793              * Note that calling this method takes priority over other update methods such as
   11794              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
   11795              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
   11796              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
   11797              * frame.
   11798              *
   11799              * @param targetPosition The target item position to scroll to using instant scrolling.
   11800              */
   11801             public void jumpTo(int targetPosition) {
   11802                 mJumpToPosition = targetPosition;
   11803             }
   11804 
   11805             boolean hasJumpTarget() {
   11806                 return mJumpToPosition >= 0;
   11807             }
   11808 
   11809             void runIfNecessary(RecyclerView recyclerView) {
   11810                 if (mJumpToPosition >= 0) {
   11811                     final int position = mJumpToPosition;
   11812                     mJumpToPosition = NO_POSITION;
   11813                     recyclerView.jumpToPositionForSmoothScroller(position);
   11814                     mChanged = false;
   11815                     return;
   11816                 }
   11817                 if (mChanged) {
   11818                     validate();
   11819                     if (mInterpolator == null) {
   11820                         if (mDuration == UNDEFINED_DURATION) {
   11821                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
   11822                         } else {
   11823                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
   11824                         }
   11825                     } else {
   11826                         recyclerView.mViewFlinger.smoothScrollBy(
   11827                                 mDx, mDy, mDuration, mInterpolator);
   11828                     }
   11829                     mConsecutiveUpdates++;
   11830                     if (mConsecutiveUpdates > 10) {
   11831                         // A new action is being set in every animation step. This looks like a bad
   11832                         // implementation. Inform developer.
   11833                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
   11834                                 + " you are not changing it unless necessary");
   11835                     }
   11836                     mChanged = false;
   11837                 } else {
   11838                     mConsecutiveUpdates = 0;
   11839                 }
   11840             }
   11841 
   11842             private void validate() {
   11843                 if (mInterpolator != null && mDuration < 1) {
   11844                     throw new IllegalStateException("If you provide an interpolator, you must"
   11845                             + " set a positive duration");
   11846                 } else if (mDuration < 1) {
   11847                     throw new IllegalStateException("Scroll duration must be a positive number");
   11848                 }
   11849             }
   11850 
   11851             @Px
   11852             public int getDx() {
   11853                 return mDx;
   11854             }
   11855 
   11856             public void setDx(@Px int dx) {
   11857                 mChanged = true;
   11858                 mDx = dx;
   11859             }
   11860 
   11861             @Px
   11862             public int getDy() {
   11863                 return mDy;
   11864             }
   11865 
   11866             public void setDy(@Px int dy) {
   11867                 mChanged = true;
   11868                 mDy = dy;
   11869             }
   11870 
   11871             public int getDuration() {
   11872                 return mDuration;
   11873             }
   11874 
   11875             public void setDuration(int duration) {
   11876                 mChanged = true;
   11877                 mDuration = duration;
   11878             }
   11879 
   11880             @Nullable
   11881             public Interpolator getInterpolator() {
   11882                 return mInterpolator;
   11883             }
   11884 
   11885             /**
   11886              * Sets the interpolator to calculate scroll steps
   11887              * @param interpolator The interpolator to use. If you specify an interpolator, you must
   11888              *                     also set the duration.
   11889              * @see #setDuration(int)
   11890              */
   11891             public void setInterpolator(@Nullable Interpolator interpolator) {
   11892                 mChanged = true;
   11893                 mInterpolator = interpolator;
   11894             }
   11895 
   11896             /**
   11897              * Updates the action with given parameters.
   11898              * @param dx Pixels to scroll horizontally
   11899              * @param dy Pixels to scroll vertically
   11900              * @param duration Duration of the animation in milliseconds
   11901              * @param interpolator Interpolator to be used when calculating scroll position in each
   11902              *                     animation step
   11903              */
   11904             public void update(@Px int dx, @Px int dy, int duration,
   11905                     @Nullable Interpolator interpolator) {
   11906                 mDx = dx;
   11907                 mDy = dy;
   11908                 mDuration = duration;
   11909                 mInterpolator = interpolator;
   11910                 mChanged = true;
   11911             }
   11912         }
   11913 
   11914         /**
   11915          * An interface which is optionally implemented by custom {@link RecyclerView.LayoutManager}
   11916          * to provide a hint to a {@link SmoothScroller} about the location of the target position.
   11917          */
   11918         public interface ScrollVectorProvider {
   11919             /**
   11920              * Should calculate the vector that points to the direction where the target position
   11921              * can be found.
   11922              * <p>
   11923              * This method is used by the {@link LinearSmoothScroller} to initiate a scroll towards
   11924              * the target position.
   11925              * <p>
   11926              * The magnitude of the vector is not important. It is always normalized before being
   11927              * used by the {@link LinearSmoothScroller}.
   11928              * <p>
   11929              * LayoutManager should not check whether the position exists in the adapter or not.
   11930              *
   11931              * @param targetPosition the target position to which the returned vector should point
   11932              *
   11933              * @return the scroll vector for a given position.
   11934              */
   11935             @Nullable
   11936             PointF computeScrollVectorForPosition(int targetPosition);
   11937         }
   11938     }
   11939 
   11940     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
   11941         public boolean hasObservers() {
   11942             return !mObservers.isEmpty();
   11943         }
   11944 
   11945         public void notifyChanged() {
   11946             // since onChanged() is implemented by the app, it could do anything, including
   11947             // removing itself from {@link mObservers} - and that could cause problems if
   11948             // an iterator is used on the ArrayList {@link mObservers}.
   11949             // to avoid such problems, just march thru the list in the reverse order.
   11950             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11951                 mObservers.get(i).onChanged();
   11952             }
   11953         }
   11954 
   11955         public void notifyItemRangeChanged(int positionStart, int itemCount) {
   11956             notifyItemRangeChanged(positionStart, itemCount, null);
   11957         }
   11958 
   11959         public void notifyItemRangeChanged(int positionStart, int itemCount,
   11960                 @Nullable Object payload) {
   11961             // since onItemRangeChanged() is implemented by the app, it could do anything, including
   11962             // removing itself from {@link mObservers} - and that could cause problems if
   11963             // an iterator is used on the ArrayList {@link mObservers}.
   11964             // to avoid such problems, just march thru the list in the reverse order.
   11965             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11966                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
   11967             }
   11968         }
   11969 
   11970         public void notifyItemRangeInserted(int positionStart, int itemCount) {
   11971             // since onItemRangeInserted() is implemented by the app, it could do anything,
   11972             // including removing itself from {@link mObservers} - and that could cause problems if
   11973             // an iterator is used on the ArrayList {@link mObservers}.
   11974             // to avoid such problems, just march thru the list in the reverse order.
   11975             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11976                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
   11977             }
   11978         }
   11979 
   11980         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
   11981             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
   11982             // removing itself from {@link mObservers} - and that could cause problems if
   11983             // an iterator is used on the ArrayList {@link mObservers}.
   11984             // to avoid such problems, just march thru the list in the reverse order.
   11985             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11986                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
   11987             }
   11988         }
   11989 
   11990         public void notifyItemMoved(int fromPosition, int toPosition) {
   11991             for (int i = mObservers.size() - 1; i >= 0; i--) {
   11992                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
   11993             }
   11994         }
   11995     }
   11996 
   11997     /**
   11998      * This is public so that the CREATOR can be accessed on cold launch.
   11999      * @hide
   12000      */
   12001     @RestrictTo(LIBRARY_GROUP)
   12002     public static class SavedState extends AbsSavedState {
   12003 
   12004         Parcelable mLayoutState;
   12005 
   12006         /**
   12007          * called by CREATOR
   12008          */
   12009         SavedState(Parcel in, ClassLoader loader) {
   12010             super(in, loader);
   12011             mLayoutState = in.readParcelable(
   12012                     loader != null ? loader : LayoutManager.class.getClassLoader());
   12013         }
   12014 
   12015         /**
   12016          * Called by onSaveInstanceState
   12017          */
   12018         SavedState(Parcelable superState) {
   12019             super(superState);
   12020         }
   12021 
   12022         @Override
   12023         public void writeToParcel(Parcel dest, int flags) {
   12024             super.writeToParcel(dest, flags);
   12025             dest.writeParcelable(mLayoutState, 0);
   12026         }
   12027 
   12028         void copyFrom(SavedState other) {
   12029             mLayoutState = other.mLayoutState;
   12030         }
   12031 
   12032         public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
   12033             @Override
   12034             public SavedState createFromParcel(Parcel in, ClassLoader loader) {
   12035                 return new SavedState(in, loader);
   12036             }
   12037 
   12038             @Override
   12039             public SavedState createFromParcel(Parcel in) {
   12040                 return new SavedState(in, null);
   12041             }
   12042 
   12043             @Override
   12044             public SavedState[] newArray(int size) {
   12045                 return new SavedState[size];
   12046             }
   12047         };
   12048     }
   12049 
   12050     /**
   12051      * <p>Contains useful information about the current RecyclerView state like target scroll
   12052      * position or view focus. State object can also keep arbitrary data, identified by resource
   12053      * ids.</p>
   12054      * <p>Often times, RecyclerView components will need to pass information between each other.
   12055      * To provide a well defined data bus between components, RecyclerView passes the same State
   12056      * object to component callbacks and these components can use it to exchange data.</p>
   12057      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
   12058      * data between your components without needing to manage their lifecycles.</p>
   12059      */
   12060     public static class State {
   12061         static final int STEP_START = 1;
   12062         static final int STEP_LAYOUT = 1 << 1;
   12063         static final int STEP_ANIMATIONS = 1 << 2;
   12064 
   12065         void assertLayoutStep(int accepted) {
   12066             if ((accepted & mLayoutStep) == 0) {
   12067                 throw new IllegalStateException("Layout state should be one of "
   12068                         + Integer.toBinaryString(accepted) + " but it is "
   12069                         + Integer.toBinaryString(mLayoutStep));
   12070             }
   12071         }
   12072 
   12073 
   12074         /** Owned by SmoothScroller */
   12075         private int mTargetPosition = RecyclerView.NO_POSITION;
   12076 
   12077         private SparseArray<Object> mData;
   12078 
   12079         ////////////////////////////////////////////////////////////////////////////////////////////
   12080         // Fields below are carried from one layout pass to the next
   12081         ////////////////////////////////////////////////////////////////////////////////////////////
   12082 
   12083         /**
   12084          * Number of items adapter had in the previous layout.
   12085          */
   12086         int mPreviousLayoutItemCount = 0;
   12087 
   12088         /**
   12089          * Number of items that were NOT laid out but has been deleted from the adapter after the
   12090          * previous layout.
   12091          */
   12092         int mDeletedInvisibleItemCountSincePreviousLayout = 0;
   12093 
   12094         ////////////////////////////////////////////////////////////////////////////////////////////
   12095         // Fields below must be updated or cleared before they are used (generally before a pass)
   12096         ////////////////////////////////////////////////////////////////////////////////////////////
   12097 
   12098         @IntDef(flag = true, value = {
   12099                 STEP_START, STEP_LAYOUT, STEP_ANIMATIONS
   12100         })
   12101         @Retention(RetentionPolicy.SOURCE)
   12102         @interface LayoutState {}
   12103 
   12104         @LayoutState
   12105         int mLayoutStep = STEP_START;
   12106 
   12107         /**
   12108          * Number of items adapter has.
   12109          */
   12110         int mItemCount = 0;
   12111 
   12112         boolean mStructureChanged = false;
   12113 
   12114         /**
   12115          * True if the associated {@link RecyclerView} is in the pre-layout step where it is having
   12116          * its {@link LayoutManager} layout items where they will be at the beginning of a set of
   12117          * predictive item animations.
   12118          */
   12119         boolean mInPreLayout = false;
   12120 
   12121         boolean mTrackOldChangeHolders = false;
   12122 
   12123         boolean mIsMeasuring = false;
   12124 
   12125         ////////////////////////////////////////////////////////////////////////////////////////////
   12126         // Fields below are always reset outside of the pass (or passes) that use them
   12127         ////////////////////////////////////////////////////////////////////////////////////////////
   12128 
   12129         boolean mRunSimpleAnimations = false;
   12130 
   12131         boolean mRunPredictiveAnimations = false;
   12132 
   12133         /**
   12134          * This data is saved before a layout calculation happens. After the layout is finished,
   12135          * if the previously focused view has been replaced with another view for the same item, we
   12136          * move the focus to the new item automatically.
   12137          */
   12138         int mFocusedItemPosition;
   12139         long mFocusedItemId;
   12140         // when a sub child has focus, record its id and see if we can directly request focus on
   12141         // that one instead
   12142         int mFocusedSubChildId;
   12143 
   12144         int mRemainingScrollHorizontal;
   12145         int mRemainingScrollVertical;
   12146 
   12147         ////////////////////////////////////////////////////////////////////////////////////////////
   12148 
   12149         State reset() {
   12150             mTargetPosition = RecyclerView.NO_POSITION;
   12151             if (mData != null) {
   12152                 mData.clear();
   12153             }
   12154             mItemCount = 0;
   12155             mStructureChanged = false;
   12156             mIsMeasuring = false;
   12157             return this;
   12158         }
   12159 
   12160         /**
   12161          * Prepare for a prefetch occurring on the RecyclerView in between traversals, potentially
   12162          * prior to any layout passes.
   12163          *
   12164          * <p>Don't touch any state stored between layout passes, only reset per-layout state, so
   12165          * that Recycler#getViewForPosition() can function safely.</p>
   12166          */
   12167         void prepareForNestedPrefetch(Adapter adapter) {
   12168             mLayoutStep = STEP_START;
   12169             mItemCount = adapter.getItemCount();
   12170             mInPreLayout = false;
   12171             mTrackOldChangeHolders = false;
   12172             mIsMeasuring = false;
   12173         }
   12174 
   12175         /**
   12176          * Returns true if the RecyclerView is currently measuring the layout. This value is
   12177          * {@code true} only if the LayoutManager opted into the auto measure API and RecyclerView
   12178          * has non-exact measurement specs.
   12179          * <p>
   12180          * Note that if the LayoutManager supports predictive animations and it is calculating the
   12181          * pre-layout step, this value will be {@code false} even if the RecyclerView is in
   12182          * {@code onMeasure} call. This is because pre-layout means the previous state of the
   12183          * RecyclerView and measurements made for that state cannot change the RecyclerView's size.
   12184          * LayoutManager is always guaranteed to receive another call to
   12185          * {@link LayoutManager#onLayoutChildren(Recycler, State)} when this happens.
   12186          *
   12187          * @return True if the RecyclerView is currently calculating its bounds, false otherwise.
   12188          */
   12189         public boolean isMeasuring() {
   12190             return mIsMeasuring;
   12191         }
   12192 
   12193         /**
   12194          * Returns true if the {@link RecyclerView} is in the pre-layout step where it is having its
   12195          * {@link LayoutManager} layout items where they will be at the beginning of a set of
   12196          * predictive item animations.
   12197          */
   12198         public boolean isPreLayout() {
   12199             return mInPreLayout;
   12200         }
   12201 
   12202         /**
   12203          * Returns whether RecyclerView will run predictive animations in this layout pass
   12204          * or not.
   12205          *
   12206          * @return true if RecyclerView is calculating predictive animations to be run at the end
   12207          *         of the layout pass.
   12208          */
   12209         public boolean willRunPredictiveAnimations() {
   12210             return mRunPredictiveAnimations;
   12211         }
   12212 
   12213         /**
   12214          * Returns whether RecyclerView will run simple animations in this layout pass
   12215          * or not.
   12216          *
   12217          * @return true if RecyclerView is calculating simple animations to be run at the end of
   12218          *         the layout pass.
   12219          */
   12220         public boolean willRunSimpleAnimations() {
   12221             return mRunSimpleAnimations;
   12222         }
   12223 
   12224         /**
   12225          * Removes the mapping from the specified id, if there was any.
   12226          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
   12227          *                   preserve cross functionality and avoid conflicts.
   12228          */
   12229         public void remove(int resourceId) {
   12230             if (mData == null) {
   12231                 return;
   12232             }
   12233             mData.remove(resourceId);
   12234         }
   12235 
   12236         /**
   12237          * Gets the Object mapped from the specified id, or <code>null</code>
   12238          * if no such data exists.
   12239          *
   12240          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
   12241          *                   to
   12242          *                   preserve cross functionality and avoid conflicts.
   12243          */
   12244         @SuppressWarnings("TypeParameterUnusedInFormals")
   12245         public <T> T get(int resourceId) {
   12246             if (mData == null) {
   12247                 return null;
   12248             }
   12249             return (T) mData.get(resourceId);
   12250         }
   12251 
   12252         /**
   12253          * Adds a mapping from the specified id to the specified value, replacing the previous
   12254          * mapping from the specified key if there was one.
   12255          *
   12256          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
   12257          *                   preserve cross functionality and avoid conflicts.
   12258          * @param data       The data you want to associate with the resourceId.
   12259          */
   12260         public void put(int resourceId, Object data) {
   12261             if (mData == null) {
   12262                 mData = new SparseArray<Object>();
   12263             }
   12264             mData.put(resourceId, data);
   12265         }
   12266 
   12267         /**
   12268          * If scroll is triggered to make a certain item visible, this value will return the
   12269          * adapter index of that item.
   12270          * @return Adapter index of the target item or
   12271          * {@link RecyclerView#NO_POSITION} if there is no target
   12272          * position.
   12273          */
   12274         public int getTargetScrollPosition() {
   12275             return mTargetPosition;
   12276         }
   12277 
   12278         /**
   12279          * Returns if current scroll has a target position.
   12280          * @return true if scroll is being triggered to make a certain position visible
   12281          * @see #getTargetScrollPosition()
   12282          */
   12283         public boolean hasTargetScrollPosition() {
   12284             return mTargetPosition != RecyclerView.NO_POSITION;
   12285         }
   12286 
   12287         /**
   12288          * @return true if the structure of the data set has changed since the last call to
   12289          *         onLayoutChildren, false otherwise
   12290          */
   12291         public boolean didStructureChange() {
   12292             return mStructureChanged;
   12293         }
   12294 
   12295         /**
   12296          * Returns the total number of items that can be laid out. Note that this number is not
   12297          * necessarily equal to the number of items in the adapter, so you should always use this
   12298          * number for your position calculations and never access the adapter directly.
   12299          * <p>
   12300          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
   12301          * data changes on existing Views. These calculations are used to decide which animations
   12302          * should be run.
   12303          * <p>
   12304          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
   12305          * present the correct state to LayoutManager in pre-layout pass.
   12306          * <p>
   12307          * For example, a newly added item is not included in pre-layout item count because
   12308          * pre-layout reflects the contents of the adapter before the item is added. Behind the
   12309          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
   12310          * LayoutManager does not know about the new item's existence in pre-layout. The item will
   12311          * be available in second layout pass and will be included in the item count. Similar
   12312          * adjustments are made for moved and removed items as well.
   12313          * <p>
   12314          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
   12315          *
   12316          * @return The number of items currently available
   12317          * @see LayoutManager#getItemCount()
   12318          */
   12319         public int getItemCount() {
   12320             return mInPreLayout
   12321                     ? (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout)
   12322                     : mItemCount;
   12323         }
   12324 
   12325         /**
   12326          * Returns remaining horizontal scroll distance of an ongoing scroll animation(fling/
   12327          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
   12328          * other than {@link #SCROLL_STATE_SETTLING}.
   12329          *
   12330          * @return Remaining horizontal scroll distance
   12331          */
   12332         public int getRemainingScrollHorizontal() {
   12333             return mRemainingScrollHorizontal;
   12334         }
   12335 
   12336         /**
   12337          * Returns remaining vertical scroll distance of an ongoing scroll animation(fling/
   12338          * smoothScrollTo/SmoothScroller) in pixels. Returns zero if {@link #getScrollState()} is
   12339          * other than {@link #SCROLL_STATE_SETTLING}.
   12340          *
   12341          * @return Remaining vertical scroll distance
   12342          */
   12343         public int getRemainingScrollVertical() {
   12344             return mRemainingScrollVertical;
   12345         }
   12346 
   12347         @Override
   12348         public String toString() {
   12349             return "State{"
   12350                     + "mTargetPosition=" + mTargetPosition
   12351                     + ", mData=" + mData
   12352                     + ", mItemCount=" + mItemCount
   12353                     + ", mIsMeasuring=" + mIsMeasuring
   12354                     + ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount
   12355                     + ", mDeletedInvisibleItemCountSincePreviousLayout="
   12356                     + mDeletedInvisibleItemCountSincePreviousLayout
   12357                     + ", mStructureChanged=" + mStructureChanged
   12358                     + ", mInPreLayout=" + mInPreLayout
   12359                     + ", mRunSimpleAnimations=" + mRunSimpleAnimations
   12360                     + ", mRunPredictiveAnimations=" + mRunPredictiveAnimations
   12361                     + '}';
   12362         }
   12363     }
   12364 
   12365     /**
   12366      * This class defines the behavior of fling if the developer wishes to handle it.
   12367      * <p>
   12368      * Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
   12369      *
   12370      * @see #setOnFlingListener(OnFlingListener)
   12371      */
   12372     public abstract static class OnFlingListener {
   12373 
   12374         /**
   12375          * Override this to handle a fling given the velocities in both x and y directions.
   12376          * Note that this method will only be called if the associated {@link LayoutManager}
   12377          * supports scrolling and the fling is not handled by nested scrolls first.
   12378          *
   12379          * @param velocityX the fling velocity on the X axis
   12380          * @param velocityY the fling velocity on the Y axis
   12381          *
   12382          * @return true if the fling was handled, false otherwise.
   12383          */
   12384         public abstract boolean onFling(int velocityX, int velocityY);
   12385     }
   12386 
   12387     /**
   12388      * Internal listener that manages items after animations finish. This is how items are
   12389      * retained (not recycled) during animations, but allowed to be recycled afterwards.
   12390      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
   12391      * method on the animator's listener when it is done animating any item.
   12392      */
   12393     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
   12394 
   12395         ItemAnimatorRestoreListener() {
   12396         }
   12397 
   12398         @Override
   12399         public void onAnimationFinished(ViewHolder item) {
   12400             item.setIsRecyclable(true);
   12401             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
   12402                 item.mShadowedHolder = null;
   12403             }
   12404             // always null this because an OldViewHolder can never become NewViewHolder w/o being
   12405             // recycled.
   12406             item.mShadowingHolder = null;
   12407             if (!item.shouldBeKeptAsChild()) {
   12408                 if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
   12409                     removeDetachedView(item.itemView, false);
   12410                 }
   12411             }
   12412         }
   12413     }
   12414 
   12415     /**
   12416      * This class defines the animations that take place on items as changes are made
   12417      * to the adapter.
   12418      *
   12419      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
   12420      * ViewHolder items. The RecyclerView will manage retaining these items while they
   12421      * are being animated, but implementors must call {@link #dispatchAnimationFinished(ViewHolder)}
   12422      * when a ViewHolder's animation is finished. In other words, there must be a matching
   12423      * {@link #dispatchAnimationFinished(ViewHolder)} call for each
   12424      * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo) animateAppearance()},
   12425      * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12426      * animateChange()}
   12427      * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo) animatePersistence()},
   12428      * and
   12429      * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12430      * animateDisappearance()} call.
   12431      *
   12432      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}.</p>
   12433      *
   12434      * @see #setItemAnimator(ItemAnimator)
   12435      */
   12436     @SuppressWarnings("UnusedParameters")
   12437     public abstract static class ItemAnimator {
   12438 
   12439         /**
   12440          * The Item represented by this ViewHolder is updated.
   12441          * <p>
   12442          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   12443          */
   12444         public static final int FLAG_CHANGED = ViewHolder.FLAG_UPDATE;
   12445 
   12446         /**
   12447          * The Item represented by this ViewHolder is removed from the adapter.
   12448          * <p>
   12449          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   12450          */
   12451         public static final int FLAG_REMOVED = ViewHolder.FLAG_REMOVED;
   12452 
   12453         /**
   12454          * Adapter {@link Adapter#notifyDataSetChanged()} has been called and the content
   12455          * represented by this ViewHolder is invalid.
   12456          * <p>
   12457          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   12458          */
   12459         public static final int FLAG_INVALIDATED = ViewHolder.FLAG_INVALID;
   12460 
   12461         /**
   12462          * The position of the Item represented by this ViewHolder has been changed. This flag is
   12463          * not bound to {@link Adapter#notifyItemMoved(int, int)}. It might be set in response to
   12464          * any adapter change that may have a side effect on this item. (e.g. The item before this
   12465          * one has been removed from the Adapter).
   12466          * <p>
   12467          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   12468          */
   12469         public static final int FLAG_MOVED = ViewHolder.FLAG_MOVED;
   12470 
   12471         /**
   12472          * This ViewHolder was not laid out but has been added to the layout in pre-layout state
   12473          * by the {@link LayoutManager}. This means that the item was already in the Adapter but
   12474          * invisible and it may become visible in the post layout phase. LayoutManagers may prefer
   12475          * to add new items in pre-layout to specify their virtual location when they are invisible
   12476          * (e.g. to specify the item should <i>animate in</i> from below the visible area).
   12477          * <p>
   12478          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   12479          */
   12480         public static final int FLAG_APPEARED_IN_PRE_LAYOUT =
   12481                 ViewHolder.FLAG_APPEARED_IN_PRE_LAYOUT;
   12482 
   12483         /**
   12484          * The set of flags that might be passed to
   12485          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12486          */
   12487         @IntDef(flag = true, value = {
   12488                 FLAG_CHANGED, FLAG_REMOVED, FLAG_MOVED, FLAG_INVALIDATED,
   12489                 FLAG_APPEARED_IN_PRE_LAYOUT
   12490         })
   12491         @Retention(RetentionPolicy.SOURCE)
   12492         public @interface AdapterChanges {}
   12493         private ItemAnimatorListener mListener = null;
   12494         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
   12495                 new ArrayList<ItemAnimatorFinishedListener>();
   12496 
   12497         private long mAddDuration = 120;
   12498         private long mRemoveDuration = 120;
   12499         private long mMoveDuration = 250;
   12500         private long mChangeDuration = 250;
   12501 
   12502         /**
   12503          * Gets the current duration for which all move animations will run.
   12504          *
   12505          * @return The current move duration
   12506          */
   12507         public long getMoveDuration() {
   12508             return mMoveDuration;
   12509         }
   12510 
   12511         /**
   12512          * Sets the duration for which all move animations will run.
   12513          *
   12514          * @param moveDuration The move duration
   12515          */
   12516         public void setMoveDuration(long moveDuration) {
   12517             mMoveDuration = moveDuration;
   12518         }
   12519 
   12520         /**
   12521          * Gets the current duration for which all add animations will run.
   12522          *
   12523          * @return The current add duration
   12524          */
   12525         public long getAddDuration() {
   12526             return mAddDuration;
   12527         }
   12528 
   12529         /**
   12530          * Sets the duration for which all add animations will run.
   12531          *
   12532          * @param addDuration The add duration
   12533          */
   12534         public void setAddDuration(long addDuration) {
   12535             mAddDuration = addDuration;
   12536         }
   12537 
   12538         /**
   12539          * Gets the current duration for which all remove animations will run.
   12540          *
   12541          * @return The current remove duration
   12542          */
   12543         public long getRemoveDuration() {
   12544             return mRemoveDuration;
   12545         }
   12546 
   12547         /**
   12548          * Sets the duration for which all remove animations will run.
   12549          *
   12550          * @param removeDuration The remove duration
   12551          */
   12552         public void setRemoveDuration(long removeDuration) {
   12553             mRemoveDuration = removeDuration;
   12554         }
   12555 
   12556         /**
   12557          * Gets the current duration for which all change animations will run.
   12558          *
   12559          * @return The current change duration
   12560          */
   12561         public long getChangeDuration() {
   12562             return mChangeDuration;
   12563         }
   12564 
   12565         /**
   12566          * Sets the duration for which all change animations will run.
   12567          *
   12568          * @param changeDuration The change duration
   12569          */
   12570         public void setChangeDuration(long changeDuration) {
   12571             mChangeDuration = changeDuration;
   12572         }
   12573 
   12574         /**
   12575          * Internal only:
   12576          * Sets the listener that must be called when the animator is finished
   12577          * animating the item (or immediately if no animation happens). This is set
   12578          * internally and is not intended to be set by external code.
   12579          *
   12580          * @param listener The listener that must be called.
   12581          */
   12582         void setListener(ItemAnimatorListener listener) {
   12583             mListener = listener;
   12584         }
   12585 
   12586         /**
   12587          * Called by the RecyclerView before the layout begins. Item animator should record
   12588          * necessary information about the View before it is potentially rebound, moved or removed.
   12589          * <p>
   12590          * The data returned from this method will be passed to the related <code>animate**</code>
   12591          * methods.
   12592          * <p>
   12593          * Note that this method may be called after pre-layout phase if LayoutManager adds new
   12594          * Views to the layout in pre-layout pass.
   12595          * <p>
   12596          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
   12597          * the View and the adapter change flags.
   12598          *
   12599          * @param state       The current State of RecyclerView which includes some useful data
   12600          *                    about the layout that will be calculated.
   12601          * @param viewHolder  The ViewHolder whose information should be recorded.
   12602          * @param changeFlags Additional information about what changes happened in the Adapter
   12603          *                    about the Item represented by this ViewHolder. For instance, if
   12604          *                    item is deleted from the adapter, {@link #FLAG_REMOVED} will be set.
   12605          * @param payloads    The payload list that was previously passed to
   12606          *                    {@link Adapter#notifyItemChanged(int, Object)} or
   12607          *                    {@link Adapter#notifyItemRangeChanged(int, int, Object)}.
   12608          *
   12609          * @return An ItemHolderInfo instance that preserves necessary information about the
   12610          * ViewHolder. This object will be passed back to related <code>animate**</code> methods
   12611          * after layout is complete.
   12612          *
   12613          * @see #recordPostLayoutInformation(State, ViewHolder)
   12614          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12615          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12616          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12617          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12618          */
   12619         public @NonNull ItemHolderInfo recordPreLayoutInformation(@NonNull State state,
   12620                 @NonNull ViewHolder viewHolder, @AdapterChanges int changeFlags,
   12621                 @NonNull List<Object> payloads) {
   12622             return obtainHolderInfo().setFrom(viewHolder);
   12623         }
   12624 
   12625         /**
   12626          * Called by the RecyclerView after the layout is complete. Item animator should record
   12627          * necessary information about the View's final state.
   12628          * <p>
   12629          * The data returned from this method will be passed to the related <code>animate**</code>
   12630          * methods.
   12631          * <p>
   12632          * The default implementation returns an {@link ItemHolderInfo} which holds the bounds of
   12633          * the View.
   12634          *
   12635          * @param state      The current State of RecyclerView which includes some useful data about
   12636          *                   the layout that will be calculated.
   12637          * @param viewHolder The ViewHolder whose information should be recorded.
   12638          *
   12639          * @return An ItemHolderInfo that preserves necessary information about the ViewHolder.
   12640          * This object will be passed back to related <code>animate**</code> methods when
   12641          * RecyclerView decides how items should be animated.
   12642          *
   12643          * @see #recordPreLayoutInformation(State, ViewHolder, int, List)
   12644          * @see #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12645          * @see #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12646          * @see #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12647          * @see #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12648          */
   12649         public @NonNull ItemHolderInfo recordPostLayoutInformation(@NonNull State state,
   12650                 @NonNull ViewHolder viewHolder) {
   12651             return obtainHolderInfo().setFrom(viewHolder);
   12652         }
   12653 
   12654         /**
   12655          * Called by the RecyclerView when a ViewHolder has disappeared from the layout.
   12656          * <p>
   12657          * This means that the View was a child of the LayoutManager when layout started but has
   12658          * been removed by the LayoutManager. It might have been removed from the adapter or simply
   12659          * become invisible due to other factors. You can distinguish these two cases by checking
   12660          * the change flags that were passed to
   12661          * {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12662          * <p>
   12663          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
   12664          * animation callback method which will be called by the RecyclerView depends on the
   12665          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
   12666          * LayoutManager's decision whether to layout the changed version of a disappearing
   12667          * ViewHolder or not. RecyclerView will call
   12668          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12669          * animateChange} instead of {@code animateDisappearance} if and only if the ItemAnimator
   12670          * returns {@code false} from
   12671          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
   12672          * LayoutManager lays out a new disappearing view that holds the updated information.
   12673          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
   12674          * <p>
   12675          * If LayoutManager supports predictive animations, it might provide a target disappear
   12676          * location for the View by laying it out in that location. When that happens,
   12677          * RecyclerView will call {@link #recordPostLayoutInformation(State, ViewHolder)} and the
   12678          * response of that call will be passed to this method as the <code>postLayoutInfo</code>.
   12679          * <p>
   12680          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   12681          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   12682          * decides not to animate the view).
   12683          *
   12684          * @param viewHolder    The ViewHolder which should be animated
   12685          * @param preLayoutInfo The information that was returned from
   12686          *                      {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12687          * @param postLayoutInfo The information that was returned from
   12688          *                       {@link #recordPostLayoutInformation(State, ViewHolder)}. Might be
   12689          *                       null if the LayoutManager did not layout the item.
   12690          *
   12691          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   12692          * false otherwise.
   12693          */
   12694         public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
   12695                 @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);
   12696 
   12697         /**
   12698          * Called by the RecyclerView when a ViewHolder is added to the layout.
   12699          * <p>
   12700          * In detail, this means that the ViewHolder was <b>not</b> a child when the layout started
   12701          * but has  been added by the LayoutManager. It might be newly added to the adapter or
   12702          * simply become visible due to other factors.
   12703          * <p>
   12704          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   12705          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   12706          * decides not to animate the view).
   12707          *
   12708          * @param viewHolder     The ViewHolder which should be animated
   12709          * @param preLayoutInfo  The information that was returned from
   12710          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12711          *                       Might be null if Item was just added to the adapter or
   12712          *                       LayoutManager does not support predictive animations or it could
   12713          *                       not predict that this ViewHolder will become visible.
   12714          * @param postLayoutInfo The information that was returned from {@link
   12715          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12716          *
   12717          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   12718          * false otherwise.
   12719          */
   12720         public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
   12721                 @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   12722 
   12723         /**
   12724          * Called by the RecyclerView when a ViewHolder is present in both before and after the
   12725          * layout and RecyclerView has not received a {@link Adapter#notifyItemChanged(int)} call
   12726          * for it or a {@link Adapter#notifyDataSetChanged()} call.
   12727          * <p>
   12728          * This ViewHolder still represents the same data that it was representing when the layout
   12729          * started but its position / size may be changed by the LayoutManager.
   12730          * <p>
   12731          * If the Item's layout position didn't change, RecyclerView still calls this method because
   12732          * it does not track this information (or does not necessarily know that an animation is
   12733          * not required). Your ItemAnimator should handle this case and if there is nothing to
   12734          * animate, it should call {@link #dispatchAnimationFinished(ViewHolder)} and return
   12735          * <code>false</code>.
   12736          * <p>
   12737          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} when the animation
   12738          * is complete (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it
   12739          * decides not to animate the view).
   12740          *
   12741          * @param viewHolder     The ViewHolder which should be animated
   12742          * @param preLayoutInfo  The information that was returned from
   12743          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12744          * @param postLayoutInfo The information that was returned from {@link
   12745          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12746          *
   12747          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   12748          * false otherwise.
   12749          */
   12750         public abstract boolean animatePersistence(@NonNull ViewHolder viewHolder,
   12751                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   12752 
   12753         /**
   12754          * Called by the RecyclerView when an adapter item is present both before and after the
   12755          * layout and RecyclerView has received a {@link Adapter#notifyItemChanged(int)} call
   12756          * for it. This method may also be called when
   12757          * {@link Adapter#notifyDataSetChanged()} is called and adapter has stable ids so that
   12758          * RecyclerView could still rebind views to the same ViewHolders. If viewType changes when
   12759          * {@link Adapter#notifyDataSetChanged()} is called, this method <b>will not</b> be called,
   12760          * instead, {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)} will be
   12761          * called for the new ViewHolder and the old one will be recycled.
   12762          * <p>
   12763          * If this method is called due to a {@link Adapter#notifyDataSetChanged()} call, there is
   12764          * a good possibility that item contents didn't really change but it is rebound from the
   12765          * adapter. {@link DefaultItemAnimator} will skip animating the View if its location on the
   12766          * screen didn't change and your animator should handle this case as well and avoid creating
   12767          * unnecessary animations.
   12768          * <p>
   12769          * When an item is updated, ItemAnimator has a chance to ask RecyclerView to keep the
   12770          * previous presentation of the item as-is and supply a new ViewHolder for the updated
   12771          * presentation (see: {@link #canReuseUpdatedViewHolder(ViewHolder, List)}.
   12772          * This is useful if you don't know the contents of the Item and would like
   12773          * to cross-fade the old and the new one ({@link DefaultItemAnimator} uses this technique).
   12774          * <p>
   12775          * When you are writing a custom item animator for your layout, it might be more performant
   12776          * and elegant to re-use the same ViewHolder and animate the content changes manually.
   12777          * <p>
   12778          * When {@link Adapter#notifyItemChanged(int)} is called, the Item's view type may change.
   12779          * If the Item's view type has changed or ItemAnimator returned <code>false</code> for
   12780          * this ViewHolder when {@link #canReuseUpdatedViewHolder(ViewHolder, List)} was called, the
   12781          * <code>oldHolder</code> and <code>newHolder</code> will be different ViewHolder instances
   12782          * which represent the same Item. In that case, only the new ViewHolder is visible
   12783          * to the LayoutManager but RecyclerView keeps old ViewHolder attached for animations.
   12784          * <p>
   12785          * ItemAnimator must call {@link #dispatchAnimationFinished(ViewHolder)} for each distinct
   12786          * ViewHolder when their animation is complete
   12787          * (or instantly call {@link #dispatchAnimationFinished(ViewHolder)} if it decides not to
   12788          * animate the view).
   12789          * <p>
   12790          *  If oldHolder and newHolder are the same instance, you should call
   12791          * {@link #dispatchAnimationFinished(ViewHolder)} <b>only once</b>.
   12792          * <p>
   12793          * Note that when a ViewHolder both changes and disappears in the same layout pass, the
   12794          * animation callback method which will be called by the RecyclerView depends on the
   12795          * ItemAnimator's decision whether to re-use the same ViewHolder or not, and also the
   12796          * LayoutManager's decision whether to layout the changed version of a disappearing
   12797          * ViewHolder or not. RecyclerView will call
   12798          * {@code animateChange} instead of
   12799          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12800          * animateDisappearance} if and only if the ItemAnimator returns {@code false} from
   12801          * {@link #canReuseUpdatedViewHolder(ViewHolder) canReuseUpdatedViewHolder} and the
   12802          * LayoutManager lays out a new disappearing view that holds the updated information.
   12803          * Built-in LayoutManagers try to avoid laying out updated versions of disappearing views.
   12804          *
   12805          * @param oldHolder     The ViewHolder before the layout is started, might be the same
   12806          *                      instance with newHolder.
   12807          * @param newHolder     The ViewHolder after the layout is finished, might be the same
   12808          *                      instance with oldHolder.
   12809          * @param preLayoutInfo  The information that was returned from
   12810          *                       {@link #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12811          * @param postLayoutInfo The information that was returned from {@link
   12812          *                       #recordPreLayoutInformation(State, ViewHolder, int, List)}.
   12813          *
   12814          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   12815          * false otherwise.
   12816          */
   12817         public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
   12818                 @NonNull ViewHolder newHolder,
   12819                 @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);
   12820 
   12821         @AdapterChanges static int buildAdapterChangeFlagsForAnimations(ViewHolder viewHolder) {
   12822             int flags = viewHolder.mFlags & (FLAG_INVALIDATED | FLAG_REMOVED | FLAG_CHANGED);
   12823             if (viewHolder.isInvalid()) {
   12824                 return FLAG_INVALIDATED;
   12825             }
   12826             if ((flags & FLAG_INVALIDATED) == 0) {
   12827                 final int oldPos = viewHolder.getOldPosition();
   12828                 final int pos = viewHolder.getAdapterPosition();
   12829                 if (oldPos != NO_POSITION && pos != NO_POSITION && oldPos != pos) {
   12830                     flags |= FLAG_MOVED;
   12831                 }
   12832             }
   12833             return flags;
   12834         }
   12835 
   12836         /**
   12837          * Called when there are pending animations waiting to be started. This state
   12838          * is governed by the return values from
   12839          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12840          * animateAppearance()},
   12841          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12842          * animateChange()}
   12843          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12844          * animatePersistence()}, and
   12845          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12846          * animateDisappearance()}, which inform the RecyclerView that the ItemAnimator wants to be
   12847          * called later to start the associated animations. runPendingAnimations() will be scheduled
   12848          * to be run on the next frame.
   12849          */
   12850         public abstract void runPendingAnimations();
   12851 
   12852         /**
   12853          * Method called when an animation on a view should be ended immediately.
   12854          * This could happen when other events, like scrolling, occur, so that
   12855          * animating views can be quickly put into their proper end locations.
   12856          * Implementations should ensure that any animations running on the item
   12857          * are canceled and affected properties are set to their end values.
   12858          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
   12859          * animation since the animations are effectively done when this method is called.
   12860          *
   12861          * @param item The item for which an animation should be stopped.
   12862          */
   12863         public abstract void endAnimation(@NonNull ViewHolder item);
   12864 
   12865         /**
   12866          * Method called when all item animations should be ended immediately.
   12867          * This could happen when other events, like scrolling, occur, so that
   12868          * animating views can be quickly put into their proper end locations.
   12869          * Implementations should ensure that any animations running on any items
   12870          * are canceled and affected properties are set to their end values.
   12871          * Also, {@link #dispatchAnimationFinished(ViewHolder)} should be called for each finished
   12872          * animation since the animations are effectively done when this method is called.
   12873          */
   12874         public abstract void endAnimations();
   12875 
   12876         /**
   12877          * Method which returns whether there are any item animations currently running.
   12878          * This method can be used to determine whether to delay other actions until
   12879          * animations end.
   12880          *
   12881          * @return true if there are any item animations currently running, false otherwise.
   12882          */
   12883         public abstract boolean isRunning();
   12884 
   12885         /**
   12886          * Method to be called by subclasses when an animation is finished.
   12887          * <p>
   12888          * For each call RecyclerView makes to
   12889          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12890          * animateAppearance()},
   12891          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12892          * animatePersistence()}, or
   12893          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12894          * animateDisappearance()}, there
   12895          * should
   12896          * be a matching {@link #dispatchAnimationFinished(ViewHolder)} call by the subclass.
   12897          * <p>
   12898          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12899          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
   12900          * and <code>newHolder</code>  (if they are not the same instance).
   12901          *
   12902          * @param viewHolder The ViewHolder whose animation is finished.
   12903          * @see #onAnimationFinished(ViewHolder)
   12904          */
   12905         public final void dispatchAnimationFinished(@NonNull ViewHolder viewHolder) {
   12906             onAnimationFinished(viewHolder);
   12907             if (mListener != null) {
   12908                 mListener.onAnimationFinished(viewHolder);
   12909             }
   12910         }
   12911 
   12912         /**
   12913          * Called after {@link #dispatchAnimationFinished(ViewHolder)} is called by the
   12914          * ItemAnimator.
   12915          *
   12916          * @param viewHolder The ViewHolder whose animation is finished. There might still be other
   12917          *                   animations running on this ViewHolder.
   12918          * @see #dispatchAnimationFinished(ViewHolder)
   12919          */
   12920         public void onAnimationFinished(@NonNull ViewHolder viewHolder) {
   12921         }
   12922 
   12923         /**
   12924          * Method to be called by subclasses when an animation is started.
   12925          * <p>
   12926          * For each call RecyclerView makes to
   12927          * {@link #animateAppearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12928          * animateAppearance()},
   12929          * {@link #animatePersistence(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12930          * animatePersistence()}, or
   12931          * {@link #animateDisappearance(ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12932          * animateDisappearance()}, there should be a matching
   12933          * {@link #dispatchAnimationStarted(ViewHolder)} call by the subclass.
   12934          * <p>
   12935          * For {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)
   12936          * animateChange()}, subclass should call this method for both the <code>oldHolder</code>
   12937          * and <code>newHolder</code> (if they are not the same instance).
   12938          * <p>
   12939          * If your ItemAnimator decides not to animate a ViewHolder, it should call
   12940          * {@link #dispatchAnimationFinished(ViewHolder)} <b>without</b> calling
   12941          * {@link #dispatchAnimationStarted(ViewHolder)}.
   12942          *
   12943          * @param viewHolder The ViewHolder whose animation is starting.
   12944          * @see #onAnimationStarted(ViewHolder)
   12945          */
   12946         public final void dispatchAnimationStarted(@NonNull ViewHolder viewHolder) {
   12947             onAnimationStarted(viewHolder);
   12948         }
   12949 
   12950         /**
   12951          * Called when a new animation is started on the given ViewHolder.
   12952          *
   12953          * @param viewHolder The ViewHolder which started animating. Note that the ViewHolder
   12954          *                   might already be animating and this might be another animation.
   12955          * @see #dispatchAnimationStarted(ViewHolder)
   12956          */
   12957         public void onAnimationStarted(@NonNull ViewHolder viewHolder) {
   12958 
   12959         }
   12960 
   12961         /**
   12962          * Like {@link #isRunning()}, this method returns whether there are any item
   12963          * animations currently running. Additionally, the listener passed in will be called
   12964          * when there are no item animations running, either immediately (before the method
   12965          * returns) if no animations are currently running, or when the currently running
   12966          * animations are {@link #dispatchAnimationsFinished() finished}.
   12967          *
   12968          * <p>Note that the listener is transient - it is either called immediately and not
   12969          * stored at all, or stored only until it is called when running animations
   12970          * are finished sometime later.</p>
   12971          *
   12972          * @param listener A listener to be called immediately if no animations are running
   12973          * or later when currently-running animations have finished. A null listener is
   12974          * equivalent to calling {@link #isRunning()}.
   12975          * @return true if there are any item animations currently running, false otherwise.
   12976          */
   12977         public final boolean isRunning(@Nullable ItemAnimatorFinishedListener listener) {
   12978             boolean running = isRunning();
   12979             if (listener != null) {
   12980                 if (!running) {
   12981                     listener.onAnimationsFinished();
   12982                 } else {
   12983                     mFinishedListeners.add(listener);
   12984                 }
   12985             }
   12986             return running;
   12987         }
   12988 
   12989         /**
   12990          * When an item is changed, ItemAnimator can decide whether it wants to re-use
   12991          * the same ViewHolder for animations or RecyclerView should create a copy of the
   12992          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
   12993          * <p>
   12994          * Note that this method will only be called if the {@link ViewHolder} still has the same
   12995          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
   12996          * both {@link ViewHolder}s in the
   12997          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
   12998          * <p>
   12999          * If your application is using change payloads, you can override
   13000          * {@link #canReuseUpdatedViewHolder(ViewHolder, List)} to decide based on payloads.
   13001          *
   13002          * @param viewHolder The ViewHolder which represents the changed item's old content.
   13003          *
   13004          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
   13005          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
   13006          *         ItemAnimator to animate. Default implementation returns <code>true</code>.
   13007          *
   13008          * @see #canReuseUpdatedViewHolder(ViewHolder, List)
   13009          */
   13010         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder) {
   13011             return true;
   13012         }
   13013 
   13014         /**
   13015          * When an item is changed, ItemAnimator can decide whether it wants to re-use
   13016          * the same ViewHolder for animations or RecyclerView should create a copy of the
   13017          * item and ItemAnimator will use both to run the animation (e.g. cross-fade).
   13018          * <p>
   13019          * Note that this method will only be called if the {@link ViewHolder} still has the same
   13020          * type ({@link Adapter#getItemViewType(int)}). Otherwise, ItemAnimator will always receive
   13021          * both {@link ViewHolder}s in the
   13022          * {@link #animateChange(ViewHolder, ViewHolder, ItemHolderInfo, ItemHolderInfo)} method.
   13023          *
   13024          * @param viewHolder The ViewHolder which represents the changed item's old content.
   13025          * @param payloads A non-null list of merged payloads that were sent with change
   13026          *                 notifications. Can be empty if the adapter is invalidated via
   13027          *                 {@link RecyclerView.Adapter#notifyDataSetChanged()}. The same list of
   13028          *                 payloads will be passed into
   13029          *                 {@link RecyclerView.Adapter#onBindViewHolder(ViewHolder, int, List)}
   13030          *                 method <b>if</b> this method returns <code>true</code>.
   13031          *
   13032          * @return True if RecyclerView should just rebind to the same ViewHolder or false if
   13033          *         RecyclerView should create a new ViewHolder and pass this ViewHolder to the
   13034          *         ItemAnimator to animate. Default implementation calls
   13035          *         {@link #canReuseUpdatedViewHolder(ViewHolder)}.
   13036          *
   13037          * @see #canReuseUpdatedViewHolder(ViewHolder)
   13038          */
   13039         public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
   13040                 @NonNull List<Object> payloads) {
   13041             return canReuseUpdatedViewHolder(viewHolder);
   13042         }
   13043 
   13044         /**
   13045          * This method should be called by ItemAnimator implementations to notify
   13046          * any listeners that all pending and active item animations are finished.
   13047          */
   13048         public final void dispatchAnimationsFinished() {
   13049             final int count = mFinishedListeners.size();
   13050             for (int i = 0; i < count; ++i) {
   13051                 mFinishedListeners.get(i).onAnimationsFinished();
   13052             }
   13053             mFinishedListeners.clear();
   13054         }
   13055 
   13056         /**
   13057          * Returns a new {@link ItemHolderInfo} which will be used to store information about the
   13058          * ViewHolder. This information will later be passed into <code>animate**</code> methods.
   13059          * <p>
   13060          * You can override this method if you want to extend {@link ItemHolderInfo} and provide
   13061          * your own instances.
   13062          *
   13063          * @return A new {@link ItemHolderInfo}.
   13064          */
   13065         @NonNull
   13066         public ItemHolderInfo obtainHolderInfo() {
   13067             return new ItemHolderInfo();
   13068         }
   13069 
   13070         /**
   13071          * The interface to be implemented by listeners to animation events from this
   13072          * ItemAnimator. This is used internally and is not intended for developers to
   13073          * create directly.
   13074          */
   13075         interface ItemAnimatorListener {
   13076             void onAnimationFinished(@NonNull ViewHolder item);
   13077         }
   13078 
   13079         /**
   13080          * This interface is used to inform listeners when all pending or running animations
   13081          * in an ItemAnimator are finished. This can be used, for example, to delay an action
   13082          * in a data set until currently-running animations are complete.
   13083          *
   13084          * @see #isRunning(ItemAnimatorFinishedListener)
   13085          */
   13086         public interface ItemAnimatorFinishedListener {
   13087             /**
   13088              * Notifies when all pending or running animations in an ItemAnimator are finished.
   13089              */
   13090             void onAnimationsFinished();
   13091         }
   13092 
   13093         /**
   13094          * A simple data structure that holds information about an item's bounds.
   13095          * This information is used in calculating item animations. Default implementation of
   13096          * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)} and
   13097          * {@link #recordPostLayoutInformation(RecyclerView.State, ViewHolder)} returns this data
   13098          * structure. You can extend this class if you would like to keep more information about
   13099          * the Views.
   13100          * <p>
   13101          * If you want to provide your own implementation but still use `super` methods to record
   13102          * basic information, you can override {@link #obtainHolderInfo()} to provide your own
   13103          * instances.
   13104          */
   13105         public static class ItemHolderInfo {
   13106 
   13107             /**
   13108              * The left edge of the View (excluding decorations)
   13109              */
   13110             public int left;
   13111 
   13112             /**
   13113              * The top edge of the View (excluding decorations)
   13114              */
   13115             public int top;
   13116 
   13117             /**
   13118              * The right edge of the View (excluding decorations)
   13119              */
   13120             public int right;
   13121 
   13122             /**
   13123              * The bottom edge of the View (excluding decorations)
   13124              */
   13125             public int bottom;
   13126 
   13127             /**
   13128              * The change flags that were passed to
   13129              * {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int, List)}.
   13130              */
   13131             @AdapterChanges
   13132             public int changeFlags;
   13133 
   13134             public ItemHolderInfo() {
   13135             }
   13136 
   13137             /**
   13138              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
   13139              * the given ViewHolder. Clears all {@link #changeFlags}.
   13140              *
   13141              * @param holder The ViewHolder whose bounds should be copied.
   13142              * @return This {@link ItemHolderInfo}
   13143              */
   13144             @NonNull
   13145             public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder) {
   13146                 return setFrom(holder, 0);
   13147             }
   13148 
   13149             /**
   13150              * Sets the {@link #left}, {@link #top}, {@link #right} and {@link #bottom} values from
   13151              * the given ViewHolder and sets the {@link #changeFlags} to the given flags parameter.
   13152              *
   13153              * @param holder The ViewHolder whose bounds should be copied.
   13154              * @param flags  The adapter change flags that were passed into
   13155              *               {@link #recordPreLayoutInformation(RecyclerView.State, ViewHolder, int,
   13156              *               List)}.
   13157              * @return This {@link ItemHolderInfo}
   13158              */
   13159             @NonNull
   13160             public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder,
   13161                     @AdapterChanges int flags) {
   13162                 final View view = holder.itemView;
   13163                 this.left = view.getLeft();
   13164                 this.top = view.getTop();
   13165                 this.right = view.getRight();
   13166                 this.bottom = view.getBottom();
   13167                 return this;
   13168             }
   13169         }
   13170     }
   13171 
   13172     @Override
   13173     protected int getChildDrawingOrder(int childCount, int i) {
   13174         if (mChildDrawingOrderCallback == null) {
   13175             return super.getChildDrawingOrder(childCount, i);
   13176         } else {
   13177             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
   13178         }
   13179     }
   13180 
   13181     /**
   13182      * A callback interface that can be used to alter the drawing order of RecyclerView children.
   13183      * <p>
   13184      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
   13185      * that applies to that method also applies to this callback. For example, changing the drawing
   13186      * order of two views will not have any effect if their elevation values are different since
   13187      * elevation overrides the result of this callback.
   13188      */
   13189     public interface ChildDrawingOrderCallback {
   13190         /**
   13191          * Returns the index of the child to draw for this iteration. Override this
   13192          * if you want to change the drawing order of children. By default, it
   13193          * returns i.
   13194          *
   13195          * @param i The current iteration.
   13196          * @return The index of the child to draw this iteration.
   13197          *
   13198          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
   13199          */
   13200         int onGetChildDrawingOrder(int childCount, int i);
   13201     }
   13202 
   13203     private NestedScrollingChildHelper getScrollingChildHelper() {
   13204         if (mScrollingChildHelper == null) {
   13205             mScrollingChildHelper = new NestedScrollingChildHelper(this);
   13206         }
   13207         return mScrollingChildHelper;
   13208     }
   13209 }
   13210