Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 
     18 package android.support.v7.widget;
     19 
     20 import android.content.Context;
     21 import android.content.res.TypedArray;
     22 import android.database.Observable;
     23 import android.graphics.Canvas;
     24 import android.graphics.PointF;
     25 import android.graphics.Rect;
     26 import android.os.Build;
     27 import android.os.Bundle;
     28 import android.os.Parcel;
     29 import android.os.Parcelable;
     30 import android.support.annotation.CallSuper;
     31 import android.os.SystemClock;
     32 import android.support.annotation.Nullable;
     33 import android.support.v4.os.TraceCompat;
     34 import android.support.v4.util.ArrayMap;
     35 import android.support.v4.view.InputDeviceCompat;
     36 import android.support.v4.view.MotionEventCompat;
     37 import android.support.v4.view.NestedScrollingChild;
     38 import android.support.v4.view.NestedScrollingChildHelper;
     39 import android.support.v4.view.ScrollingView;
     40 import android.support.v4.view.VelocityTrackerCompat;
     41 import android.support.v4.view.ViewCompat;
     42 import android.support.v4.view.ViewConfigurationCompat;
     43 import android.support.v4.view.accessibility.AccessibilityEventCompat;
     44 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
     45 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
     46 import android.support.v4.widget.EdgeEffectCompat;
     47 import android.support.v4.widget.ScrollerCompat;
     48 import android.support.v7.recyclerview.R;
     49 import android.util.AttributeSet;
     50 import android.util.Log;
     51 import android.util.SparseArray;
     52 import android.util.SparseIntArray;
     53 import android.util.TypedValue;
     54 import android.view.FocusFinder;
     55 import android.view.MotionEvent;
     56 import android.view.VelocityTracker;
     57 import android.view.View;
     58 import android.view.ViewConfiguration;
     59 import android.view.ViewGroup;
     60 import android.view.ViewParent;
     61 import android.view.accessibility.AccessibilityEvent;
     62 import android.view.accessibility.AccessibilityManager;
     63 import android.view.animation.Interpolator;
     64 
     65 import java.lang.reflect.Constructor;
     66 import java.lang.reflect.InvocationTargetException;
     67 import java.util.ArrayList;
     68 import java.util.Collections;
     69 import java.util.List;
     70 
     71 import static android.support.v7.widget.AdapterHelper.Callback;
     72 import static android.support.v7.widget.AdapterHelper.UpdateOp;
     73 
     74 /**
     75  * A flexible view for providing a limited window into a large data set.
     76  *
     77  * <h3>Glossary of terms:</h3>
     78  *
     79  * <ul>
     80  *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views
     81  *     that represent items in a data set.</li>
     82  *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li>
     83  *     <li><em>Index:</em> The index of an attached child view as used in a call to
     84  *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li>
     85  *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding
     86  *     to a <em>position</em> within the adapter.</li>
     87  *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter
     88  *     position may be placed in a cache for later reuse to display the same type of data again
     89  *     later. This can drastically improve performance by skipping initial layout inflation
     90  *     or construction.</li>
     91  *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached
     92  *     state during layout. Scrap views may be reused without becoming fully detached
     93  *     from the parent RecyclerView, either unmodified if no rebinding is required or modified
     94  *     by the adapter if the view was considered <em>dirty</em>.</li>
     95  *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before
     96  *     being displayed.</li>
     97  * </ul>
     98  *
     99  * <h4>Positions in RecyclerView:</h4>
    100  * <p>
    101  * RecyclerView introduces an additional level of abstraction between the {@link Adapter} and
    102  * {@link LayoutManager} to be able to detect data set changes in batches during a layout
    103  * calculation. This saves LayoutManager from tracking adapter changes to calculate animations.
    104  * It also helps with performance because all view bindings happen at the same time and unnecessary
    105  * bindings are avoided.
    106  * <p>
    107  * For this reason, there are two types of <code>position</code> related methods in RecyclerView:
    108  * <ul>
    109  *     <li>layout position: Position of an item in the latest layout calculation. This is the
    110  *     position from the LayoutManager's perspective.</li>
    111  *     <li>adapter position: Position of an item in the adapter. This is the position from
    112  *     the Adapter's perspective.</li>
    113  * </ul>
    114  * <p>
    115  * These two positions are the same except the time between dispatching <code>adapter.notify*
    116  * </code> events and calculating the updated layout.
    117  * <p>
    118  * Methods that return or receive <code>*LayoutPosition*</code> use position as of the latest
    119  * layout calculation (e.g. {@link ViewHolder#getLayoutPosition()},
    120  * {@link #findViewHolderForLayoutPosition(int)}). These positions include all changes until the
    121  * last layout calculation. You can rely on these positions to be consistent with what user is
    122  * currently seeing on the screen. For example, if you have a list of items on the screen and user
    123  * asks for the 5<sup>th</sup> element, you should use these methods as they'll match what user
    124  * is seeing.
    125  * <p>
    126  * The other set of position related methods are in the form of
    127  * <code>*AdapterPosition*</code>. (e.g. {@link ViewHolder#getAdapterPosition()},
    128  * {@link #findViewHolderForAdapterPosition(int)}) You should use these methods when you need to
    129  * work with up-to-date adapter positions even if they may not have been reflected to layout yet.
    130  * For example, if you want to access the item in the adapter on a ViewHolder click, you should use
    131  * {@link ViewHolder#getAdapterPosition()}. Beware that these methods may not be able to calculate
    132  * adapter positions if {@link Adapter#notifyDataSetChanged()} has been called and new layout has
    133  * not yet been calculated. For this reasons, you should carefully handle {@link #NO_POSITION} or
    134  * <code>null</code> results from these methods.
    135  * <p>
    136  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
    137  * writing an {@link Adapter}, you probably want to use adapter positions.
    138  *
    139  * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
    140  */
    141 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
    142 
    143     private static final String TAG = "RecyclerView";
    144 
    145     private static final boolean DEBUG = false;
    146 
    147     /**
    148      * On Kitkat and JB MR2, there is a bug which prevents DisplayList from being invalidated if
    149      * a View is two levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by
    150      * setting View's visibility to INVISIBLE when View is detached. On Kitkat and JB MR2, Recycler
    151      * recursively traverses itemView and invalidates display list for each ViewGroup that matches
    152      * this criteria.
    153      */
    154     private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 18
    155             || Build.VERSION.SDK_INT == 19 || Build.VERSION.SDK_INT == 20;
    156 
    157     private static final boolean DISPATCH_TEMP_DETACH = false;
    158     public static final int HORIZONTAL = 0;
    159     public static final int VERTICAL = 1;
    160 
    161     public static final int NO_POSITION = -1;
    162     public static final long NO_ID = -1;
    163     public static final int INVALID_TYPE = -1;
    164 
    165     /**
    166      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    167      * that the RecyclerView should use the standard touch slop for smooth,
    168      * continuous scrolling.
    169      */
    170     public static final int TOUCH_SLOP_DEFAULT = 0;
    171 
    172     /**
    173      * Constant for use with {@link #setScrollingTouchSlop(int)}. Indicates
    174      * that the RecyclerView should use the standard touch slop for scrolling
    175      * widgets that snap to a page or other coarse-grained barrier.
    176      */
    177     public static final int TOUCH_SLOP_PAGING = 1;
    178 
    179     private static final int MAX_SCROLL_DURATION = 2000;
    180 
    181     /**
    182      * RecyclerView is calculating a scroll.
    183      * If there are too many of these in Systrace, some Views inside RecyclerView might be causing
    184      * it. Try to avoid using EditText, focusable views or handle them with care.
    185      */
    186     private static final String TRACE_SCROLL_TAG = "RV Scroll";
    187 
    188     /**
    189      * OnLayout has been called by the View system.
    190      * If this shows up too many times in Systrace, make sure the children of RecyclerView do not
    191      * update themselves directly. This will cause a full re-layout but when it happens via the
    192      * Adapter notifyItemChanged, RecyclerView can avoid full layout calculation.
    193      */
    194     private static final String TRACE_ON_LAYOUT_TAG = "RV OnLayout";
    195 
    196     /**
    197      * NotifyDataSetChanged or equal has been called.
    198      * If this is taking a long time, try sending granular notify adapter changes instead of just
    199      * calling notifyDataSetChanged or setAdapter / swapAdapter. Adding stable ids to your adapter
    200      * might help.
    201      */
    202     private static final String TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG = "RV FullInvalidate";
    203 
    204     /**
    205      * RecyclerView is doing a layout for partial adapter updates (we know what has changed)
    206      * If this is taking a long time, you may have dispatched too many Adapter updates causing too
    207      * many Views being rebind. Make sure all are necessary and also prefer using notify*Range
    208      * methods.
    209      */
    210     private static final String TRACE_HANDLE_ADAPTER_UPDATES_TAG = "RV PartialInvalidate";
    211 
    212     /**
    213      * RecyclerView is rebinding a View.
    214      * If this is taking a lot of time, consider optimizing your layout or make sure you are not
    215      * doing extra operations in onBindViewHolder call.
    216      */
    217     private static final String TRACE_BIND_VIEW_TAG = "RV OnBindView";
    218 
    219     /**
    220      * RecyclerView is creating a new View.
    221      * If too many of these present in Systrace:
    222      * - There might be a problem in Recycling (e.g. custom Animations that set transient state and
    223      * prevent recycling or ItemAnimator not implementing the contract properly. ({@link
    224      * > Adapter#onFailedToRecycleView(ViewHolder)})
    225      *
    226      * - There might be too many item view types.
    227      * > Try merging them
    228      *
    229      * - There might be too many itemChange animations and not enough space in RecyclerPool.
    230      * >Try increasing your pool size and item cache size.
    231      */
    232     private static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
    233     private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
    234             new Class[]{Context.class, AttributeSet.class, int.class, int.class};
    235 
    236     private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
    237 
    238     final Recycler mRecycler = new Recycler();
    239 
    240     private SavedState mPendingSavedState;
    241 
    242     AdapterHelper mAdapterHelper;
    243 
    244     ChildHelper mChildHelper;
    245 
    246     /**
    247      * Prior to L, there is no way to query this variable which is why we override the setter and
    248      * track it here.
    249      */
    250     private boolean mClipToPadding;
    251 
    252     /**
    253      * Note: this Runnable is only ever posted if:
    254      * 1) We've been through first layout
    255      * 2) We know we have a fixed size (mHasFixedSize)
    256      * 3) We're attached
    257      */
    258     private final Runnable mUpdateChildViewsRunnable = new Runnable() {
    259         public void run() {
    260             if (!mFirstLayoutComplete) {
    261                 // a layout request will happen, we should not do layout here.
    262                 return;
    263             }
    264             if (mDataSetHasChangedAfterLayout) {
    265                 TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
    266                 dispatchLayout();
    267                 TraceCompat.endSection();
    268             } else if (mAdapterHelper.hasPendingUpdates()) {
    269                 TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
    270                 eatRequestLayout();
    271                 mAdapterHelper.preProcess();
    272                 if (!mLayoutRequestEaten) {
    273                     // We run this after pre-processing is complete so that ViewHolders have their
    274                     // final adapter positions. No need to run it if a layout is already requested.
    275                     rebindUpdatedViewHolders();
    276                 }
    277                 resumeRequestLayout(true);
    278                 TraceCompat.endSection();
    279             }
    280         }
    281     };
    282 
    283     private final Rect mTempRect = new Rect();
    284     private Adapter mAdapter;
    285     private LayoutManager mLayout;
    286     private RecyclerListener mRecyclerListener;
    287     private final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<ItemDecoration>();
    288     private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =
    289             new ArrayList<OnItemTouchListener>();
    290     private OnItemTouchListener mActiveOnItemTouchListener;
    291     private boolean mIsAttached;
    292     private boolean mHasFixedSize;
    293     private boolean mFirstLayoutComplete;
    294     private boolean mEatRequestLayout;
    295     private boolean mLayoutRequestEaten;
    296     private boolean mLayoutFrozen;
    297     private boolean mIgnoreMotionEventTillDown;
    298 
    299     // binary OR of change events that were eaten during a layout or scroll.
    300     private int mEatenAccessibilityChangeFlags;
    301     private boolean mAdapterUpdateDuringMeasure;
    302     private final boolean mPostUpdatesOnAnimation;
    303     private final AccessibilityManager mAccessibilityManager;
    304     private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
    305 
    306     /**
    307      * Set to true when an adapter data set changed notification is received.
    308      * In that case, we cannot run any animations since we don't know what happened.
    309      */
    310     private boolean mDataSetHasChangedAfterLayout = false;
    311 
    312     /**
    313      * This variable is incremented during a dispatchLayout and/or scroll.
    314      * Some methods should not be called during these periods (e.g. adapter data change).
    315      * Doing so will create hard to find bugs so we better check it and throw an exception.
    316      *
    317      * @see #assertInLayoutOrScroll(String)
    318      * @see #assertNotInLayoutOrScroll(String)
    319      */
    320     private int mLayoutOrScrollCounter = 0;
    321 
    322     private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
    323 
    324     ItemAnimator mItemAnimator = new DefaultItemAnimator();
    325 
    326     private static final int INVALID_POINTER = -1;
    327 
    328     /**
    329      * The RecyclerView is not currently scrolling.
    330      * @see #getScrollState()
    331      */
    332     public static final int SCROLL_STATE_IDLE = 0;
    333 
    334     /**
    335      * The RecyclerView is currently being dragged by outside input such as user touch input.
    336      * @see #getScrollState()
    337      */
    338     public static final int SCROLL_STATE_DRAGGING = 1;
    339 
    340     /**
    341      * The RecyclerView is currently animating to a final position while not under
    342      * outside control.
    343      * @see #getScrollState()
    344      */
    345     public static final int SCROLL_STATE_SETTLING = 2;
    346 
    347     // Touch/scrolling handling
    348 
    349     private int mScrollState = SCROLL_STATE_IDLE;
    350     private int mScrollPointerId = INVALID_POINTER;
    351     private VelocityTracker mVelocityTracker;
    352     private int mInitialTouchX;
    353     private int mInitialTouchY;
    354     private int mLastTouchX;
    355     private int mLastTouchY;
    356     private int mTouchSlop;
    357     private final int mMinFlingVelocity;
    358     private final int mMaxFlingVelocity;
    359     // This value is used when handling generic motion events.
    360     private float mScrollFactor = Float.MIN_VALUE;
    361 
    362     private final ViewFlinger mViewFlinger = new ViewFlinger();
    363 
    364     final State mState = new State();
    365 
    366     private OnScrollListener mScrollListener;
    367     private List<OnScrollListener> mScrollListeners;
    368 
    369     // For use in item animations
    370     boolean mItemsAddedOrRemoved = false;
    371     boolean mItemsChanged = false;
    372     private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
    373             new ItemAnimatorRestoreListener();
    374     private boolean mPostedAnimatorRunner = false;
    375     private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
    376     private ChildDrawingOrderCallback mChildDrawingOrderCallback;
    377 
    378     // simple array to keep min and max child position during a layout calculation
    379     // preserved not to create a new one in each layout pass
    380     private final int[] mMinMaxLayoutPositions = new int[2];
    381 
    382     private final NestedScrollingChildHelper mScrollingChildHelper;
    383     private final int[] mScrollOffset = new int[2];
    384     private final int[] mScrollConsumed = new int[2];
    385     private final int[] mNestedOffsets = new int[2];
    386 
    387     private Runnable mItemAnimatorRunner = new Runnable() {
    388         @Override
    389         public void run() {
    390             if (mItemAnimator != null) {
    391                 mItemAnimator.runPendingAnimations();
    392             }
    393             mPostedAnimatorRunner = false;
    394         }
    395     };
    396 
    397     private static final Interpolator sQuinticInterpolator = new Interpolator() {
    398         public float getInterpolation(float t) {
    399             t -= 1.0f;
    400             return t * t * t * t * t + 1.0f;
    401         }
    402     };
    403 
    404     public RecyclerView(Context context) {
    405         this(context, null);
    406     }
    407 
    408     public RecyclerView(Context context, @Nullable AttributeSet attrs) {
    409         this(context, attrs, 0);
    410     }
    411 
    412     public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    413         super(context, attrs, defStyle);
    414         setScrollContainer(true);
    415         setFocusableInTouchMode(true);
    416         final int version = Build.VERSION.SDK_INT;
    417         mPostUpdatesOnAnimation = version >= 16;
    418 
    419         final ViewConfiguration vc = ViewConfiguration.get(context);
    420         mTouchSlop = vc.getScaledTouchSlop();
    421         mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
    422         mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
    423         setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
    424 
    425         mItemAnimator.setListener(mItemAnimatorListener);
    426         initAdapterManager();
    427         initChildrenHelper();
    428         // If not explicitly specified this view is important for accessibility.
    429         if (ViewCompat.getImportantForAccessibility(this)
    430                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
    431             ViewCompat.setImportantForAccessibility(this,
    432                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
    433         }
    434         mAccessibilityManager = (AccessibilityManager) getContext()
    435                 .getSystemService(Context.ACCESSIBILITY_SERVICE);
    436         setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
    437         // Create the layoutManager if specified.
    438         if (attrs != null) {
    439             int defStyleRes = 0;
    440             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
    441                     defStyle, defStyleRes);
    442             String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
    443             a.recycle();
    444             createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
    445         }
    446 
    447         mScrollingChildHelper = new NestedScrollingChildHelper(this);
    448         setNestedScrollingEnabled(true);
    449     }
    450 
    451     /**
    452      * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
    453      * @return An instance of AccessibilityDelegateCompat used by RecyclerView
    454      */
    455     public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
    456         return mAccessibilityDelegate;
    457     }
    458 
    459     /**
    460      * Sets the accessibility delegate compatibility implementation used by RecyclerView.
    461      * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
    462      */
    463     public void setAccessibilityDelegateCompat(
    464             RecyclerViewAccessibilityDelegate accessibilityDelegate) {
    465         mAccessibilityDelegate = accessibilityDelegate;
    466         ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
    467     }
    468 
    469     /**
    470      * Instantiate and set a LayoutManager, if specified in the attributes.
    471      */
    472     private void createLayoutManager(Context context, String className, AttributeSet attrs,
    473             int defStyleAttr, int defStyleRes) {
    474         if (className != null) {
    475             className = className.trim();
    476             if (className.length() != 0) {  // Can't use isEmpty since it was added in API 9.
    477                 className = getFullClassName(context, className);
    478                 try {
    479                     ClassLoader classLoader;
    480                     if (isInEditMode()) {
    481                         // Stupid layoutlib cannot handle simple class loaders.
    482                         classLoader = this.getClass().getClassLoader();
    483                     } else {
    484                         classLoader = context.getClassLoader();
    485                     }
    486                     Class<? extends LayoutManager> layoutManagerClass =
    487                             classLoader.loadClass(className).asSubclass(LayoutManager.class);
    488                     Constructor<? extends LayoutManager> constructor;
    489                     Object[] constructorArgs = null;
    490                     try {
    491                         constructor = layoutManagerClass
    492                                 .getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
    493                         constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
    494                     } catch (NoSuchMethodException e) {
    495                         try {
    496                             constructor = layoutManagerClass.getConstructor();
    497                         } catch (NoSuchMethodException e1) {
    498                             e1.initCause(e);
    499                             throw new IllegalStateException(attrs.getPositionDescription() +
    500                                     ": Error creating LayoutManager " + className, e1);
    501                         }
    502                     }
    503                     constructor.setAccessible(true);
    504                     setLayoutManager(constructor.newInstance(constructorArgs));
    505                 } catch (ClassNotFoundException e) {
    506                     throw new IllegalStateException(attrs.getPositionDescription()
    507                             + ": Unable to find LayoutManager " + className, e);
    508                 } catch (InvocationTargetException e) {
    509                     throw new IllegalStateException(attrs.getPositionDescription()
    510                             + ": Could not instantiate the LayoutManager: " + className, e);
    511                 } catch (InstantiationException e) {
    512                     throw new IllegalStateException(attrs.getPositionDescription()
    513                             + ": Could not instantiate the LayoutManager: " + className, e);
    514                 } catch (IllegalAccessException e) {
    515                     throw new IllegalStateException(attrs.getPositionDescription()
    516                             + ": Cannot access non-public constructor " + className, e);
    517                 } catch (ClassCastException e) {
    518                     throw new IllegalStateException(attrs.getPositionDescription()
    519                             + ": Class is not a LayoutManager " + className, e);
    520                 }
    521             }
    522         }
    523     }
    524 
    525     private String getFullClassName(Context context, String className) {
    526         if (className.charAt(0) == '.') {
    527             return context.getPackageName() + className;
    528         }
    529         if (className.contains(".")) {
    530             return className;
    531         }
    532         return RecyclerView.class.getPackage().getName() + '.' + className;
    533     }
    534 
    535     private void initChildrenHelper() {
    536         mChildHelper = new ChildHelper(new ChildHelper.Callback() {
    537             @Override
    538             public int getChildCount() {
    539                 return RecyclerView.this.getChildCount();
    540             }
    541 
    542             @Override
    543             public void addView(View child, int index) {
    544                 RecyclerView.this.addView(child, index);
    545                 dispatchChildAttached(child);
    546             }
    547 
    548             @Override
    549             public int indexOfChild(View view) {
    550                 return RecyclerView.this.indexOfChild(view);
    551             }
    552 
    553             @Override
    554             public void removeViewAt(int index) {
    555                 final View child = RecyclerView.this.getChildAt(index);
    556                 if (child != null) {
    557                     dispatchChildDetached(child);
    558                 }
    559                 RecyclerView.this.removeViewAt(index);
    560             }
    561 
    562             @Override
    563             public View getChildAt(int offset) {
    564                 return RecyclerView.this.getChildAt(offset);
    565             }
    566 
    567             @Override
    568             public void removeAllViews() {
    569                 final int count = getChildCount();
    570                 for (int i = 0; i < count; i ++) {
    571                     dispatchChildDetached(getChildAt(i));
    572                 }
    573                 RecyclerView.this.removeAllViews();
    574             }
    575 
    576             @Override
    577             public ViewHolder getChildViewHolder(View view) {
    578                 return getChildViewHolderInt(view);
    579             }
    580 
    581             @Override
    582             public void attachViewToParent(View child, int index,
    583                     ViewGroup.LayoutParams layoutParams) {
    584                 final ViewHolder vh = getChildViewHolderInt(child);
    585                 if (vh != null) {
    586                     if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
    587                         throw new IllegalArgumentException("Called attach on a child which is not"
    588                                 + " detached: " + vh);
    589                     }
    590                     if (DEBUG) {
    591                         Log.d(TAG, "reAttach " + vh);
    592                     }
    593                     vh.clearTmpDetachFlag();
    594                 }
    595                 RecyclerView.this.attachViewToParent(child, index, layoutParams);
    596             }
    597 
    598             @Override
    599             public void detachViewFromParent(int offset) {
    600                 final View view = getChildAt(offset);
    601                 if (view != null) {
    602                     final ViewHolder vh = getChildViewHolderInt(view);
    603                     if (vh != null) {
    604                         if (vh.isTmpDetached() && !vh.shouldIgnore()) {
    605                             throw new IllegalArgumentException("called detach on an already"
    606                                     + " detached child " + vh);
    607                         }
    608                         if (DEBUG) {
    609                             Log.d(TAG, "tmpDetach " + vh);
    610                         }
    611                         vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
    612                     }
    613                 }
    614                 RecyclerView.this.detachViewFromParent(offset);
    615             }
    616 
    617             @Override
    618             public void onEnteredHiddenState(View child) {
    619                 final ViewHolder vh = getChildViewHolderInt(child);
    620                 if (vh != null) {
    621                     vh.onEnteredHiddenState();
    622                 }
    623             }
    624 
    625             @Override
    626             public void onLeftHiddenState(View child) {
    627                 final ViewHolder vh = getChildViewHolderInt(child);
    628                 if (vh != null) {
    629                     vh.onLeftHiddenState();
    630                 }
    631             }
    632         });
    633     }
    634 
    635     void initAdapterManager() {
    636         mAdapterHelper = new AdapterHelper(new Callback() {
    637             @Override
    638             public ViewHolder findViewHolder(int position) {
    639                 final ViewHolder vh = findViewHolderForPosition(position, true);
    640                 if (vh == null) {
    641                     return null;
    642                 }
    643                 // ensure it is not hidden because for adapter helper, the only thing matter is that
    644                 // LM thinks view is a child.
    645                 if (mChildHelper.isHidden(vh.itemView)) {
    646                     if (DEBUG) {
    647                         Log.d(TAG, "assuming view holder cannot be find because it is hidden");
    648                     }
    649                     return null;
    650                 }
    651                 return vh;
    652             }
    653 
    654             @Override
    655             public void offsetPositionsForRemovingInvisible(int start, int count) {
    656                 offsetPositionRecordsForRemove(start, count, true);
    657                 mItemsAddedOrRemoved = true;
    658                 mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
    659             }
    660 
    661             @Override
    662             public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) {
    663                 offsetPositionRecordsForRemove(positionStart, itemCount, false);
    664                 mItemsAddedOrRemoved = true;
    665             }
    666 
    667             @Override
    668             public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
    669                 viewRangeUpdate(positionStart, itemCount, payload);
    670                 mItemsChanged = true;
    671             }
    672 
    673             @Override
    674             public void onDispatchFirstPass(UpdateOp op) {
    675                 dispatchUpdate(op);
    676             }
    677 
    678             void dispatchUpdate(UpdateOp op) {
    679                 switch (op.cmd) {
    680                     case UpdateOp.ADD:
    681                         mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
    682                         break;
    683                     case UpdateOp.REMOVE:
    684                         mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
    685                         break;
    686                     case UpdateOp.UPDATE:
    687                         mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
    688                                 op.payload);
    689                         break;
    690                     case UpdateOp.MOVE:
    691                         mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
    692                         break;
    693                 }
    694             }
    695 
    696             @Override
    697             public void onDispatchSecondPass(UpdateOp op) {
    698                 dispatchUpdate(op);
    699             }
    700 
    701             @Override
    702             public void offsetPositionsForAdd(int positionStart, int itemCount) {
    703                 offsetPositionRecordsForInsert(positionStart, itemCount);
    704                 mItemsAddedOrRemoved = true;
    705             }
    706 
    707             @Override
    708             public void offsetPositionsForMove(int from, int to) {
    709                 offsetPositionRecordsForMove(from, to);
    710                 // should we create mItemsMoved ?
    711                 mItemsAddedOrRemoved = true;
    712             }
    713         });
    714     }
    715 
    716     /**
    717      * RecyclerView can perform several optimizations if it can know in advance that changes in
    718      * adapter content cannot change the size of the RecyclerView itself.
    719      * If your use of RecyclerView falls into this category, set this to true.
    720      *
    721      * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
    722      */
    723     public void setHasFixedSize(boolean hasFixedSize) {
    724         mHasFixedSize = hasFixedSize;
    725     }
    726 
    727     /**
    728      * @return true if the app has specified that changes in adapter content cannot change
    729      * the size of the RecyclerView itself.
    730      */
    731     public boolean hasFixedSize() {
    732         return mHasFixedSize;
    733     }
    734 
    735     @Override
    736     public void setClipToPadding(boolean clipToPadding) {
    737         if (clipToPadding != mClipToPadding) {
    738             invalidateGlows();
    739         }
    740         mClipToPadding = clipToPadding;
    741         super.setClipToPadding(clipToPadding);
    742         if (mFirstLayoutComplete) {
    743             requestLayout();
    744         }
    745     }
    746 
    747     /**
    748      * Configure the scrolling touch slop for a specific use case.
    749      *
    750      * Set up the RecyclerView's scrolling motion threshold based on common usages.
    751      * Valid arguments are {@link #TOUCH_SLOP_DEFAULT} and {@link #TOUCH_SLOP_PAGING}.
    752      *
    753      * @param slopConstant One of the <code>TOUCH_SLOP_</code> constants representing
    754      *                     the intended usage of this RecyclerView
    755      */
    756     public void setScrollingTouchSlop(int slopConstant) {
    757         final ViewConfiguration vc = ViewConfiguration.get(getContext());
    758         switch (slopConstant) {
    759             default:
    760                 Log.w(TAG, "setScrollingTouchSlop(): bad argument constant "
    761                       + slopConstant + "; using default value");
    762                 // fall-through
    763             case TOUCH_SLOP_DEFAULT:
    764                 mTouchSlop = vc.getScaledTouchSlop();
    765                 break;
    766 
    767             case TOUCH_SLOP_PAGING:
    768                 mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc);
    769                 break;
    770         }
    771     }
    772 
    773     /**
    774      * Swaps the current adapter with the provided one. It is similar to
    775      * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
    776      * {@link ViewHolder} and does not clear the RecycledViewPool.
    777      * <p>
    778      * Note that it still calls onAdapterChanged callbacks.
    779      *
    780      * @param adapter The new adapter to set, or null to set no adapter.
    781      * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
    782      *                                      Views. If adapters have stable ids and/or you want to
    783      *                                      animate the disappearing views, you may prefer to set
    784      *                                      this to false.
    785      * @see #setAdapter(Adapter)
    786      */
    787     public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
    788         // bail out if layout is frozen
    789         setLayoutFrozen(false);
    790         setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
    791         setDataSetChangedAfterLayout();
    792         requestLayout();
    793     }
    794     /**
    795      * Set a new adapter to provide child views on demand.
    796      * <p>
    797      * When adapter is changed, all existing views are recycled back to the pool. If the pool has
    798      * only one adapter, it will be cleared.
    799      *
    800      * @param adapter The new adapter to set, or null to set no adapter.
    801      * @see #swapAdapter(Adapter, boolean)
    802      */
    803     public void setAdapter(Adapter adapter) {
    804         // bail out if layout is frozen
    805         setLayoutFrozen(false);
    806         setAdapterInternal(adapter, false, true);
    807         requestLayout();
    808     }
    809 
    810     /**
    811      * Replaces the current adapter with the new one and triggers listeners.
    812      * @param adapter The new adapter
    813      * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
    814      *                               item types with the current adapter (helps us avoid cache
    815      *                               invalidation).
    816      * @param removeAndRecycleViews  If true, we'll remove and recycle all existing views. If
    817      *                               compatibleWithPrevious is false, this parameter is ignored.
    818      */
    819     private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
    820             boolean removeAndRecycleViews) {
    821         if (mAdapter != null) {
    822             mAdapter.unregisterAdapterDataObserver(mObserver);
    823             mAdapter.onDetachedFromRecyclerView(this);
    824         }
    825         if (!compatibleWithPrevious || removeAndRecycleViews) {
    826             // end all running animations
    827             if (mItemAnimator != null) {
    828                 mItemAnimator.endAnimations();
    829             }
    830             // Since animations are ended, mLayout.children should be equal to
    831             // recyclerView.children. This may not be true if item animator's end does not work as
    832             // expected. (e.g. not release children instantly). It is safer to use mLayout's child
    833             // count.
    834             if (mLayout != null) {
    835                 mLayout.removeAndRecycleAllViews(mRecycler);
    836                 mLayout.removeAndRecycleScrapInt(mRecycler);
    837             }
    838             // we should clear it here before adapters are swapped to ensure correct callbacks.
    839             mRecycler.clear();
    840         }
    841         mAdapterHelper.reset();
    842         final Adapter oldAdapter = mAdapter;
    843         mAdapter = adapter;
    844         if (adapter != null) {
    845             adapter.registerAdapterDataObserver(mObserver);
    846             adapter.onAttachedToRecyclerView(this);
    847         }
    848         if (mLayout != null) {
    849             mLayout.onAdapterChanged(oldAdapter, mAdapter);
    850         }
    851         mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
    852         mState.mStructureChanged = true;
    853         markKnownViewsInvalid();
    854     }
    855 
    856     /**
    857      * Retrieves the previously set adapter or null if no adapter is set.
    858      *
    859      * @return The previously set adapter
    860      * @see #setAdapter(Adapter)
    861      */
    862     public Adapter getAdapter() {
    863         return mAdapter;
    864     }
    865 
    866     /**
    867      * Register a listener that will be notified whenever a child view is recycled.
    868      *
    869      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
    870      * that a child view is no longer needed. If an application associates expensive
    871      * or heavyweight data with item views, this may be a good place to release
    872      * or free those resources.</p>
    873      *
    874      * @param listener Listener to register, or null to clear
    875      */
    876     public void setRecyclerListener(RecyclerListener listener) {
    877         mRecyclerListener = listener;
    878     }
    879 
    880     /**
    881      * <p>Return the offset of the RecyclerView's text baseline from the its top
    882      * boundary. If the LayoutManager of this RecyclerView does not support baseline alignment,
    883      * this method returns -1.</p>
    884      *
    885      * @return the offset of the baseline within the RecyclerView's bounds or -1
    886      *         if baseline alignment is not supported
    887      */
    888     @Override
    889     public int getBaseline() {
    890         if (mLayout != null) {
    891             return mLayout.getBaseline();
    892         } else {
    893             return super.getBaseline();
    894         }
    895     }
    896 
    897     /**
    898      * Register a listener that will be notified whenever a child view is attached to or detached
    899      * from RecyclerView.
    900      *
    901      * <p>This listener will be called when a LayoutManager or the RecyclerView decides
    902      * that a child view is no longer needed. If an application associates expensive
    903      * or heavyweight data with item views, this may be a good place to release
    904      * or free those resources.</p>
    905      *
    906      * @param listener Listener to register
    907      */
    908     public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
    909         if (mOnChildAttachStateListeners == null) {
    910             mOnChildAttachStateListeners = new ArrayList<OnChildAttachStateChangeListener>();
    911         }
    912         mOnChildAttachStateListeners.add(listener);
    913     }
    914 
    915     /**
    916      * Removes the provided listener from child attached state listeners list.
    917      *
    918      * @param listener Listener to unregister
    919      */
    920     public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
    921         if (mOnChildAttachStateListeners == null) {
    922             return;
    923         }
    924         mOnChildAttachStateListeners.remove(listener);
    925     }
    926 
    927     /**
    928      * Removes all listeners that were added via
    929      * {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
    930      */
    931     public void clearOnChildAttachStateChangeListeners() {
    932         if (mOnChildAttachStateListeners != null) {
    933             mOnChildAttachStateListeners.clear();
    934         }
    935     }
    936 
    937     /**
    938      * Set the {@link LayoutManager} that this RecyclerView will use.
    939      *
    940      * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}
    941      * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
    942      * layout arrangements for child views. These arrangements are controlled by the
    943      * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>
    944      *
    945      * <p>Several default strategies are provided for common uses such as lists and grids.</p>
    946      *
    947      * @param layout LayoutManager to use
    948      */
    949     public void setLayoutManager(LayoutManager layout) {
    950         if (layout == mLayout) {
    951             return;
    952         }
    953         // TODO We should do this switch a dispachLayout pass and animate children. There is a good
    954         // chance that LayoutManagers will re-use views.
    955         if (mLayout != null) {
    956             if (mIsAttached) {
    957                 mLayout.dispatchDetachedFromWindow(this, mRecycler);
    958             }
    959             mLayout.setRecyclerView(null);
    960         }
    961         mRecycler.clear();
    962         mChildHelper.removeAllViewsUnfiltered();
    963         mLayout = layout;
    964         if (layout != null) {
    965             if (layout.mRecyclerView != null) {
    966                 throw new IllegalArgumentException("LayoutManager " + layout +
    967                         " is already attached to a RecyclerView: " + layout.mRecyclerView);
    968             }
    969             mLayout.setRecyclerView(this);
    970             if (mIsAttached) {
    971                 mLayout.dispatchAttachedToWindow(this);
    972             }
    973         }
    974         requestLayout();
    975     }
    976 
    977     @Override
    978     protected Parcelable onSaveInstanceState() {
    979         SavedState state = new SavedState(super.onSaveInstanceState());
    980         if (mPendingSavedState != null) {
    981             state.copyFrom(mPendingSavedState);
    982         } else if (mLayout != null) {
    983             state.mLayoutState = mLayout.onSaveInstanceState();
    984         } else {
    985             state.mLayoutState = null;
    986         }
    987 
    988         return state;
    989     }
    990 
    991     @Override
    992     protected void onRestoreInstanceState(Parcelable state) {
    993         mPendingSavedState = (SavedState) state;
    994         super.onRestoreInstanceState(mPendingSavedState.getSuperState());
    995         if (mLayout != null && mPendingSavedState.mLayoutState != null) {
    996             mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
    997         }
    998     }
    999 
   1000     /**
   1001      * Override to prevent freezing of any views created by the adapter.
   1002      */
   1003     @Override
   1004     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
   1005         dispatchFreezeSelfOnly(container);
   1006     }
   1007 
   1008     /**
   1009      * Override to prevent thawing of any views created by the adapter.
   1010      */
   1011     @Override
   1012     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
   1013         dispatchThawSelfOnly(container);
   1014     }
   1015 
   1016     /**
   1017      * Adds a view to the animatingViews list.
   1018      * mAnimatingViews holds the child views that are currently being kept around
   1019      * purely for the purpose of being animated out of view. They are drawn as a regular
   1020      * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
   1021      * as they are managed separately from the regular child views.
   1022      * @param viewHolder The ViewHolder to be removed
   1023      */
   1024     private void addAnimatingView(ViewHolder viewHolder) {
   1025         final View view = viewHolder.itemView;
   1026         final boolean alreadyParented = view.getParent() == this;
   1027         mRecycler.unscrapView(getChildViewHolder(view));
   1028         if (viewHolder.isTmpDetached()) {
   1029             // re-attach
   1030             mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
   1031         } else if(!alreadyParented) {
   1032             mChildHelper.addView(view, true);
   1033         } else {
   1034             mChildHelper.hide(view);
   1035         }
   1036     }
   1037 
   1038     /**
   1039      * Removes a view from the animatingViews list.
   1040      * @param view The view to be removed
   1041      * @see #addAnimatingView(RecyclerView.ViewHolder)
   1042      * @return true if an animating view is removed
   1043      */
   1044     private boolean removeAnimatingView(View view) {
   1045         eatRequestLayout();
   1046         final boolean removed = mChildHelper.removeViewIfHidden(view);
   1047         if (removed) {
   1048             final ViewHolder viewHolder = getChildViewHolderInt(view);
   1049             mRecycler.unscrapView(viewHolder);
   1050             mRecycler.recycleViewHolderInternal(viewHolder);
   1051             if (DEBUG) {
   1052                 Log.d(TAG, "after removing animated view: " + view + ", " + this);
   1053             }
   1054         }
   1055         resumeRequestLayout(false);
   1056         return removed;
   1057     }
   1058 
   1059     /**
   1060      * Return the {@link LayoutManager} currently responsible for
   1061      * layout policy for this RecyclerView.
   1062      *
   1063      * @return The currently bound LayoutManager
   1064      */
   1065     public LayoutManager getLayoutManager() {
   1066         return mLayout;
   1067     }
   1068 
   1069     /**
   1070      * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
   1071      * if no pool is set for this view a new one will be created. See
   1072      * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
   1073      *
   1074      * @return The pool used to store recycled item views for reuse.
   1075      * @see #setRecycledViewPool(RecycledViewPool)
   1076      */
   1077     public RecycledViewPool getRecycledViewPool() {
   1078         return mRecycler.getRecycledViewPool();
   1079     }
   1080 
   1081     /**
   1082      * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
   1083      * This can be useful if you have multiple RecyclerViews with adapters that use the same
   1084      * view types, for example if you have several data sets with the same kinds of item views
   1085      * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
   1086      *
   1087      * @param pool Pool to set. If this parameter is null a new pool will be created and used.
   1088      */
   1089     public void setRecycledViewPool(RecycledViewPool pool) {
   1090         mRecycler.setRecycledViewPool(pool);
   1091     }
   1092 
   1093     /**
   1094      * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
   1095      *
   1096      * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
   1097      *
   1098      * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
   1099      */
   1100     public void setViewCacheExtension(ViewCacheExtension extension) {
   1101         mRecycler.setViewCacheExtension(extension);
   1102     }
   1103 
   1104     /**
   1105      * Set the number of offscreen views to retain before adding them to the potentially shared
   1106      * {@link #getRecycledViewPool() recycled view pool}.
   1107      *
   1108      * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing
   1109      * a LayoutManager to reuse those views unmodified without needing to return to the adapter
   1110      * to rebind them.</p>
   1111      *
   1112      * @param size Number of views to cache offscreen before returning them to the general
   1113      *             recycled view pool
   1114      */
   1115     public void setItemViewCacheSize(int size) {
   1116         mRecycler.setViewCacheSize(size);
   1117     }
   1118 
   1119     /**
   1120      * Return the current scrolling state of the RecyclerView.
   1121      *
   1122      * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
   1123      * {@link #SCROLL_STATE_SETTLING}
   1124      */
   1125     public int getScrollState() {
   1126         return mScrollState;
   1127     }
   1128 
   1129     private void setScrollState(int state) {
   1130         if (state == mScrollState) {
   1131             return;
   1132         }
   1133         if (DEBUG) {
   1134             Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState,
   1135                     new Exception());
   1136         }
   1137         mScrollState = state;
   1138         if (state != SCROLL_STATE_SETTLING) {
   1139             stopScrollersInternal();
   1140         }
   1141         dispatchOnScrollStateChanged(state);
   1142     }
   1143 
   1144     /**
   1145      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1146      * affect both measurement and drawing of individual item views.
   1147      *
   1148      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1149      * be run/queried/drawn first for their effects on item views. Padding added to views
   1150      * will be nested; a padding added by an earlier decoration will mean further
   1151      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1152      * given area.</p>
   1153      *
   1154      * @param decor Decoration to add
   1155      * @param index Position in the decoration chain to insert this decoration at. If this value
   1156      *              is negative the decoration will be added at the end.
   1157      */
   1158     public void addItemDecoration(ItemDecoration decor, int index) {
   1159         if (mLayout != null) {
   1160             mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll  or"
   1161                     + " layout");
   1162         }
   1163         if (mItemDecorations.isEmpty()) {
   1164             setWillNotDraw(false);
   1165         }
   1166         if (index < 0) {
   1167             mItemDecorations.add(decor);
   1168         } else {
   1169             mItemDecorations.add(index, decor);
   1170         }
   1171         markItemDecorInsetsDirty();
   1172         requestLayout();
   1173     }
   1174 
   1175     /**
   1176      * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
   1177      * affect both measurement and drawing of individual item views.
   1178      *
   1179      * <p>Item decorations are ordered. Decorations placed earlier in the list will
   1180      * be run/queried/drawn first for their effects on item views. Padding added to views
   1181      * will be nested; a padding added by an earlier decoration will mean further
   1182      * item decorations in the list will be asked to draw/pad within the previous decoration's
   1183      * given area.</p>
   1184      *
   1185      * @param decor Decoration to add
   1186      */
   1187     public void addItemDecoration(ItemDecoration decor) {
   1188         addItemDecoration(decor, -1);
   1189     }
   1190 
   1191     /**
   1192      * Remove an {@link ItemDecoration} from this RecyclerView.
   1193      *
   1194      * <p>The given decoration will no longer impact the measurement and drawing of
   1195      * item views.</p>
   1196      *
   1197      * @param decor Decoration to remove
   1198      * @see #addItemDecoration(ItemDecoration)
   1199      */
   1200     public void removeItemDecoration(ItemDecoration decor) {
   1201         if (mLayout != null) {
   1202             mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll  or"
   1203                     + " layout");
   1204         }
   1205         mItemDecorations.remove(decor);
   1206         if (mItemDecorations.isEmpty()) {
   1207             setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
   1208         }
   1209         markItemDecorInsetsDirty();
   1210         requestLayout();
   1211     }
   1212 
   1213     /**
   1214      * Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
   1215      * <p>
   1216      * See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
   1217      * always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
   1218      * true if childDrawingOrderCallback is not null, false otherwise.
   1219      * <p>
   1220      * Note that child drawing order may be overridden by View's elevation.
   1221      *
   1222      * @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
   1223      *                                  system.
   1224      */
   1225     public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
   1226         if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
   1227             return;
   1228         }
   1229         mChildDrawingOrderCallback = childDrawingOrderCallback;
   1230         setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
   1231     }
   1232 
   1233     /**
   1234      * Set a listener that will be notified of any changes in scroll state or position.
   1235      *
   1236      * @param listener Listener to set or null to clear
   1237      *
   1238      * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
   1239      *             {@link #removeOnScrollListener(OnScrollListener)}
   1240      */
   1241     @Deprecated
   1242     public void setOnScrollListener(OnScrollListener listener) {
   1243         mScrollListener = listener;
   1244     }
   1245 
   1246     /**
   1247      * Add a listener that will be notified of any changes in scroll state or position.
   1248      *
   1249      * <p>Components that add a listener should take care to remove it when finished.
   1250      * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
   1251      * to remove all attached listeners.</p>
   1252      *
   1253      * @param listener listener to set or null to clear
   1254      */
   1255     public void addOnScrollListener(OnScrollListener listener) {
   1256         if (mScrollListeners == null) {
   1257             mScrollListeners = new ArrayList<OnScrollListener>();
   1258         }
   1259         mScrollListeners.add(listener);
   1260     }
   1261 
   1262     /**
   1263      * Remove a listener that was notified of any changes in scroll state or position.
   1264      *
   1265      * @param listener listener to set or null to clear
   1266      */
   1267     public void removeOnScrollListener(OnScrollListener listener) {
   1268         if (mScrollListeners != null) {
   1269             mScrollListeners.remove(listener);
   1270         }
   1271     }
   1272 
   1273     /**
   1274      * Remove all secondary listener that were notified of any changes in scroll state or position.
   1275      */
   1276     public void clearOnScrollListeners() {
   1277         if (mScrollListeners != null) {
   1278             mScrollListeners.clear();
   1279         }
   1280     }
   1281 
   1282     /**
   1283      * Convenience method to scroll to a certain position.
   1284      *
   1285      * RecyclerView does not implement scrolling logic, rather forwards the call to
   1286      * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}
   1287      * @param position Scroll to this adapter position
   1288      * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)
   1289      */
   1290     public void scrollToPosition(int position) {
   1291         if (mLayoutFrozen) {
   1292             return;
   1293         }
   1294         stopScroll();
   1295         if (mLayout == null) {
   1296             Log.e(TAG, "Cannot scroll to position a LayoutManager set. " +
   1297                     "Call setLayoutManager with a non-null argument.");
   1298             return;
   1299         }
   1300         mLayout.scrollToPosition(position);
   1301         awakenScrollBars();
   1302     }
   1303 
   1304     private void jumpToPositionForSmoothScroller(int position) {
   1305         if (mLayout == null) {
   1306             return;
   1307         }
   1308         mLayout.scrollToPosition(position);
   1309         awakenScrollBars();
   1310     }
   1311 
   1312     /**
   1313      * Starts a smooth scroll to an adapter position.
   1314      * <p>
   1315      * To support smooth scrolling, you must override
   1316      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
   1317      * {@link SmoothScroller}.
   1318      * <p>
   1319      * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
   1320      * provide a custom smooth scroll logic, override
   1321      * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
   1322      * LayoutManager.
   1323      *
   1324      * @param position The adapter position to scroll to
   1325      * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
   1326      */
   1327     public void smoothScrollToPosition(int position) {
   1328         if (mLayoutFrozen) {
   1329             return;
   1330         }
   1331         if (mLayout == null) {
   1332             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
   1333                     "Call setLayoutManager with a non-null argument.");
   1334             return;
   1335         }
   1336         mLayout.smoothScrollToPosition(this, mState, position);
   1337     }
   1338 
   1339     @Override
   1340     public void scrollTo(int x, int y) {
   1341         throw new UnsupportedOperationException(
   1342                 "RecyclerView does not support scrolling to an absolute position.");
   1343     }
   1344 
   1345     @Override
   1346     public void scrollBy(int x, int y) {
   1347         if (mLayout == null) {
   1348             Log.e(TAG, "Cannot scroll without a LayoutManager set. " +
   1349                     "Call setLayoutManager with a non-null argument.");
   1350             return;
   1351         }
   1352         if (mLayoutFrozen) {
   1353             return;
   1354         }
   1355         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   1356         final boolean canScrollVertical = mLayout.canScrollVertically();
   1357         if (canScrollHorizontal || canScrollVertical) {
   1358             scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
   1359         }
   1360     }
   1361 
   1362     /**
   1363      * Helper method reflect data changes to the state.
   1364      * <p>
   1365      * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
   1366      * but data actually changed.
   1367      * <p>
   1368      * This method consumes all deferred changes to avoid that case.
   1369      */
   1370     private void consumePendingUpdateOperations() {
   1371         mUpdateChildViewsRunnable.run();
   1372     }
   1373 
   1374     /**
   1375      * Does not perform bounds checking. Used by internal methods that have already validated input.
   1376      * <p>
   1377      * It also reports any unused scroll request to the related EdgeEffect.
   1378      *
   1379      * @param x The amount of horizontal scroll request
   1380      * @param y The amount of vertical scroll request
   1381      * @param ev The originating MotionEvent, or null if not from a touch event.
   1382      *
   1383      * @return Whether any scroll was consumed in either direction.
   1384      */
   1385     boolean scrollByInternal(int x, int y, MotionEvent ev) {
   1386         int unconsumedX = 0, unconsumedY = 0;
   1387         int consumedX = 0, consumedY = 0;
   1388 
   1389         consumePendingUpdateOperations();
   1390         if (mAdapter != null) {
   1391             eatRequestLayout();
   1392             onEnterLayoutOrScroll();
   1393             TraceCompat.beginSection(TRACE_SCROLL_TAG);
   1394             if (x != 0) {
   1395                 consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
   1396                 unconsumedX = x - consumedX;
   1397             }
   1398             if (y != 0) {
   1399                 consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
   1400                 unconsumedY = y - consumedY;
   1401             }
   1402             TraceCompat.endSection();
   1403             if (supportsChangeAnimations()) {
   1404                 // Fix up shadow views used by changing animations
   1405                 int count = mChildHelper.getChildCount();
   1406                 for (int i = 0; i < count; i++) {
   1407                     View view = mChildHelper.getChildAt(i);
   1408                     ViewHolder holder = getChildViewHolder(view);
   1409                     if (holder != null && holder.mShadowingHolder != null) {
   1410                         ViewHolder shadowingHolder = holder.mShadowingHolder;
   1411                         View shadowingView = shadowingHolder != null ? shadowingHolder.itemView : null;
   1412                         if (shadowingView != null) {
   1413                             int left = view.getLeft();
   1414                             int top = view.getTop();
   1415                             if (left != shadowingView.getLeft() || top != shadowingView.getTop()) {
   1416                                 shadowingView.layout(left, top,
   1417                                         left + shadowingView.getWidth(),
   1418                                         top + shadowingView.getHeight());
   1419                             }
   1420                         }
   1421                     }
   1422                 }
   1423             }
   1424             onExitLayoutOrScroll();
   1425             resumeRequestLayout(false);
   1426         }
   1427         if (!mItemDecorations.isEmpty()) {
   1428             invalidate();
   1429         }
   1430 
   1431         if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
   1432             // Update the last touch co-ords, taking any scroll offset into account
   1433             mLastTouchX -= mScrollOffset[0];
   1434             mLastTouchY -= mScrollOffset[1];
   1435             if (ev != null) {
   1436                 ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   1437             }
   1438             mNestedOffsets[0] += mScrollOffset[0];
   1439             mNestedOffsets[1] += mScrollOffset[1];
   1440         } else if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
   1441             if (ev != null) {
   1442                 pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
   1443             }
   1444             considerReleasingGlowsOnScroll(x, y);
   1445         }
   1446         if (consumedX != 0 || consumedY != 0) {
   1447             dispatchOnScrolled(consumedX, consumedY);
   1448         }
   1449         if (!awakenScrollBars()) {
   1450             invalidate();
   1451         }
   1452         return consumedX != 0 || consumedY != 0;
   1453     }
   1454 
   1455     /**
   1456      * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
   1457      * range. This value is used to compute the length of the thumb within the scrollbar's track.
   1458      * </p>
   1459      *
   1460      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1461      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>
   1462      *
   1463      * <p>Default implementation returns 0.</p>
   1464      *
   1465      * <p>If you want to support scroll bars, override
   1466      * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your
   1467      * LayoutManager. </p>
   1468      *
   1469      * @return The horizontal offset of the scrollbar's thumb
   1470      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset
   1471      * (RecyclerView.Adapter)
   1472      */
   1473     @Override
   1474     public int computeHorizontalScrollOffset() {
   1475         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState)
   1476                 : 0;
   1477     }
   1478 
   1479     /**
   1480      * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the
   1481      * horizontal range. This value is used to compute the length of the thumb within the
   1482      * scrollbar's track.</p>
   1483      *
   1484      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1485      * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>
   1486      *
   1487      * <p>Default implementation returns 0.</p>
   1488      *
   1489      * <p>If you want to support scroll bars, override
   1490      * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your
   1491      * LayoutManager.</p>
   1492      *
   1493      * @return The horizontal extent of the scrollbar's thumb
   1494      * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)
   1495      */
   1496     @Override
   1497     public int computeHorizontalScrollExtent() {
   1498         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
   1499     }
   1500 
   1501     /**
   1502      * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>
   1503      *
   1504      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1505      * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>
   1506      *
   1507      * <p>Default implementation returns 0.</p>
   1508      *
   1509      * <p>If you want to support scroll bars, override
   1510      * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your
   1511      * LayoutManager.</p>
   1512      *
   1513      * @return The total horizontal range represented by the vertical scrollbar
   1514      * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)
   1515      */
   1516     @Override
   1517     public int computeHorizontalScrollRange() {
   1518         return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
   1519     }
   1520 
   1521     /**
   1522      * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
   1523      * This value is used to compute the length of the thumb within the scrollbar's track. </p>
   1524      *
   1525      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1526      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>
   1527      *
   1528      * <p>Default implementation returns 0.</p>
   1529      *
   1530      * <p>If you want to support scroll bars, override
   1531      * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your
   1532      * LayoutManager.</p>
   1533      *
   1534      * @return The vertical offset of the scrollbar's thumb
   1535      * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset
   1536      * (RecyclerView.Adapter)
   1537      */
   1538     @Override
   1539     public int computeVerticalScrollOffset() {
   1540         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
   1541     }
   1542 
   1543     /**
   1544      * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
   1545      * This value is used to compute the length of the thumb within the scrollbar's track.</p>
   1546      *
   1547      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1548      * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>
   1549      *
   1550      * <p>Default implementation returns 0.</p>
   1551      *
   1552      * <p>If you want to support scroll bars, override
   1553      * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your
   1554      * LayoutManager.</p>
   1555      *
   1556      * @return The vertical extent of the scrollbar's thumb
   1557      * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)
   1558      */
   1559     @Override
   1560     public int computeVerticalScrollExtent() {
   1561         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
   1562     }
   1563 
   1564     /**
   1565      * <p>Compute the vertical range that the vertical scrollbar represents.</p>
   1566      *
   1567      * <p>The range is expressed in arbitrary units that must be the same as the units used by
   1568      * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>
   1569      *
   1570      * <p>Default implementation returns 0.</p>
   1571      *
   1572      * <p>If you want to support scroll bars, override
   1573      * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your
   1574      * LayoutManager.</p>
   1575      *
   1576      * @return The total vertical range represented by the vertical scrollbar
   1577      * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)
   1578      */
   1579     @Override
   1580     public int computeVerticalScrollRange() {
   1581         return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
   1582     }
   1583 
   1584 
   1585     void eatRequestLayout() {
   1586         if (!mEatRequestLayout) {
   1587             mEatRequestLayout = true;
   1588             if (!mLayoutFrozen) {
   1589                 mLayoutRequestEaten = false;
   1590             }
   1591         }
   1592     }
   1593 
   1594     void resumeRequestLayout(boolean performLayoutChildren) {
   1595         if (mEatRequestLayout) {
   1596             // when layout is frozen we should delay dispatchLayout()
   1597             if (performLayoutChildren && mLayoutRequestEaten && !mLayoutFrozen &&
   1598                     mLayout != null && mAdapter != null) {
   1599                 dispatchLayout();
   1600             }
   1601             mEatRequestLayout = false;
   1602             if (!mLayoutFrozen) {
   1603                 mLayoutRequestEaten = false;
   1604             }
   1605         }
   1606     }
   1607 
   1608     /**
   1609      * Enable or disable layout and scroll.  After <code>setLayoutFrozen(true)</code> is called,
   1610      * Layout requests will be postponed until <code>setLayoutFrozen(false)</code> is called;
   1611      * child views are not updated when RecyclerView is frozen, {@link #smoothScrollBy(int, int)},
   1612      * {@link #scrollBy(int, int)}, {@link #scrollToPosition(int)} and
   1613      * {@link #smoothScrollToPosition(int)} are dropped; TouchEvents and GenericMotionEvents are
   1614      * dropped; {@link LayoutManager#onFocusSearchFailed(View, int, Recycler, State)} will not be
   1615      * called.
   1616      *
   1617      * <p>
   1618      * <code>setLayoutFrozen(true)</code> does not prevent app from directly calling {@link
   1619      * LayoutManager#scrollToPosition(int)}, {@link LayoutManager#smoothScrollToPosition(
   1620      * RecyclerView, State, int)}.
   1621      * <p>
   1622      * {@link #setAdapter(Adapter)} and {@link #swapAdapter(Adapter, boolean)} will automatically
   1623      * stop frozen.
   1624      * <p>
   1625      * Note: Running ItemAnimator is not stopped automatically,  it's caller's
   1626      * responsibility to call ItemAnimator.end().
   1627      *
   1628      * @param frozen   true to freeze layout and scroll, false to re-enable.
   1629      */
   1630     public void setLayoutFrozen(boolean frozen) {
   1631         if (frozen != mLayoutFrozen) {
   1632             assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
   1633             if (!frozen) {
   1634                 mLayoutFrozen = frozen;
   1635                 if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
   1636                     requestLayout();
   1637                 }
   1638                 mLayoutRequestEaten = false;
   1639             } else {
   1640                 final long now = SystemClock.uptimeMillis();
   1641                 MotionEvent cancelEvent = MotionEvent.obtain(now, now,
   1642                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
   1643                 onTouchEvent(cancelEvent);
   1644                 mLayoutFrozen = frozen;
   1645                 mIgnoreMotionEventTillDown = true;
   1646                 stopScroll();
   1647             }
   1648         }
   1649     }
   1650 
   1651     /**
   1652      * Returns true if layout and scroll are frozen.
   1653      *
   1654      * @return true if layout and scroll are frozen
   1655      * @see #setLayoutFrozen(boolean)
   1656      */
   1657     public boolean isLayoutFrozen() {
   1658         return mLayoutFrozen;
   1659     }
   1660 
   1661     /**
   1662      * Animate a scroll by the given amount of pixels along either axis.
   1663      *
   1664      * @param dx Pixels to scroll horizontally
   1665      * @param dy Pixels to scroll vertically
   1666      */
   1667     public void smoothScrollBy(int dx, int dy) {
   1668         if (mLayout == null) {
   1669             Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
   1670                     "Call setLayoutManager with a non-null argument.");
   1671             return;
   1672         }
   1673         if (mLayoutFrozen) {
   1674             return;
   1675         }
   1676         if (!mLayout.canScrollHorizontally()) {
   1677             dx = 0;
   1678         }
   1679         if (!mLayout.canScrollVertically()) {
   1680             dy = 0;
   1681         }
   1682         if (dx != 0 || dy != 0) {
   1683             mViewFlinger.smoothScrollBy(dx, dy);
   1684         }
   1685     }
   1686 
   1687     /**
   1688      * Begin a standard fling with an initial velocity along each axis in pixels per second.
   1689      * If the velocity given is below the system-defined minimum this method will return false
   1690      * and no fling will occur.
   1691      *
   1692      * @param velocityX Initial horizontal velocity in pixels per second
   1693      * @param velocityY Initial vertical velocity in pixels per second
   1694      * @return true if the fling was started, false if the velocity was too low to fling or
   1695      * LayoutManager does not support scrolling in the axis fling is issued.
   1696      *
   1697      * @see LayoutManager#canScrollVertically()
   1698      * @see LayoutManager#canScrollHorizontally()
   1699      */
   1700     public boolean fling(int velocityX, int velocityY) {
   1701         if (mLayout == null) {
   1702             Log.e(TAG, "Cannot fling without a LayoutManager set. " +
   1703                     "Call setLayoutManager with a non-null argument.");
   1704             return false;
   1705         }
   1706         if (mLayoutFrozen) {
   1707             return false;
   1708         }
   1709 
   1710         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
   1711         final boolean canScrollVertical = mLayout.canScrollVertically();
   1712 
   1713         if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
   1714             velocityX = 0;
   1715         }
   1716         if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
   1717             velocityY = 0;
   1718         }
   1719         if (velocityX == 0 && velocityY == 0) {
   1720             // If we don't have any velocity, return false
   1721             return false;
   1722         }
   1723 
   1724         if (!dispatchNestedPreFling(velocityX, velocityY)) {
   1725             final boolean canScroll = canScrollHorizontal || canScrollVertical;
   1726             dispatchNestedFling(velocityX, velocityY, canScroll);
   1727 
   1728             if (canScroll) {
   1729                 velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
   1730                 velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
   1731                 mViewFlinger.fling(velocityX, velocityY);
   1732                 return true;
   1733             }
   1734         }
   1735         return false;
   1736     }
   1737 
   1738     /**
   1739      * Stop any current scroll in progress, such as one started by
   1740      * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
   1741      */
   1742     public void stopScroll() {
   1743         setScrollState(SCROLL_STATE_IDLE);
   1744         stopScrollersInternal();
   1745     }
   1746 
   1747     /**
   1748      * Similar to {@link #stopScroll()} but does not set the state.
   1749      */
   1750     private void stopScrollersInternal() {
   1751         mViewFlinger.stop();
   1752         if (mLayout != null) {
   1753             mLayout.stopSmoothScroller();
   1754         }
   1755     }
   1756 
   1757     /**
   1758      * Returns the minimum velocity to start a fling.
   1759      *
   1760      * @return The minimum velocity to start a fling
   1761      */
   1762     public int getMinFlingVelocity() {
   1763         return mMinFlingVelocity;
   1764     }
   1765 
   1766 
   1767     /**
   1768      * Returns the maximum fling velocity used by this RecyclerView.
   1769      *
   1770      * @return The maximum fling velocity used by this RecyclerView.
   1771      */
   1772     public int getMaxFlingVelocity() {
   1773         return mMaxFlingVelocity;
   1774     }
   1775 
   1776     /**
   1777      * Apply a pull to relevant overscroll glow effects
   1778      */
   1779     private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
   1780         boolean invalidate = false;
   1781         if (overscrollX < 0) {
   1782             ensureLeftGlow();
   1783             if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y  / getHeight())) {
   1784                 invalidate = true;
   1785             }
   1786         } else if (overscrollX > 0) {
   1787             ensureRightGlow();
   1788             if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
   1789                 invalidate = true;
   1790             }
   1791         }
   1792 
   1793         if (overscrollY < 0) {
   1794             ensureTopGlow();
   1795             if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
   1796                 invalidate = true;
   1797             }
   1798         } else if (overscrollY > 0) {
   1799             ensureBottomGlow();
   1800             if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
   1801                 invalidate = true;
   1802             }
   1803         }
   1804 
   1805         if (invalidate || overscrollX != 0 || overscrollY != 0) {
   1806             ViewCompat.postInvalidateOnAnimation(this);
   1807         }
   1808     }
   1809 
   1810     private void releaseGlows() {
   1811         boolean needsInvalidate = false;
   1812         if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
   1813         if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
   1814         if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
   1815         if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
   1816         if (needsInvalidate) {
   1817             ViewCompat.postInvalidateOnAnimation(this);
   1818         }
   1819     }
   1820 
   1821     private void considerReleasingGlowsOnScroll(int dx, int dy) {
   1822         boolean needsInvalidate = false;
   1823         if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
   1824             needsInvalidate = mLeftGlow.onRelease();
   1825         }
   1826         if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
   1827             needsInvalidate |= mRightGlow.onRelease();
   1828         }
   1829         if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
   1830             needsInvalidate |= mTopGlow.onRelease();
   1831         }
   1832         if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
   1833             needsInvalidate |= mBottomGlow.onRelease();
   1834         }
   1835         if (needsInvalidate) {
   1836             ViewCompat.postInvalidateOnAnimation(this);
   1837         }
   1838     }
   1839 
   1840     void absorbGlows(int velocityX, int velocityY) {
   1841         if (velocityX < 0) {
   1842             ensureLeftGlow();
   1843             mLeftGlow.onAbsorb(-velocityX);
   1844         } else if (velocityX > 0) {
   1845             ensureRightGlow();
   1846             mRightGlow.onAbsorb(velocityX);
   1847         }
   1848 
   1849         if (velocityY < 0) {
   1850             ensureTopGlow();
   1851             mTopGlow.onAbsorb(-velocityY);
   1852         } else if (velocityY > 0) {
   1853             ensureBottomGlow();
   1854             mBottomGlow.onAbsorb(velocityY);
   1855         }
   1856 
   1857         if (velocityX != 0 || velocityY != 0) {
   1858             ViewCompat.postInvalidateOnAnimation(this);
   1859         }
   1860     }
   1861 
   1862     void ensureLeftGlow() {
   1863         if (mLeftGlow != null) {
   1864             return;
   1865         }
   1866         mLeftGlow = new EdgeEffectCompat(getContext());
   1867         if (mClipToPadding) {
   1868             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   1869                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   1870         } else {
   1871             mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   1872         }
   1873     }
   1874 
   1875     void ensureRightGlow() {
   1876         if (mRightGlow != null) {
   1877             return;
   1878         }
   1879         mRightGlow = new EdgeEffectCompat(getContext());
   1880         if (mClipToPadding) {
   1881             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
   1882                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
   1883         } else {
   1884             mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
   1885         }
   1886     }
   1887 
   1888     void ensureTopGlow() {
   1889         if (mTopGlow != null) {
   1890             return;
   1891         }
   1892         mTopGlow = new EdgeEffectCompat(getContext());
   1893         if (mClipToPadding) {
   1894             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   1895                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   1896         } else {
   1897             mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   1898         }
   1899 
   1900     }
   1901 
   1902     void ensureBottomGlow() {
   1903         if (mBottomGlow != null) {
   1904             return;
   1905         }
   1906         mBottomGlow = new EdgeEffectCompat(getContext());
   1907         if (mClipToPadding) {
   1908             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
   1909                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
   1910         } else {
   1911             mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
   1912         }
   1913     }
   1914 
   1915     void invalidateGlows() {
   1916         mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
   1917     }
   1918 
   1919     // Focus handling
   1920 
   1921     @Override
   1922     public View focusSearch(View focused, int direction) {
   1923         View result = mLayout.onInterceptFocusSearch(focused, direction);
   1924         if (result != null) {
   1925             return result;
   1926         }
   1927         final FocusFinder ff = FocusFinder.getInstance();
   1928         result = ff.findNextFocus(this, focused, direction);
   1929         if (result == null && mAdapter != null && mLayout != null && !isComputingLayout()
   1930                 && !mLayoutFrozen) {
   1931             eatRequestLayout();
   1932             result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
   1933             resumeRequestLayout(false);
   1934         }
   1935         return result != null ? result : super.focusSearch(focused, direction);
   1936     }
   1937 
   1938     @Override
   1939     public void requestChildFocus(View child, View focused) {
   1940         if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
   1941             mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
   1942 
   1943             // get item decor offsets w/o refreshing. If they are invalid, there will be another
   1944             // layout pass to fix them, then it is LayoutManager's responsibility to keep focused
   1945             // View in viewport.
   1946             final ViewGroup.LayoutParams focusedLayoutParams = focused.getLayoutParams();
   1947             if (focusedLayoutParams instanceof LayoutParams) {
   1948                 // if focused child has item decors, use them. Otherwise, ignore.
   1949                 final LayoutParams lp = (LayoutParams) focusedLayoutParams;
   1950                 if (!lp.mInsetsDirty) {
   1951                     final Rect insets = lp.mDecorInsets;
   1952                     mTempRect.left -= insets.left;
   1953                     mTempRect.right += insets.right;
   1954                     mTempRect.top -= insets.top;
   1955                     mTempRect.bottom += insets.bottom;
   1956                 }
   1957             }
   1958 
   1959             offsetDescendantRectToMyCoords(focused, mTempRect);
   1960             offsetRectIntoDescendantCoords(child, mTempRect);
   1961             requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
   1962         }
   1963         super.requestChildFocus(child, focused);
   1964     }
   1965 
   1966     @Override
   1967     public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
   1968         return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
   1969     }
   1970 
   1971     @Override
   1972     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
   1973         if (mLayout == null || !mLayout.onAddFocusables(this, views, direction, focusableMode)) {
   1974             super.addFocusables(views, direction, focusableMode);
   1975         }
   1976     }
   1977 
   1978     @Override
   1979     protected void onAttachedToWindow() {
   1980         super.onAttachedToWindow();
   1981         mLayoutOrScrollCounter = 0;
   1982         mIsAttached = true;
   1983         mFirstLayoutComplete = false;
   1984         if (mLayout != null) {
   1985             mLayout.dispatchAttachedToWindow(this);
   1986         }
   1987         mPostedAnimatorRunner = false;
   1988     }
   1989 
   1990     @Override
   1991     protected void onDetachedFromWindow() {
   1992         super.onDetachedFromWindow();
   1993         if (mItemAnimator != null) {
   1994             mItemAnimator.endAnimations();
   1995         }
   1996         mFirstLayoutComplete = false;
   1997 
   1998         stopScroll();
   1999         mIsAttached = false;
   2000         if (mLayout != null) {
   2001             mLayout.dispatchDetachedFromWindow(this, mRecycler);
   2002         }
   2003         removeCallbacks(mItemAnimatorRunner);
   2004     }
   2005 
   2006     /**
   2007      * Returns true if RecyclerView is attached to window.
   2008      */
   2009     // @override
   2010     public boolean isAttachedToWindow() {
   2011         return mIsAttached;
   2012     }
   2013 
   2014     /**
   2015      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2016      * {@link IllegalStateException} if it <b>is not</b>.
   2017      *
   2018      * @param message The message for the exception. Can be null.
   2019      * @see #assertNotInLayoutOrScroll(String)
   2020      */
   2021     void assertInLayoutOrScroll(String message) {
   2022         if (!isComputingLayout()) {
   2023             if (message == null) {
   2024                 throw new IllegalStateException("Cannot call this method unless RecyclerView is "
   2025                         + "computing a layout or scrolling");
   2026             }
   2027             throw new IllegalStateException(message);
   2028 
   2029         }
   2030     }
   2031 
   2032     /**
   2033      * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   2034      * {@link IllegalStateException} if it <b>is</b>.
   2035      *
   2036      * @param message The message for the exception. Can be null.
   2037      * @see #assertInLayoutOrScroll(String)
   2038      */
   2039     void assertNotInLayoutOrScroll(String message) {
   2040         if (isComputingLayout()) {
   2041             if (message == null) {
   2042                 throw new IllegalStateException("Cannot call this method while RecyclerView is "
   2043                         + "computing a layout or scrolling");
   2044             }
   2045             throw new IllegalStateException(message);
   2046         }
   2047     }
   2048 
   2049     /**
   2050      * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
   2051      * to child views or this view's standard scrolling behavior.
   2052      *
   2053      * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
   2054      * returns true from
   2055      * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
   2056      * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
   2057      * for each incoming MotionEvent until the end of the gesture.</p>
   2058      *
   2059      * @param listener Listener to add
   2060      * @see SimpleOnItemTouchListener
   2061      */
   2062     public void addOnItemTouchListener(OnItemTouchListener listener) {
   2063         mOnItemTouchListeners.add(listener);
   2064     }
   2065 
   2066     /**
   2067      * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
   2068      *
   2069      * @param listener Listener to remove
   2070      */
   2071     public void removeOnItemTouchListener(OnItemTouchListener listener) {
   2072         mOnItemTouchListeners.remove(listener);
   2073         if (mActiveOnItemTouchListener == listener) {
   2074             mActiveOnItemTouchListener = null;
   2075         }
   2076     }
   2077 
   2078     private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
   2079         final int action = e.getAction();
   2080         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
   2081             mActiveOnItemTouchListener = null;
   2082         }
   2083 
   2084         final int listenerCount = mOnItemTouchListeners.size();
   2085         for (int i = 0; i < listenerCount; i++) {
   2086             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2087             if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
   2088                 mActiveOnItemTouchListener = listener;
   2089                 return true;
   2090             }
   2091         }
   2092         return false;
   2093     }
   2094 
   2095     private boolean dispatchOnItemTouch(MotionEvent e) {
   2096         final int action = e.getAction();
   2097         if (mActiveOnItemTouchListener != null) {
   2098             if (action == MotionEvent.ACTION_DOWN) {
   2099                 // Stale state from a previous gesture, we're starting a new one. Clear it.
   2100                 mActiveOnItemTouchListener = null;
   2101             } else {
   2102                 mActiveOnItemTouchListener.onTouchEvent(this, e);
   2103                 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
   2104                     // Clean up for the next gesture.
   2105                     mActiveOnItemTouchListener = null;
   2106                 }
   2107                 return true;
   2108             }
   2109         }
   2110 
   2111         // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
   2112         // as called from onInterceptTouchEvent; skip it.
   2113         if (action != MotionEvent.ACTION_DOWN) {
   2114             final int listenerCount = mOnItemTouchListeners.size();
   2115             for (int i = 0; i < listenerCount; i++) {
   2116                 final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2117                 if (listener.onInterceptTouchEvent(this, e)) {
   2118                     mActiveOnItemTouchListener = listener;
   2119                     return true;
   2120                 }
   2121             }
   2122         }
   2123         return false;
   2124     }
   2125 
   2126     @Override
   2127     public boolean onInterceptTouchEvent(MotionEvent e) {
   2128         if (mLayoutFrozen) {
   2129             // When layout is frozen,  RV does not intercept the motion event.
   2130             // A child view e.g. a button may still get the click.
   2131             return false;
   2132         }
   2133         if (dispatchOnItemTouchIntercept(e)) {
   2134             cancelTouch();
   2135             return true;
   2136         }
   2137 
   2138         if (mLayout == null) {
   2139             return false;
   2140         }
   2141 
   2142         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   2143         final boolean canScrollVertically = mLayout.canScrollVertically();
   2144 
   2145         if (mVelocityTracker == null) {
   2146             mVelocityTracker = VelocityTracker.obtain();
   2147         }
   2148         mVelocityTracker.addMovement(e);
   2149 
   2150         final int action = MotionEventCompat.getActionMasked(e);
   2151         final int actionIndex = MotionEventCompat.getActionIndex(e);
   2152 
   2153         switch (action) {
   2154             case MotionEvent.ACTION_DOWN:
   2155                 if (mIgnoreMotionEventTillDown) {
   2156                     mIgnoreMotionEventTillDown = false;
   2157                 }
   2158                 mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
   2159                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   2160                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   2161 
   2162                 if (mScrollState == SCROLL_STATE_SETTLING) {
   2163                     getParent().requestDisallowInterceptTouchEvent(true);
   2164                     setScrollState(SCROLL_STATE_DRAGGING);
   2165                 }
   2166 
   2167                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
   2168                 if (canScrollHorizontally) {
   2169                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
   2170                 }
   2171                 if (canScrollVertically) {
   2172                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
   2173                 }
   2174                 startNestedScroll(nestedScrollAxis);
   2175                 break;
   2176 
   2177             case MotionEventCompat.ACTION_POINTER_DOWN:
   2178                 mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
   2179                 mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
   2180                 mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
   2181                 break;
   2182 
   2183             case MotionEvent.ACTION_MOVE: {
   2184                 final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
   2185                 if (index < 0) {
   2186                     Log.e(TAG, "Error processing scroll; pointer index for id " +
   2187                             mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   2188                     return false;
   2189                 }
   2190 
   2191                 final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
   2192                 final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
   2193                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   2194                     final int dx = x - mInitialTouchX;
   2195                     final int dy = y - mInitialTouchY;
   2196                     boolean startScroll = false;
   2197                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   2198                         mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
   2199                         startScroll = true;
   2200                     }
   2201                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   2202                         mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
   2203                         startScroll = true;
   2204                     }
   2205                     if (startScroll) {
   2206                         final ViewParent parent = getParent();
   2207                         if (parent != null) {
   2208                             parent.requestDisallowInterceptTouchEvent(true);
   2209                         }
   2210                         setScrollState(SCROLL_STATE_DRAGGING);
   2211                     }
   2212                 }
   2213             } break;
   2214 
   2215             case MotionEventCompat.ACTION_POINTER_UP: {
   2216                 onPointerUp(e);
   2217             } break;
   2218 
   2219             case MotionEvent.ACTION_UP: {
   2220                 mVelocityTracker.clear();
   2221                 stopNestedScroll();
   2222             } break;
   2223 
   2224             case MotionEvent.ACTION_CANCEL: {
   2225                 cancelTouch();
   2226             }
   2227         }
   2228         return mScrollState == SCROLL_STATE_DRAGGING;
   2229     }
   2230 
   2231     @Override
   2232     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   2233         final int listenerCount = mOnItemTouchListeners.size();
   2234         for (int i = 0; i < listenerCount; i++) {
   2235             final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
   2236             listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
   2237         }
   2238         super.requestDisallowInterceptTouchEvent(disallowIntercept);
   2239     }
   2240 
   2241     @Override
   2242     public boolean onTouchEvent(MotionEvent e) {
   2243         if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
   2244             return false;
   2245         }
   2246         if (dispatchOnItemTouch(e)) {
   2247             cancelTouch();
   2248             return true;
   2249         }
   2250 
   2251         if (mLayout == null) {
   2252             return false;
   2253         }
   2254 
   2255         final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
   2256         final boolean canScrollVertically = mLayout.canScrollVertically();
   2257 
   2258         if (mVelocityTracker == null) {
   2259             mVelocityTracker = VelocityTracker.obtain();
   2260         }
   2261         boolean eventAddedToVelocityTracker = false;
   2262 
   2263         final MotionEvent vtev = MotionEvent.obtain(e);
   2264         final int action = MotionEventCompat.getActionMasked(e);
   2265         final int actionIndex = MotionEventCompat.getActionIndex(e);
   2266 
   2267         if (action == MotionEvent.ACTION_DOWN) {
   2268             mNestedOffsets[0] = mNestedOffsets[1] = 0;
   2269         }
   2270         vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
   2271 
   2272         switch (action) {
   2273             case MotionEvent.ACTION_DOWN: {
   2274                 mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
   2275                 mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
   2276                 mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
   2277 
   2278                 int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
   2279                 if (canScrollHorizontally) {
   2280                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
   2281                 }
   2282                 if (canScrollVertically) {
   2283                     nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
   2284                 }
   2285                 startNestedScroll(nestedScrollAxis);
   2286             } break;
   2287 
   2288             case MotionEventCompat.ACTION_POINTER_DOWN: {
   2289                 mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
   2290                 mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
   2291                 mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
   2292             } break;
   2293 
   2294             case MotionEvent.ACTION_MOVE: {
   2295                 final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
   2296                 if (index < 0) {
   2297                     Log.e(TAG, "Error processing scroll; pointer index for id " +
   2298                             mScrollPointerId + " not found. Did any MotionEvents get skipped?");
   2299                     return false;
   2300                 }
   2301 
   2302                 final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
   2303                 final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
   2304                 int dx = mLastTouchX - x;
   2305                 int dy = mLastTouchY - y;
   2306 
   2307                 if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
   2308                     dx -= mScrollConsumed[0];
   2309                     dy -= mScrollConsumed[1];
   2310                     vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
   2311                     // Updated the nested offsets
   2312                     mNestedOffsets[0] += mScrollOffset[0];
   2313                     mNestedOffsets[1] += mScrollOffset[1];
   2314                 }
   2315 
   2316                 if (mScrollState != SCROLL_STATE_DRAGGING) {
   2317                     boolean startScroll = false;
   2318                     if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
   2319                         if (dx > 0) {
   2320                             dx -= mTouchSlop;
   2321                         } else {
   2322                             dx += mTouchSlop;
   2323                         }
   2324                         startScroll = true;
   2325                     }
   2326                     if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
   2327                         if (dy > 0) {
   2328                             dy -= mTouchSlop;
   2329                         } else {
   2330                             dy += mTouchSlop;
   2331                         }
   2332                         startScroll = true;
   2333                     }
   2334                     if (startScroll) {
   2335                         final ViewParent parent = getParent();
   2336                         if (parent != null) {
   2337                             parent.requestDisallowInterceptTouchEvent(true);
   2338                         }
   2339                         setScrollState(SCROLL_STATE_DRAGGING);
   2340                     }
   2341                 }
   2342 
   2343                 if (mScrollState == SCROLL_STATE_DRAGGING) {
   2344                     mLastTouchX = x - mScrollOffset[0];
   2345                     mLastTouchY = y - mScrollOffset[1];
   2346 
   2347                     if (scrollByInternal(
   2348                             canScrollHorizontally ? dx : 0,
   2349                             canScrollVertically ? dy : 0,
   2350                             vtev)) {
   2351                         getParent().requestDisallowInterceptTouchEvent(true);
   2352                     }
   2353                 }
   2354             } break;
   2355 
   2356             case MotionEventCompat.ACTION_POINTER_UP: {
   2357                 onPointerUp(e);
   2358             } break;
   2359 
   2360             case MotionEvent.ACTION_UP: {
   2361                 mVelocityTracker.addMovement(vtev);
   2362                 eventAddedToVelocityTracker = true;
   2363                 mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
   2364                 final float xvel = canScrollHorizontally ?
   2365                         -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
   2366                 final float yvel = canScrollVertically ?
   2367                         -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
   2368                 if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
   2369                     setScrollState(SCROLL_STATE_IDLE);
   2370                 }
   2371                 resetTouch();
   2372             } break;
   2373 
   2374             case MotionEvent.ACTION_CANCEL: {
   2375                 cancelTouch();
   2376             } break;
   2377         }
   2378 
   2379         if (!eventAddedToVelocityTracker) {
   2380             mVelocityTracker.addMovement(vtev);
   2381         }
   2382         vtev.recycle();
   2383 
   2384         return true;
   2385     }
   2386 
   2387     private void resetTouch() {
   2388         if (mVelocityTracker != null) {
   2389             mVelocityTracker.clear();
   2390         }
   2391         stopNestedScroll();
   2392         releaseGlows();
   2393     }
   2394 
   2395     private void cancelTouch() {
   2396         resetTouch();
   2397         setScrollState(SCROLL_STATE_IDLE);
   2398     }
   2399 
   2400     private void onPointerUp(MotionEvent e) {
   2401         final int actionIndex = MotionEventCompat.getActionIndex(e);
   2402         if (MotionEventCompat.getPointerId(e, actionIndex) == mScrollPointerId) {
   2403             // Pick a new pointer to pick up the slack.
   2404             final int newIndex = actionIndex == 0 ? 1 : 0;
   2405             mScrollPointerId = MotionEventCompat.getPointerId(e, newIndex);
   2406             mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, newIndex) + 0.5f);
   2407             mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, newIndex) + 0.5f);
   2408         }
   2409     }
   2410 
   2411     // @Override
   2412     public boolean onGenericMotionEvent(MotionEvent event) {
   2413         if (mLayout == null) {
   2414             return false;
   2415         }
   2416         if (mLayoutFrozen) {
   2417             return false;
   2418         }
   2419         if ((MotionEventCompat.getSource(event) & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
   2420             if (event.getAction() == MotionEventCompat.ACTION_SCROLL) {
   2421                 final float vScroll, hScroll;
   2422                 if (mLayout.canScrollVertically()) {
   2423                     // Inverse the sign of the vertical scroll to align the scroll orientation
   2424                     // with AbsListView.
   2425                     vScroll = -MotionEventCompat
   2426                             .getAxisValue(event, MotionEventCompat.AXIS_VSCROLL);
   2427                 } else {
   2428                     vScroll = 0f;
   2429                 }
   2430                 if (mLayout.canScrollHorizontally()) {
   2431                     hScroll = MotionEventCompat
   2432                             .getAxisValue(event, MotionEventCompat.AXIS_HSCROLL);
   2433                 } else {
   2434                     hScroll = 0f;
   2435                 }
   2436 
   2437                 if (vScroll != 0 || hScroll != 0) {
   2438                     final float scrollFactor = getScrollFactor();
   2439                     scrollByInternal((int) (hScroll * scrollFactor),
   2440                             (int) (vScroll * scrollFactor), event);
   2441                 }
   2442             }
   2443         }
   2444         return false;
   2445     }
   2446 
   2447     /**
   2448      * Ported from View.getVerticalScrollFactor.
   2449      */
   2450     private float getScrollFactor() {
   2451         if (mScrollFactor == Float.MIN_VALUE) {
   2452             TypedValue outValue = new TypedValue();
   2453             if (getContext().getTheme().resolveAttribute(
   2454                     android.R.attr.listPreferredItemHeight, outValue, true)) {
   2455                 mScrollFactor = outValue.getDimension(
   2456                         getContext().getResources().getDisplayMetrics());
   2457             } else {
   2458                 return 0; //listPreferredItemHeight is not defined, no generic scrolling
   2459             }
   2460 
   2461         }
   2462         return mScrollFactor;
   2463     }
   2464 
   2465     @Override
   2466     protected void onMeasure(int widthSpec, int heightSpec) {
   2467         if (mAdapterUpdateDuringMeasure) {
   2468             eatRequestLayout();
   2469             processAdapterUpdatesAndSetAnimationFlags();
   2470 
   2471             if (mState.mRunPredictiveAnimations) {
   2472                 // TODO: try to provide a better approach.
   2473                 // When RV decides to run predictive animations, we need to measure in pre-layout
   2474                 // state so that pre-layout pass results in correct layout.
   2475                 // On the other hand, this will prevent the layout manager from resizing properly.
   2476                 mState.mInPreLayout = true;
   2477             } else {
   2478                 // consume remaining updates to provide a consistent state with the layout pass.
   2479                 mAdapterHelper.consumeUpdatesInOnePass();
   2480                 mState.mInPreLayout = false;
   2481             }
   2482             mAdapterUpdateDuringMeasure = false;
   2483             resumeRequestLayout(false);
   2484         }
   2485 
   2486         if (mAdapter != null) {
   2487             mState.mItemCount = mAdapter.getItemCount();
   2488         } else {
   2489             mState.mItemCount = 0;
   2490         }
   2491         if (mLayout == null) {
   2492             defaultOnMeasure(widthSpec, heightSpec);
   2493         } else {
   2494             mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
   2495         }
   2496 
   2497         mState.mInPreLayout = false; // clear
   2498     }
   2499 
   2500     /**
   2501      * Used when onMeasure is called before layout manager is set
   2502      */
   2503     private void defaultOnMeasure(int widthSpec, int heightSpec) {
   2504         final int widthMode = MeasureSpec.getMode(widthSpec);
   2505         final int heightMode = MeasureSpec.getMode(heightSpec);
   2506         final int widthSize = MeasureSpec.getSize(widthSpec);
   2507         final int heightSize = MeasureSpec.getSize(heightSpec);
   2508 
   2509         int width = 0;
   2510         int height = 0;
   2511 
   2512         switch (widthMode) {
   2513             case MeasureSpec.EXACTLY:
   2514             case MeasureSpec.AT_MOST:
   2515                 width = widthSize;
   2516                 break;
   2517             case MeasureSpec.UNSPECIFIED:
   2518             default:
   2519                 width = ViewCompat.getMinimumWidth(this);
   2520                 break;
   2521         }
   2522 
   2523         switch (heightMode) {
   2524             case MeasureSpec.EXACTLY:
   2525             case MeasureSpec.AT_MOST:
   2526                 height = heightSize;
   2527                 break;
   2528             case MeasureSpec.UNSPECIFIED:
   2529             default:
   2530                 height = ViewCompat.getMinimumHeight(this);
   2531                 break;
   2532         }
   2533 
   2534         setMeasuredDimension(width, height);
   2535     }
   2536 
   2537     @Override
   2538     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   2539         super.onSizeChanged(w, h, oldw, oldh);
   2540         if (w != oldw || h != oldh) {
   2541             invalidateGlows();
   2542         }
   2543     }
   2544 
   2545     /**
   2546      * Sets the {@link ItemAnimator} that will handle animations involving changes
   2547      * to the items in this RecyclerView. By default, RecyclerView instantiates and
   2548      * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
   2549      * enabled for the RecyclerView depends on the ItemAnimator and whether
   2550      * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
   2551      * supports item animations}.
   2552      *
   2553      * @param animator The ItemAnimator being set. If null, no animations will occur
   2554      * when changes occur to the items in this RecyclerView.
   2555      */
   2556     public void setItemAnimator(ItemAnimator animator) {
   2557         if (mItemAnimator != null) {
   2558             mItemAnimator.endAnimations();
   2559             mItemAnimator.setListener(null);
   2560         }
   2561         mItemAnimator = animator;
   2562         if (mItemAnimator != null) {
   2563             mItemAnimator.setListener(mItemAnimatorListener);
   2564         }
   2565     }
   2566 
   2567     private void onEnterLayoutOrScroll() {
   2568         mLayoutOrScrollCounter ++;
   2569     }
   2570 
   2571     private void onExitLayoutOrScroll() {
   2572         mLayoutOrScrollCounter --;
   2573         if (mLayoutOrScrollCounter < 1) {
   2574             if (DEBUG && mLayoutOrScrollCounter < 0) {
   2575                 throw new IllegalStateException("layout or scroll counter cannot go below zero."
   2576                         + "Some calls are not matching");
   2577             }
   2578             mLayoutOrScrollCounter = 0;
   2579             dispatchContentChangedIfNecessary();
   2580         }
   2581     }
   2582 
   2583     boolean isAccessibilityEnabled() {
   2584         return mAccessibilityManager != null && mAccessibilityManager.isEnabled();
   2585     }
   2586 
   2587     private void dispatchContentChangedIfNecessary() {
   2588         final int flags = mEatenAccessibilityChangeFlags;
   2589         mEatenAccessibilityChangeFlags = 0;
   2590         if (flags != 0 && isAccessibilityEnabled()) {
   2591             final AccessibilityEvent event = AccessibilityEvent.obtain();
   2592             event.setEventType(AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
   2593             AccessibilityEventCompat.setContentChangeTypes(event, flags);
   2594             sendAccessibilityEventUnchecked(event);
   2595         }
   2596     }
   2597 
   2598     /**
   2599      * Returns whether RecyclerView is currently computing a layout.
   2600      * <p>
   2601      * If this method returns true, it means that RecyclerView is in a lockdown state and any
   2602      * attempt to update adapter contents will result in an exception because adapter contents
   2603      * cannot be changed while RecyclerView is trying to compute the layout.
   2604      * <p>
   2605      * It is very unlikely that your code will be running during this state as it is
   2606      * called by the framework when a layout traversal happens or RecyclerView starts to scroll
   2607      * in response to system events (touch, accessibility etc).
   2608      * <p>
   2609      * This case may happen if you have some custom logic to change adapter contents in
   2610      * response to a View callback (e.g. focus change callback) which might be triggered during a
   2611      * layout calculation. In these cases, you should just postpone the change using a Handler or a
   2612      * similar mechanism.
   2613      *
   2614      * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
   2615      *         otherwise
   2616      */
   2617     public boolean isComputingLayout() {
   2618         return mLayoutOrScrollCounter > 0;
   2619     }
   2620 
   2621     /**
   2622      * Returns true if an accessibility event should not be dispatched now. This happens when an
   2623      * accessibility request arrives while RecyclerView does not have a stable state which is very
   2624      * hard to handle for a LayoutManager. Instead, this method records necessary information about
   2625      * the event and dispatches a window change event after the critical section is finished.
   2626      *
   2627      * @return True if the accessibility event should be postponed.
   2628      */
   2629     boolean shouldDeferAccessibilityEvent(AccessibilityEvent event) {
   2630         if (isComputingLayout()) {
   2631             int type = 0;
   2632             if (event != null) {
   2633                 type = AccessibilityEventCompat.getContentChangeTypes(event);
   2634             }
   2635             if (type == 0) {
   2636                 type = AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED;
   2637             }
   2638             mEatenAccessibilityChangeFlags |= type;
   2639             return true;
   2640         }
   2641         return false;
   2642     }
   2643 
   2644     @Override
   2645     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
   2646         if (shouldDeferAccessibilityEvent(event)) {
   2647             return;
   2648         }
   2649         super.sendAccessibilityEventUnchecked(event);
   2650     }
   2651 
   2652     /**
   2653      * Gets the current ItemAnimator for this RecyclerView. A null return value
   2654      * indicates that there is no animator and that item changes will happen without
   2655      * any animations. By default, RecyclerView instantiates and
   2656      * uses an instance of {@link DefaultItemAnimator}.
   2657      *
   2658      * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
   2659      * when changes occur to the items in this RecyclerView.
   2660      */
   2661     public ItemAnimator getItemAnimator() {
   2662         return mItemAnimator;
   2663     }
   2664 
   2665     private boolean supportsChangeAnimations() {
   2666         return mItemAnimator != null && mItemAnimator.getSupportsChangeAnimations();
   2667     }
   2668 
   2669     /**
   2670      * Post a runnable to the next frame to run pending item animations. Only the first such
   2671      * request will be posted, governed by the mPostedAnimatorRunner flag.
   2672      */
   2673     private void postAnimationRunner() {
   2674         if (!mPostedAnimatorRunner && mIsAttached) {
   2675             ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
   2676             mPostedAnimatorRunner = true;
   2677         }
   2678     }
   2679 
   2680     private boolean predictiveItemAnimationsEnabled() {
   2681         return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
   2682     }
   2683 
   2684     /**
   2685      * Consumes adapter updates and calculates which type of animations we want to run.
   2686      * Called in onMeasure and dispatchLayout.
   2687      * <p>
   2688      * This method may process only the pre-layout state of updates or all of them.
   2689      */
   2690     private void processAdapterUpdatesAndSetAnimationFlags() {
   2691         if (mDataSetHasChangedAfterLayout) {
   2692             // Processing these items have no value since data set changed unexpectedly.
   2693             // Instead, we just reset it.
   2694             mAdapterHelper.reset();
   2695             markKnownViewsInvalid();
   2696             mLayout.onItemsChanged(this);
   2697         }
   2698         // simple animations are a subset of advanced animations (which will cause a
   2699         // pre-layout step)
   2700         // If layout supports predictive animations, pre-process to decide if we want to run them
   2701         if (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations()) {
   2702             mAdapterHelper.preProcess();
   2703         } else {
   2704             mAdapterHelper.consumeUpdatesInOnePass();
   2705         }
   2706         boolean animationTypeSupported = (mItemsAddedOrRemoved && !mItemsChanged) ||
   2707                 (mItemsAddedOrRemoved || (mItemsChanged && supportsChangeAnimations()));
   2708         mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null &&
   2709                 (mDataSetHasChangedAfterLayout || animationTypeSupported ||
   2710                         mLayout.mRequestedSimpleAnimations) &&
   2711                 (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds());
   2712         mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations &&
   2713                 animationTypeSupported && !mDataSetHasChangedAfterLayout &&
   2714                 predictiveItemAnimationsEnabled();
   2715     }
   2716 
   2717     /**
   2718      * Wrapper around layoutChildren() that handles animating changes caused by layout.
   2719      * Animations work on the assumption that there are five different kinds of items
   2720      * in play:
   2721      * PERSISTENT: items are visible before and after layout
   2722      * REMOVED: items were visible before layout and were removed by the app
   2723      * ADDED: items did not exist before layout and were added by the app
   2724      * DISAPPEARING: items exist in the data set before/after, but changed from
   2725      * visible to non-visible in the process of layout (they were moved off
   2726      * screen as a side-effect of other changes)
   2727      * APPEARING: items exist in the data set before/after, but changed from
   2728      * non-visible to visible in the process of layout (they were moved on
   2729      * screen as a side-effect of other changes)
   2730      * The overall approach figures out what items exist before/after layout and
   2731      * infers one of the five above states for each of the items. Then the animations
   2732      * are set up accordingly:
   2733      * PERSISTENT views are moved ({@link ItemAnimator#animateMove(ViewHolder, int, int, int, int)})
   2734      * REMOVED views are removed ({@link ItemAnimator#animateRemove(ViewHolder)})
   2735      * ADDED views are added ({@link ItemAnimator#animateAdd(ViewHolder)})
   2736      * DISAPPEARING views are moved off screen
   2737      * APPEARING views are moved on screen
   2738      */
   2739     void dispatchLayout() {
   2740         if (mAdapter == null) {
   2741             Log.e(TAG, "No adapter attached; skipping layout");
   2742             return;
   2743         }
   2744         if (mLayout == null) {
   2745             Log.e(TAG, "No layout manager attached; skipping layout");
   2746             return;
   2747         }
   2748         mState.mDisappearingViewsInLayoutPass.clear();
   2749         eatRequestLayout();
   2750         onEnterLayoutOrScroll();
   2751 
   2752         processAdapterUpdatesAndSetAnimationFlags();
   2753 
   2754         mState.mOldChangedHolders = mState.mRunSimpleAnimations && mItemsChanged
   2755                 && supportsChangeAnimations() ? new ArrayMap<Long, ViewHolder>() : null;
   2756         mItemsAddedOrRemoved = mItemsChanged = false;
   2757         ArrayMap<View, Rect> appearingViewInitialBounds = null;
   2758         mState.mInPreLayout = mState.mRunPredictiveAnimations;
   2759         mState.mItemCount = mAdapter.getItemCount();
   2760         findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
   2761 
   2762         if (mState.mRunSimpleAnimations) {
   2763             // Step 0: Find out where all non-removed items are, pre-layout
   2764             mState.mPreLayoutHolderMap.clear();
   2765             mState.mPostLayoutHolderMap.clear();
   2766             int count = mChildHelper.getChildCount();
   2767             for (int i = 0; i < count; ++i) {
   2768                 final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   2769                 if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
   2770                     continue;
   2771                 }
   2772                 final View view = holder.itemView;
   2773                 mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder,
   2774                         view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
   2775             }
   2776         }
   2777         if (mState.mRunPredictiveAnimations) {
   2778             // Step 1: run prelayout: This will use the old positions of items. The layout manager
   2779             // is expected to layout everything, even removed items (though not to add removed
   2780             // items back to the container). This gives the pre-layout position of APPEARING views
   2781             // which come into existence as part of the real layout.
   2782 
   2783             // Save old positions so that LayoutManager can run its mapping logic.
   2784             saveOldPositions();
   2785             // processAdapterUpdatesAndSetAnimationFlags already run pre-layout animations.
   2786             if (mState.mOldChangedHolders != null) {
   2787                 int count = mChildHelper.getChildCount();
   2788                 for (int i = 0; i < count; ++i) {
   2789                     final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   2790                     if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) {
   2791                         long key = getChangedHolderKey(holder);
   2792                         mState.mOldChangedHolders.put(key, holder);
   2793                         mState.mPreLayoutHolderMap.remove(holder);
   2794                     }
   2795                 }
   2796             }
   2797 
   2798             final boolean didStructureChange = mState.mStructureChanged;
   2799             mState.mStructureChanged = false;
   2800             // temporarily disable flag because we are asking for previous layout
   2801             mLayout.onLayoutChildren(mRecycler, mState);
   2802             mState.mStructureChanged = didStructureChange;
   2803 
   2804             appearingViewInitialBounds = new ArrayMap<View, Rect>();
   2805             for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
   2806                 boolean found = false;
   2807                 View child = mChildHelper.getChildAt(i);
   2808                 if (getChildViewHolderInt(child).shouldIgnore()) {
   2809                     continue;
   2810                 }
   2811                 for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {
   2812                     ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);
   2813                     if (holder.itemView == child) {
   2814                         found = true;
   2815                         break;
   2816                     }
   2817                 }
   2818                 if (!found) {
   2819                     appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(),
   2820                             child.getRight(), child.getBottom()));
   2821                 }
   2822             }
   2823             // we don't process disappearing list because they may re-appear in post layout pass.
   2824             clearOldPositions();
   2825             mAdapterHelper.consumePostponedUpdates();
   2826         } else {
   2827             clearOldPositions();
   2828             // in case pre layout did run but we decided not to run predictive animations.
   2829             mAdapterHelper.consumeUpdatesInOnePass();
   2830             if (mState.mOldChangedHolders != null) {
   2831                 int count = mChildHelper.getChildCount();
   2832                 for (int i = 0; i < count; ++i) {
   2833                     final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   2834                     if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) {
   2835                         long key = getChangedHolderKey(holder);
   2836                         mState.mOldChangedHolders.put(key, holder);
   2837                         mState.mPreLayoutHolderMap.remove(holder);
   2838                     }
   2839                 }
   2840             }
   2841         }
   2842         mState.mItemCount = mAdapter.getItemCount();
   2843         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
   2844 
   2845         // Step 2: Run layout
   2846         mState.mInPreLayout = false;
   2847         mLayout.onLayoutChildren(mRecycler, mState);
   2848 
   2849         mState.mStructureChanged = false;
   2850         mPendingSavedState = null;
   2851 
   2852         // onLayoutChildren may have caused client code to disable item animations; re-check
   2853         mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
   2854 
   2855         if (mState.mRunSimpleAnimations) {
   2856             // Step 3: Find out where things are now, post-layout
   2857             ArrayMap<Long, ViewHolder> newChangedHolders = mState.mOldChangedHolders != null ?
   2858                     new ArrayMap<Long, ViewHolder>() : null;
   2859             int count = mChildHelper.getChildCount();
   2860             for (int i = 0; i < count; ++i) {
   2861                 ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   2862                 if (holder.shouldIgnore()) {
   2863                     continue;
   2864                 }
   2865                 final View view = holder.itemView;
   2866                 long key = getChangedHolderKey(holder);
   2867                 if (newChangedHolders != null && mState.mOldChangedHolders.get(key) != null) {
   2868                     newChangedHolders.put(key, holder);
   2869                 } else {
   2870                     mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder,
   2871                             view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
   2872                 }
   2873             }
   2874             processDisappearingList(appearingViewInitialBounds);
   2875             // Step 4: Animate DISAPPEARING and REMOVED items
   2876             int preLayoutCount = mState.mPreLayoutHolderMap.size();
   2877             for (int i = preLayoutCount - 1; i >= 0; i--) {
   2878                 ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);
   2879                 if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {
   2880                     ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i);
   2881                     mState.mPreLayoutHolderMap.removeAt(i);
   2882 
   2883                     View disappearingItemView = disappearingItem.holder.itemView;
   2884                     mRecycler.unscrapView(disappearingItem.holder);
   2885                     animateDisappearance(disappearingItem);
   2886                 }
   2887             }
   2888             // Step 5: Animate APPEARING and ADDED items
   2889             int postLayoutCount = mState.mPostLayoutHolderMap.size();
   2890             if (postLayoutCount > 0) {
   2891                 for (int i = postLayoutCount - 1; i >= 0; i--) {
   2892                     ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i);
   2893                     ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i);
   2894                     if ((mState.mPreLayoutHolderMap.isEmpty() ||
   2895                             !mState.mPreLayoutHolderMap.containsKey(itemHolder))) {
   2896                         mState.mPostLayoutHolderMap.removeAt(i);
   2897                         Rect initialBounds = (appearingViewInitialBounds != null) ?
   2898                                 appearingViewInitialBounds.get(itemHolder.itemView) : null;
   2899                         animateAppearance(itemHolder, initialBounds,
   2900                                 info.left, info.top);
   2901                     }
   2902                 }
   2903             }
   2904             // Step 6: Animate PERSISTENT items
   2905             count = mState.mPostLayoutHolderMap.size();
   2906             for (int i = 0; i < count; ++i) {
   2907                 ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);
   2908                 ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i);
   2909                 ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder);
   2910                 if (preInfo != null && postInfo != null) {
   2911                     if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
   2912                         postHolder.setIsRecyclable(false);
   2913                         if (DEBUG) {
   2914                             Log.d(TAG, "PERSISTENT: " + postHolder +
   2915                                     " with view " + postHolder.itemView);
   2916                         }
   2917                         if (mItemAnimator.animateMove(postHolder,
   2918                                 preInfo.left, preInfo.top, postInfo.left, postInfo.top)) {
   2919                             postAnimationRunner();
   2920                         }
   2921                     }
   2922                 }
   2923             }
   2924             // Step 7: Animate CHANGING items
   2925             count = mState.mOldChangedHolders != null ? mState.mOldChangedHolders.size() : 0;
   2926             // traverse reverse in case view gets recycled while we are traversing the list.
   2927             for (int i = count - 1; i >= 0; i--) {
   2928                 long key = mState.mOldChangedHolders.keyAt(i);
   2929                 ViewHolder oldHolder = mState.mOldChangedHolders.get(key);
   2930                 View oldView = oldHolder.itemView;
   2931                 if (oldHolder.shouldIgnore()) {
   2932                     continue;
   2933                 }
   2934                 // We probably don't need this check anymore since these views are removed from
   2935                 // the list if they are recycled.
   2936                 if (mRecycler.mChangedScrap != null &&
   2937                         mRecycler.mChangedScrap.contains(oldHolder)) {
   2938                     animateChange(oldHolder, newChangedHolders.get(key));
   2939                 } else if (DEBUG) {
   2940                     Log.e(TAG, "cannot find old changed holder in changed scrap :/" + oldHolder);
   2941                 }
   2942             }
   2943         }
   2944         resumeRequestLayout(false);
   2945         mLayout.removeAndRecycleScrapInt(mRecycler);
   2946         mState.mPreviousLayoutItemCount = mState.mItemCount;
   2947         mDataSetHasChangedAfterLayout = false;
   2948         mState.mRunSimpleAnimations = false;
   2949         mState.mRunPredictiveAnimations = false;
   2950         onExitLayoutOrScroll();
   2951         mLayout.mRequestedSimpleAnimations = false;
   2952         if (mRecycler.mChangedScrap != null) {
   2953             mRecycler.mChangedScrap.clear();
   2954         }
   2955         mState.mOldChangedHolders = null;
   2956 
   2957         if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
   2958             dispatchOnScrolled(0, 0);
   2959         }
   2960     }
   2961 
   2962     private void findMinMaxChildLayoutPositions(int[] into) {
   2963         final int count = mChildHelper.getChildCount();
   2964         if (count == 0) {
   2965             into[0] = 0;
   2966             into[1] = 0;
   2967             return;
   2968         }
   2969         int minPositionPreLayout = Integer.MAX_VALUE;
   2970         int maxPositionPreLayout = Integer.MIN_VALUE;
   2971         for (int i = 0; i < count; ++i) {
   2972             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   2973             if (holder.shouldIgnore()) {
   2974                 continue;
   2975             }
   2976             final int pos = holder.getLayoutPosition();
   2977             if (pos < minPositionPreLayout) {
   2978                 minPositionPreLayout = pos;
   2979             }
   2980             if (pos > maxPositionPreLayout) {
   2981                 maxPositionPreLayout = pos;
   2982             }
   2983         }
   2984         into[0] = minPositionPreLayout;
   2985         into[1] = maxPositionPreLayout;
   2986     }
   2987 
   2988     private boolean didChildRangeChange(int minPositionPreLayout, int maxPositionPreLayout) {
   2989         int count = mChildHelper.getChildCount();
   2990         if (count == 0) {
   2991             return minPositionPreLayout != 0 || maxPositionPreLayout != 0;
   2992         }
   2993         for (int i = 0; i < count; ++i) {
   2994             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   2995             if (holder.shouldIgnore()) {
   2996                 continue;
   2997             }
   2998             final int pos = holder.getLayoutPosition();
   2999             if (pos < minPositionPreLayout || pos > maxPositionPreLayout) {
   3000                 return true;
   3001             }
   3002         }
   3003         return false;
   3004     }
   3005 
   3006     @Override
   3007     protected void removeDetachedView(View child, boolean animate) {
   3008         ViewHolder vh = getChildViewHolderInt(child);
   3009         if (vh != null) {
   3010             if (vh.isTmpDetached()) {
   3011                 vh.clearTmpDetachFlag();
   3012             } else if (!vh.shouldIgnore()) {
   3013                 throw new IllegalArgumentException("Called removeDetachedView with a view which"
   3014                         + " is not flagged as tmp detached." + vh);
   3015             }
   3016         }
   3017         dispatchChildDetached(child);
   3018         super.removeDetachedView(child, animate);
   3019     }
   3020 
   3021     /**
   3022      * Returns a unique key to be used while handling change animations.
   3023      * It might be child's position or stable id depending on the adapter type.
   3024      */
   3025     long getChangedHolderKey(ViewHolder holder) {
   3026         return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
   3027     }
   3028 
   3029     /**
   3030      * A LayoutManager may want to layout a view just to animate disappearance.
   3031      * This method handles those views and triggers remove animation on them.
   3032      */
   3033     private void processDisappearingList(ArrayMap<View, Rect> appearingViews) {
   3034         final List<View> disappearingList = mState.mDisappearingViewsInLayoutPass;
   3035         for (int i = disappearingList.size() - 1; i >= 0; i --) {
   3036             View view = disappearingList.get(i);
   3037             ViewHolder vh = getChildViewHolderInt(view);
   3038             final ItemHolderInfo info = mState.mPreLayoutHolderMap.remove(vh);
   3039             if (!mState.isPreLayout()) {
   3040                 mState.mPostLayoutHolderMap.remove(vh);
   3041             }
   3042             if (appearingViews.remove(view) != null) {
   3043                 mLayout.removeAndRecycleView(view, mRecycler);
   3044                 continue;
   3045             }
   3046             if (info != null) {
   3047                 animateDisappearance(info);
   3048             } else {
   3049                 // let it disappear from the position it becomes visible
   3050                 animateDisappearance(new ItemHolderInfo(vh, view.getLeft(), view.getTop(),
   3051                         view.getRight(), view.getBottom()));
   3052             }
   3053         }
   3054         disappearingList.clear();
   3055     }
   3056 
   3057     private void animateAppearance(ViewHolder itemHolder, Rect beforeBounds, int afterLeft,
   3058             int afterTop) {
   3059         View newItemView = itemHolder.itemView;
   3060         if (beforeBounds != null &&
   3061                 (beforeBounds.left != afterLeft || beforeBounds.top != afterTop)) {
   3062             // slide items in if before/after locations differ
   3063             itemHolder.setIsRecyclable(false);
   3064             if (DEBUG) {
   3065                 Log.d(TAG, "APPEARING: " + itemHolder + " with view " + newItemView);
   3066             }
   3067             if (mItemAnimator.animateMove(itemHolder,
   3068                     beforeBounds.left, beforeBounds.top,
   3069                     afterLeft, afterTop)) {
   3070                 postAnimationRunner();
   3071             }
   3072         } else {
   3073             if (DEBUG) {
   3074                 Log.d(TAG, "ADDED: " + itemHolder + " with view " + newItemView);
   3075             }
   3076             itemHolder.setIsRecyclable(false);
   3077             if (mItemAnimator.animateAdd(itemHolder)) {
   3078                 postAnimationRunner();
   3079             }
   3080         }
   3081     }
   3082 
   3083     private void animateDisappearance(ItemHolderInfo disappearingItem) {
   3084         View disappearingItemView = disappearingItem.holder.itemView;
   3085         addAnimatingView(disappearingItem.holder);
   3086         int oldLeft = disappearingItem.left;
   3087         int oldTop = disappearingItem.top;
   3088         int newLeft = disappearingItemView.getLeft();
   3089         int newTop = disappearingItemView.getTop();
   3090         if (!disappearingItem.holder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
   3091             disappearingItem.holder.setIsRecyclable(false);
   3092             disappearingItemView.layout(newLeft, newTop,
   3093                     newLeft + disappearingItemView.getWidth(),
   3094                     newTop + disappearingItemView.getHeight());
   3095             if (DEBUG) {
   3096                 Log.d(TAG, "DISAPPEARING: " + disappearingItem.holder +
   3097                         " with view " + disappearingItemView);
   3098             }
   3099             if (mItemAnimator.animateMove(disappearingItem.holder, oldLeft, oldTop,
   3100                     newLeft, newTop)) {
   3101                 postAnimationRunner();
   3102             }
   3103         } else {
   3104             if (DEBUG) {
   3105                 Log.d(TAG, "REMOVED: " + disappearingItem.holder +
   3106                         " with view " + disappearingItemView);
   3107             }
   3108             disappearingItem.holder.setIsRecyclable(false);
   3109             if (mItemAnimator.animateRemove(disappearingItem.holder)) {
   3110                 postAnimationRunner();
   3111             }
   3112         }
   3113     }
   3114 
   3115     private void animateChange(ViewHolder oldHolder, ViewHolder newHolder) {
   3116         oldHolder.setIsRecyclable(false);
   3117         addAnimatingView(oldHolder);
   3118         oldHolder.mShadowedHolder = newHolder;
   3119         mRecycler.unscrapView(oldHolder);
   3120         if (DEBUG) {
   3121             Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
   3122         }
   3123         final int fromLeft = oldHolder.itemView.getLeft();
   3124         final int fromTop = oldHolder.itemView.getTop();
   3125         final int toLeft, toTop;
   3126         if (newHolder == null || newHolder.shouldIgnore()) {
   3127             toLeft = fromLeft;
   3128             toTop = fromTop;
   3129         } else {
   3130             toLeft = newHolder.itemView.getLeft();
   3131             toTop = newHolder.itemView.getTop();
   3132             newHolder.setIsRecyclable(false);
   3133             newHolder.mShadowingHolder = oldHolder;
   3134         }
   3135         if(mItemAnimator.animateChange(oldHolder, newHolder,
   3136                 fromLeft, fromTop, toLeft, toTop)) {
   3137             postAnimationRunner();
   3138         }
   3139     }
   3140 
   3141     @Override
   3142     protected void onLayout(boolean changed, int l, int t, int r, int b) {
   3143         eatRequestLayout();
   3144         TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
   3145         dispatchLayout();
   3146         TraceCompat.endSection();
   3147         resumeRequestLayout(false);
   3148         mFirstLayoutComplete = true;
   3149     }
   3150 
   3151     @Override
   3152     public void requestLayout() {
   3153         if (!mEatRequestLayout && !mLayoutFrozen) {
   3154             super.requestLayout();
   3155         } else {
   3156             mLayoutRequestEaten = true;
   3157         }
   3158     }
   3159 
   3160     void markItemDecorInsetsDirty() {
   3161         final int childCount = mChildHelper.getUnfilteredChildCount();
   3162         for (int i = 0; i < childCount; i++) {
   3163             final View child = mChildHelper.getUnfilteredChildAt(i);
   3164             ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   3165         }
   3166         mRecycler.markItemDecorInsetsDirty();
   3167     }
   3168 
   3169     @Override
   3170     public void draw(Canvas c) {
   3171         super.draw(c);
   3172 
   3173         final int count = mItemDecorations.size();
   3174         for (int i = 0; i < count; i++) {
   3175             mItemDecorations.get(i).onDrawOver(c, this, mState);
   3176         }
   3177         // TODO If padding is not 0 and chilChildrenToPadding is false, to draw glows properly, we
   3178         // need find children closest to edges. Not sure if it is worth the effort.
   3179         boolean needsInvalidate = false;
   3180         if (mLeftGlow != null && !mLeftGlow.isFinished()) {
   3181             final int restore = c.save();
   3182             final int padding = mClipToPadding ? getPaddingBottom() : 0;
   3183             c.rotate(270);
   3184             c.translate(-getHeight() + padding, 0);
   3185             needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
   3186             c.restoreToCount(restore);
   3187         }
   3188         if (mTopGlow != null && !mTopGlow.isFinished()) {
   3189             final int restore = c.save();
   3190             if (mClipToPadding) {
   3191                 c.translate(getPaddingLeft(), getPaddingTop());
   3192             }
   3193             needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
   3194             c.restoreToCount(restore);
   3195         }
   3196         if (mRightGlow != null && !mRightGlow.isFinished()) {
   3197             final int restore = c.save();
   3198             final int width = getWidth();
   3199             final int padding = mClipToPadding ? getPaddingTop() : 0;
   3200             c.rotate(90);
   3201             c.translate(-padding, -width);
   3202             needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
   3203             c.restoreToCount(restore);
   3204         }
   3205         if (mBottomGlow != null && !mBottomGlow.isFinished()) {
   3206             final int restore = c.save();
   3207             c.rotate(180);
   3208             if (mClipToPadding) {
   3209                 c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
   3210             } else {
   3211                 c.translate(-getWidth(), -getHeight());
   3212             }
   3213             needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
   3214             c.restoreToCount(restore);
   3215         }
   3216 
   3217         // If some views are animating, ItemDecorators are likely to move/change with them.
   3218         // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
   3219         // display lists are not invalidated.
   3220         if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
   3221                 mItemAnimator.isRunning()) {
   3222             needsInvalidate = true;
   3223         }
   3224 
   3225         if (needsInvalidate) {
   3226             ViewCompat.postInvalidateOnAnimation(this);
   3227         }
   3228     }
   3229 
   3230     @Override
   3231     public void onDraw(Canvas c) {
   3232         super.onDraw(c);
   3233 
   3234         final int count = mItemDecorations.size();
   3235         for (int i = 0; i < count; i++) {
   3236             mItemDecorations.get(i).onDraw(c, this, mState);
   3237         }
   3238     }
   3239 
   3240     @Override
   3241     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
   3242         return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
   3243     }
   3244 
   3245     @Override
   3246     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
   3247         if (mLayout == null) {
   3248             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3249         }
   3250         return mLayout.generateDefaultLayoutParams();
   3251     }
   3252 
   3253     @Override
   3254     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
   3255         if (mLayout == null) {
   3256             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3257         }
   3258         return mLayout.generateLayoutParams(getContext(), attrs);
   3259     }
   3260 
   3261     @Override
   3262     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
   3263         if (mLayout == null) {
   3264             throw new IllegalStateException("RecyclerView has no LayoutManager");
   3265         }
   3266         return mLayout.generateLayoutParams(p);
   3267     }
   3268 
   3269     /**
   3270      * Returns true if RecyclerView is currently running some animations.
   3271      * <p>
   3272      * If you want to be notified when animations are finished, use
   3273      * {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
   3274      *
   3275      * @return True if there are some item animations currently running or waiting to be started.
   3276      */
   3277     public boolean isAnimating() {
   3278         return mItemAnimator != null && mItemAnimator.isRunning();
   3279     }
   3280 
   3281     void saveOldPositions() {
   3282         final int childCount = mChildHelper.getUnfilteredChildCount();
   3283         for (int i = 0; i < childCount; i++) {
   3284             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3285             if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
   3286                 throw new IllegalStateException("view holder cannot have position -1 unless it"
   3287                         + " is removed");
   3288             }
   3289             if (!holder.shouldIgnore()) {
   3290                 holder.saveOldPosition();
   3291             }
   3292         }
   3293     }
   3294 
   3295     void clearOldPositions() {
   3296         final int childCount = mChildHelper.getUnfilteredChildCount();
   3297         for (int i = 0; i < childCount; i++) {
   3298             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3299             if (!holder.shouldIgnore()) {
   3300                 holder.clearOldPosition();
   3301             }
   3302         }
   3303         mRecycler.clearOldPositions();
   3304     }
   3305 
   3306     void offsetPositionRecordsForMove(int from, int to) {
   3307         final int childCount = mChildHelper.getUnfilteredChildCount();
   3308         final int start, end, inBetweenOffset;
   3309         if (from < to) {
   3310             start = from;
   3311             end = to;
   3312             inBetweenOffset = -1;
   3313         } else {
   3314             start = to;
   3315             end = from;
   3316             inBetweenOffset = 1;
   3317         }
   3318 
   3319         for (int i = 0; i < childCount; i++) {
   3320             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3321             if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   3322                 continue;
   3323             }
   3324             if (DEBUG) {
   3325                 Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " +
   3326                         holder);
   3327             }
   3328             if (holder.mPosition == from) {
   3329                 holder.offsetPosition(to - from, false);
   3330             } else {
   3331                 holder.offsetPosition(inBetweenOffset, false);
   3332             }
   3333 
   3334             mState.mStructureChanged = true;
   3335         }
   3336         mRecycler.offsetPositionRecordsForMove(from, to);
   3337         requestLayout();
   3338     }
   3339 
   3340     void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
   3341         final int childCount = mChildHelper.getUnfilteredChildCount();
   3342         for (int i = 0; i < childCount; i++) {
   3343             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3344             if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
   3345                 if (DEBUG) {
   3346                     Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +
   3347                             holder + " now at position " + (holder.mPosition + itemCount));
   3348                 }
   3349                 holder.offsetPosition(itemCount, false);
   3350                 mState.mStructureChanged = true;
   3351             }
   3352         }
   3353         mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
   3354         requestLayout();
   3355     }
   3356 
   3357     void offsetPositionRecordsForRemove(int positionStart, int itemCount,
   3358             boolean applyToPreLayout) {
   3359         final int positionEnd = positionStart + itemCount;
   3360         final int childCount = mChildHelper.getUnfilteredChildCount();
   3361         for (int i = 0; i < childCount; i++) {
   3362             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3363             if (holder != null && !holder.shouldIgnore()) {
   3364                 if (holder.mPosition >= positionEnd) {
   3365                     if (DEBUG) {
   3366                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
   3367                                 " holder " + holder + " now at position " +
   3368                                 (holder.mPosition - itemCount));
   3369                     }
   3370                     holder.offsetPosition(-itemCount, applyToPreLayout);
   3371                     mState.mStructureChanged = true;
   3372                 } else if (holder.mPosition >= positionStart) {
   3373                     if (DEBUG) {
   3374                         Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
   3375                                 " holder " + holder + " now REMOVED");
   3376                     }
   3377                     holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
   3378                             applyToPreLayout);
   3379                     mState.mStructureChanged = true;
   3380                 }
   3381             }
   3382         }
   3383         mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
   3384         requestLayout();
   3385     }
   3386 
   3387     /**
   3388      * Rebind existing views for the given range, or create as needed.
   3389      *
   3390      * @param positionStart Adapter position to start at
   3391      * @param itemCount Number of views that must explicitly be rebound
   3392      */
   3393     void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
   3394         final int childCount = mChildHelper.getUnfilteredChildCount();
   3395         final int positionEnd = positionStart + itemCount;
   3396 
   3397         for (int i = 0; i < childCount; i++) {
   3398             final View child = mChildHelper.getUnfilteredChildAt(i);
   3399             final ViewHolder holder = getChildViewHolderInt(child);
   3400             if (holder == null || holder.shouldIgnore()) {
   3401                 continue;
   3402             }
   3403             if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
   3404                 // We re-bind these view holders after pre-processing is complete so that
   3405                 // ViewHolders have their final positions assigned.
   3406                 holder.addFlags(ViewHolder.FLAG_UPDATE);
   3407                 holder.addChangePayload(payload);
   3408                 if (supportsChangeAnimations()) {
   3409                     holder.addFlags(ViewHolder.FLAG_CHANGED);
   3410                 }
   3411                 // lp cannot be null since we get ViewHolder from it.
   3412                 ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
   3413             }
   3414         }
   3415         mRecycler.viewRangeUpdate(positionStart, itemCount);
   3416     }
   3417 
   3418     void rebindUpdatedViewHolders() {
   3419         final int childCount = mChildHelper.getChildCount();
   3420         for (int i = 0; i < childCount; i++) {
   3421             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
   3422             // validate type is correct
   3423             if (holder == null || holder.shouldIgnore()) {
   3424                 continue;
   3425             }
   3426             if (holder.isRemoved() || holder.isInvalid()) {
   3427                 requestLayout();
   3428             } else if (holder.needsUpdate()) {
   3429                 final int type = mAdapter.getItemViewType(holder.mPosition);
   3430                 if (holder.getItemViewType() == type) {
   3431                     // Binding an attached view will request a layout if needed.
   3432                     if (!holder.isChanged() || !supportsChangeAnimations()) {
   3433                         mAdapter.bindViewHolder(holder, holder.mPosition);
   3434                     } else {
   3435                         // Don't rebind changed holders if change animations are enabled.
   3436                         // We want the old contents for the animation and will get a new
   3437                         // holder for the new contents.
   3438                         requestLayout();
   3439                     }
   3440                 } else {
   3441                     // binding to a new view will need re-layout anyways. We can as well trigger
   3442                     // it here so that it happens during layout
   3443                     requestLayout();
   3444                     break;
   3445                 }
   3446             }
   3447         }
   3448     }
   3449 
   3450     private void setDataSetChangedAfterLayout() {
   3451         if (mDataSetHasChangedAfterLayout) {
   3452             return;
   3453         }
   3454         mDataSetHasChangedAfterLayout = true;
   3455         final int childCount = mChildHelper.getUnfilteredChildCount();
   3456         for (int i = 0; i < childCount; i++) {
   3457             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3458             if (holder != null && !holder.shouldIgnore()) {
   3459                 holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   3460             }
   3461         }
   3462         mRecycler.setAdapterPositionsAsUnknown();
   3463     }
   3464 
   3465     /**
   3466      * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
   3467      * data change event.
   3468      */
   3469     void markKnownViewsInvalid() {
   3470         final int childCount = mChildHelper.getUnfilteredChildCount();
   3471         for (int i = 0; i < childCount; i++) {
   3472             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3473             if (holder != null && !holder.shouldIgnore()) {
   3474                 holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   3475             }
   3476         }
   3477         markItemDecorInsetsDirty();
   3478         mRecycler.markKnownViewsInvalid();
   3479     }
   3480 
   3481     /**
   3482      * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
   3483      * will trigger a {@link #requestLayout()} call.
   3484      */
   3485     public void invalidateItemDecorations() {
   3486         if (mItemDecorations.size() == 0) {
   3487             return;
   3488         }
   3489         if (mLayout != null) {
   3490             mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
   3491                     + " or layout");
   3492         }
   3493         markItemDecorInsetsDirty();
   3494         requestLayout();
   3495     }
   3496 
   3497     /**
   3498      * Retrieve the {@link ViewHolder} for the given child view.
   3499      *
   3500      * @param child Child of this RecyclerView to query for its ViewHolder
   3501      * @return The child view's ViewHolder
   3502      */
   3503     public ViewHolder getChildViewHolder(View child) {
   3504         final ViewParent parent = child.getParent();
   3505         if (parent != null && parent != this) {
   3506             throw new IllegalArgumentException("View " + child + " is not a direct child of " +
   3507                     this);
   3508         }
   3509         return getChildViewHolderInt(child);
   3510     }
   3511 
   3512     static ViewHolder getChildViewHolderInt(View child) {
   3513         if (child == null) {
   3514             return null;
   3515         }
   3516         return ((LayoutParams) child.getLayoutParams()).mViewHolder;
   3517     }
   3518 
   3519     /**
   3520      * @deprecated use {@link #getChildAdapterPosition(View)} or
   3521      * {@link #getChildLayoutPosition(View)}.
   3522      */
   3523     @Deprecated
   3524     public int getChildPosition(View child) {
   3525         return getChildAdapterPosition(child);
   3526     }
   3527 
   3528     /**
   3529      * Return the adapter position that the given child view corresponds to.
   3530      *
   3531      * @param child Child View to query
   3532      * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
   3533      */
   3534     public int getChildAdapterPosition(View child) {
   3535         final ViewHolder holder = getChildViewHolderInt(child);
   3536         return holder != null ? holder.getAdapterPosition() : NO_POSITION;
   3537     }
   3538 
   3539     /**
   3540      * Return the adapter position of the given child view as of the latest completed layout pass.
   3541      * <p>
   3542      * This position may not be equal to Item's adapter position if there are pending changes
   3543      * in the adapter which have not been reflected to the layout yet.
   3544      *
   3545      * @param child Child View to query
   3546      * @return Adapter position of the given View as of last layout pass or {@link #NO_POSITION} if
   3547      * the View is representing a removed item.
   3548      */
   3549     public int getChildLayoutPosition(View child) {
   3550         final ViewHolder holder = getChildViewHolderInt(child);
   3551         return holder != null ? holder.getLayoutPosition() : NO_POSITION;
   3552     }
   3553 
   3554     /**
   3555      * Return the stable item id that the given child view corresponds to.
   3556      *
   3557      * @param child Child View to query
   3558      * @return Item id corresponding to the given view or {@link #NO_ID}
   3559      */
   3560     public long getChildItemId(View child) {
   3561         if (mAdapter == null || !mAdapter.hasStableIds()) {
   3562             return NO_ID;
   3563         }
   3564         final ViewHolder holder = getChildViewHolderInt(child);
   3565         return holder != null ? holder.getItemId() : NO_ID;
   3566     }
   3567 
   3568     /**
   3569      * @deprecated use {@link #findViewHolderForLayoutPosition(int)} or
   3570      * {@link #findViewHolderForAdapterPosition(int)}
   3571      */
   3572     @Deprecated
   3573     public ViewHolder findViewHolderForPosition(int position) {
   3574         return findViewHolderForPosition(position, false);
   3575     }
   3576 
   3577     /**
   3578      * Return the ViewHolder for the item in the given position of the data set as of the latest
   3579      * layout pass.
   3580      * <p>
   3581      * This method checks only the children of RecyclerView. If the item at the given
   3582      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   3583      * <p>
   3584      * Note that when Adapter contents change, ViewHolder positions are not updated until the
   3585      * next layout calculation. If there are pending adapter updates, the return value of this
   3586      * method may not match your adapter contents. You can use
   3587      * #{@link ViewHolder#getAdapterPosition()} to get the current adapter position of a ViewHolder.
   3588      *
   3589      * @param position The position of the item in the data set of the adapter
   3590      * @return The ViewHolder at <code>position</code> or null if there is no such item
   3591      */
   3592     public ViewHolder findViewHolderForLayoutPosition(int position) {
   3593         return findViewHolderForPosition(position, false);
   3594     }
   3595 
   3596     /**
   3597      * Return the ViewHolder for the item in the given position of the data set. Unlike
   3598      * {@link #findViewHolderForLayoutPosition(int)} this method takes into account any pending
   3599      * adapter changes that may not be reflected to the layout yet. On the other hand, if
   3600      * {@link Adapter#notifyDataSetChanged()} has been called but the new layout has not been
   3601      * calculated yet, this method will return <code>null</code> since the new positions of views
   3602      * are unknown until the layout is calculated.
   3603      * <p>
   3604      * This method checks only the children of RecyclerView. If the item at the given
   3605      * <code>position</code> is not laid out, it <em>will not</em> create a new one.
   3606      *
   3607      * @param position The position of the item in the data set of the adapter
   3608      * @return The ViewHolder at <code>position</code> or null if there is no such item
   3609      */
   3610     public ViewHolder findViewHolderForAdapterPosition(int position) {
   3611         if (mDataSetHasChangedAfterLayout) {
   3612             return null;
   3613         }
   3614         final int childCount = mChildHelper.getUnfilteredChildCount();
   3615         for (int i = 0; i < childCount; i++) {
   3616             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3617             if (holder != null && !holder.isRemoved() && getAdapterPositionFor(holder) == position) {
   3618                 return holder;
   3619             }
   3620         }
   3621         return null;
   3622     }
   3623 
   3624     ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
   3625         final int childCount = mChildHelper.getUnfilteredChildCount();
   3626         for (int i = 0; i < childCount; i++) {
   3627             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3628             if (holder != null && !holder.isRemoved()) {
   3629                 if (checkNewPosition) {
   3630                     if (holder.mPosition == position) {
   3631                         return holder;
   3632                     }
   3633                 } else if (holder.getLayoutPosition() == position) {
   3634                     return holder;
   3635                 }
   3636             }
   3637         }
   3638         // This method should not query cached views. It creates a problem during adapter updates
   3639         // when we are dealing with already laid out views. Also, for the public method, it is more
   3640         // reasonable to return null if position is not laid out.
   3641         return null;
   3642     }
   3643 
   3644     /**
   3645      * Return the ViewHolder for the item with the given id. The RecyclerView must
   3646      * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
   3647      * return a non-null value.
   3648      * <p>
   3649      * This method checks only the children of RecyclerView. If the item with the given
   3650      * <code>id</code> is not laid out, it <em>will not</em> create a new one.
   3651      *
   3652      * @param id The id for the requested item
   3653      * @return The ViewHolder with the given <code>id</code> or null if there is no such item
   3654      */
   3655     public ViewHolder findViewHolderForItemId(long id) {
   3656         final int childCount = mChildHelper.getUnfilteredChildCount();
   3657         for (int i = 0; i < childCount; i++) {
   3658             final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
   3659             if (holder != null && holder.getItemId() == id) {
   3660                 return holder;
   3661             }
   3662         }
   3663         // this method should not query cached views. They are not children so they
   3664         // should not be returned in this public method
   3665         return null;
   3666     }
   3667 
   3668     /**
   3669      * Find the topmost view under the given point.
   3670      *
   3671      * @param x Horizontal position in pixels to search
   3672      * @param y Vertical position in pixels to search
   3673      * @return The child view under (x, y) or null if no matching child is found
   3674      */
   3675     public View findChildViewUnder(float x, float y) {
   3676         final int count = mChildHelper.getChildCount();
   3677         for (int i = count - 1; i >= 0; i--) {
   3678             final View child = mChildHelper.getChildAt(i);
   3679             final float translationX = ViewCompat.getTranslationX(child);
   3680             final float translationY = ViewCompat.getTranslationY(child);
   3681             if (x >= child.getLeft() + translationX &&
   3682                     x <= child.getRight() + translationX &&
   3683                     y >= child.getTop() + translationY &&
   3684                     y <= child.getBottom() + translationY) {
   3685                 return child;
   3686             }
   3687         }
   3688         return null;
   3689     }
   3690 
   3691     @Override
   3692     public boolean drawChild(Canvas canvas, View child, long drawingTime) {
   3693         return super.drawChild(canvas, child, drawingTime);
   3694     }
   3695 
   3696     /**
   3697      * Offset the bounds of all child views by <code>dy</code> pixels.
   3698      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   3699      *
   3700      * @param dy Vertical pixel offset to apply to the bounds of all child views
   3701      */
   3702     public void offsetChildrenVertical(int dy) {
   3703         final int childCount = mChildHelper.getChildCount();
   3704         for (int i = 0; i < childCount; i++) {
   3705             mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
   3706         }
   3707     }
   3708 
   3709     /**
   3710      * Called when an item view is attached to this RecyclerView.
   3711      *
   3712      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   3713      * of child views as they become attached. This will be called before a
   3714      * {@link LayoutManager} measures or lays out the view and is a good time to perform these
   3715      * changes.</p>
   3716      *
   3717      * @param child Child view that is now attached to this RecyclerView and its associated window
   3718      */
   3719     public void onChildAttachedToWindow(View child) {
   3720     }
   3721 
   3722     /**
   3723      * Called when an item view is detached from this RecyclerView.
   3724      *
   3725      * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
   3726      * of child views as they become detached. This will be called as a
   3727      * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>
   3728      *
   3729      * @param child Child view that is now detached from this RecyclerView and its associated window
   3730      */
   3731     public void onChildDetachedFromWindow(View child) {
   3732     }
   3733 
   3734     /**
   3735      * Offset the bounds of all child views by <code>dx</code> pixels.
   3736      * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
   3737      *
   3738      * @param dx Horizontal pixel offset to apply to the bounds of all child views
   3739      */
   3740     public void offsetChildrenHorizontal(int dx) {
   3741         final int childCount = mChildHelper.getChildCount();
   3742         for (int i = 0; i < childCount; i++) {
   3743             mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
   3744         }
   3745     }
   3746 
   3747     Rect getItemDecorInsetsForChild(View child) {
   3748         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   3749         if (!lp.mInsetsDirty) {
   3750             return lp.mDecorInsets;
   3751         }
   3752 
   3753         final Rect insets = lp.mDecorInsets;
   3754         insets.set(0, 0, 0, 0);
   3755         final int decorCount = mItemDecorations.size();
   3756         for (int i = 0; i < decorCount; i++) {
   3757             mTempRect.set(0, 0, 0, 0);
   3758             mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
   3759             insets.left += mTempRect.left;
   3760             insets.top += mTempRect.top;
   3761             insets.right += mTempRect.right;
   3762             insets.bottom += mTempRect.bottom;
   3763         }
   3764         lp.mInsetsDirty = false;
   3765         return insets;
   3766     }
   3767 
   3768     /**
   3769      * Called when the scroll position of this RecyclerView changes. Subclasses should use
   3770      * this method to respond to scrolling within the adapter's data set instead of an explicit
   3771      * listener.
   3772      *
   3773      * <p>This method will always be invoked before listeners. If a subclass needs to perform
   3774      * any additional upkeep or bookkeeping after scrolling but before listeners run,
   3775      * this is a good place to do so.</p>
   3776      *
   3777      * <p>This differs from {@link View#onScrollChanged(int, int, int, int)} in that it receives
   3778      * the distance scrolled in either direction within the adapter's data set instead of absolute
   3779      * scroll coordinates. Since RecyclerView cannot compute the absolute scroll position from
   3780      * any arbitrary point in the data set, <code>onScrollChanged</code> will always receive
   3781      * the current {@link View#getScrollX()} and {@link View#getScrollY()} values which
   3782      * do not correspond to the data set scroll position. However, some subclasses may choose
   3783      * to use these fields as special offsets.</p>
   3784      *
   3785      * @param dx horizontal distance scrolled in pixels
   3786      * @param dy vertical distance scrolled in pixels
   3787      */
   3788     public void onScrolled(int dx, int dy) {
   3789         // Do nothing
   3790     }
   3791 
   3792     void dispatchOnScrolled(int hresult, int vresult) {
   3793         // Pass the current scrollX/scrollY values; no actual change in these properties occurred
   3794         // but some general-purpose code may choose to respond to changes this way.
   3795         final int scrollX = getScrollX();
   3796         final int scrollY = getScrollY();
   3797         onScrollChanged(scrollX, scrollY, scrollX, scrollY);
   3798 
   3799         // Pass the real deltas to onScrolled, the RecyclerView-specific method.
   3800         onScrolled(hresult, vresult);
   3801 
   3802         // Invoke listeners last. Subclassed view methods always handle the event first.
   3803         // All internal state is consistent by the time listeners are invoked.
   3804         if (mScrollListener != null) {
   3805             mScrollListener.onScrolled(this, hresult, vresult);
   3806         }
   3807         if (mScrollListeners != null) {
   3808             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   3809                 mScrollListeners.get(i).onScrolled(this, hresult, vresult);
   3810             }
   3811         }
   3812     }
   3813 
   3814     /**
   3815      * Called when the scroll state of this RecyclerView changes. Subclasses should use this
   3816      * method to respond to state changes instead of an explicit listener.
   3817      *
   3818      * <p>This method will always be invoked before listeners, but after the LayoutManager
   3819      * responds to the scroll state change.</p>
   3820      *
   3821      * @param state the new scroll state, one of {@link #SCROLL_STATE_IDLE},
   3822      *              {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}
   3823      */
   3824     public void onScrollStateChanged(int state) {
   3825         // Do nothing
   3826     }
   3827 
   3828     void dispatchOnScrollStateChanged(int state) {
   3829         // Let the LayoutManager go first; this allows it to bring any properties into
   3830         // a consistent state before the RecyclerView subclass responds.
   3831         if (mLayout != null) {
   3832             mLayout.onScrollStateChanged(state);
   3833         }
   3834 
   3835         // Let the RecyclerView subclass handle this event next; any LayoutManager property
   3836         // changes will be reflected by this time.
   3837         onScrollStateChanged(state);
   3838 
   3839         // Listeners go last. All other internal state is consistent by this point.
   3840         if (mScrollListener != null) {
   3841             mScrollListener.onScrollStateChanged(this, state);
   3842         }
   3843         if (mScrollListeners != null) {
   3844             for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
   3845                 mScrollListeners.get(i).onScrollStateChanged(this, state);
   3846             }
   3847         }
   3848     }
   3849 
   3850     /**
   3851      * Returns whether there are pending adapter updates which are not yet applied to the layout.
   3852      * <p>
   3853      * If this method returns <code>true</code>, it means that what user is currently seeing may not
   3854      * reflect them adapter contents (depending on what has changed).
   3855      * You may use this information to defer or cancel some operations.
   3856      * <p>
   3857      * This method returns true if RecyclerView has not yet calculated the first layout after it is
   3858      * attached to the Window or the Adapter has been replaced.
   3859      *
   3860      * @return True if there are some adapter updates which are not yet reflected to layout or false
   3861      * if layout is up to date.
   3862      */
   3863     public boolean hasPendingAdapterUpdates() {
   3864         return !mFirstLayoutComplete || mDataSetHasChangedAfterLayout
   3865                 || mAdapterHelper.hasPendingUpdates();
   3866     }
   3867 
   3868     private class ViewFlinger implements Runnable {
   3869         private int mLastFlingX;
   3870         private int mLastFlingY;
   3871         private ScrollerCompat mScroller;
   3872         private Interpolator mInterpolator = sQuinticInterpolator;
   3873 
   3874 
   3875         // When set to true, postOnAnimation callbacks are delayed until the run method completes
   3876         private boolean mEatRunOnAnimationRequest = false;
   3877 
   3878         // Tracks if postAnimationCallback should be re-attached when it is done
   3879         private boolean mReSchedulePostAnimationCallback = false;
   3880 
   3881         public ViewFlinger() {
   3882             mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);
   3883         }
   3884 
   3885         @Override
   3886         public void run() {
   3887             disableRunOnAnimationRequests();
   3888             consumePendingUpdateOperations();
   3889             // keep a local reference so that if it is changed during onAnimation method, it won't
   3890             // cause unexpected behaviors
   3891             final ScrollerCompat scroller = mScroller;
   3892             final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
   3893             if (scroller.computeScrollOffset()) {
   3894                 final int x = scroller.getCurrX();
   3895                 final int y = scroller.getCurrY();
   3896                 final int dx = x - mLastFlingX;
   3897                 final int dy = y - mLastFlingY;
   3898                 int hresult = 0;
   3899                 int vresult = 0;
   3900                 mLastFlingX = x;
   3901                 mLastFlingY = y;
   3902                 int overscrollX = 0, overscrollY = 0;
   3903                 if (mAdapter != null) {
   3904                     eatRequestLayout();
   3905                     onEnterLayoutOrScroll();
   3906                     TraceCompat.beginSection(TRACE_SCROLL_TAG);
   3907                     if (dx != 0) {
   3908                         hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
   3909                         overscrollX = dx - hresult;
   3910                     }
   3911                     if (dy != 0) {
   3912                         vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
   3913                         overscrollY = dy - vresult;
   3914                     }
   3915                     TraceCompat.endSection();
   3916                     if (supportsChangeAnimations()) {
   3917                         // Fix up shadow views used by changing animations
   3918                         int count = mChildHelper.getChildCount();
   3919                         for (int i = 0; i < count; i++) {
   3920                             View view = mChildHelper.getChildAt(i);
   3921                             ViewHolder holder = getChildViewHolder(view);
   3922                             if (holder != null && holder.mShadowingHolder != null) {
   3923                                 View shadowingView = holder.mShadowingHolder.itemView;
   3924                                 int left = view.getLeft();
   3925                                 int top = view.getTop();
   3926                                 if (left != shadowingView.getLeft() ||
   3927                                         top != shadowingView.getTop()) {
   3928                                     shadowingView.layout(left, top,
   3929                                             left + shadowingView.getWidth(),
   3930                                             top + shadowingView.getHeight());
   3931                                 }
   3932                             }
   3933                         }
   3934                     }
   3935                     onExitLayoutOrScroll();
   3936                     resumeRequestLayout(false);
   3937 
   3938                     if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&
   3939                             smoothScroller.isRunning()) {
   3940                         final int adapterSize = mState.getItemCount();
   3941                         if (adapterSize == 0) {
   3942                             smoothScroller.stop();
   3943                         } else if (smoothScroller.getTargetPosition() >= adapterSize) {
   3944                             smoothScroller.setTargetPosition(adapterSize - 1);
   3945                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   3946                         } else {
   3947                             smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
   3948                         }
   3949                     }
   3950                 }
   3951                 if (!mItemDecorations.isEmpty()) {
   3952                     invalidate();
   3953                 }
   3954                 if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
   3955                         ViewCompat.OVER_SCROLL_NEVER) {
   3956                     considerReleasingGlowsOnScroll(dx, dy);
   3957                 }
   3958                 if (overscrollX != 0 || overscrollY != 0) {
   3959                     final int vel = (int) scroller.getCurrVelocity();
   3960 
   3961                     int velX = 0;
   3962                     if (overscrollX != x) {
   3963                         velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
   3964                     }
   3965 
   3966                     int velY = 0;
   3967                     if (overscrollY != y) {
   3968                         velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
   3969                     }
   3970 
   3971                     if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
   3972                             ViewCompat.OVER_SCROLL_NEVER) {
   3973                         absorbGlows(velX, velY);
   3974                     }
   3975                     if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&
   3976                             (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
   3977                         scroller.abortAnimation();
   3978                     }
   3979                 }
   3980                 if (hresult != 0 || vresult != 0) {
   3981                     dispatchOnScrolled(hresult, vresult);
   3982                 }
   3983 
   3984                 if (!awakenScrollBars()) {
   3985                     invalidate();
   3986                 }
   3987 
   3988                 final boolean fullyConsumedVertical = dy != 0 && mLayout.canScrollVertically()
   3989                         && vresult == dy;
   3990                 final boolean fullyConsumedHorizontal = dx != 0 && mLayout.canScrollHorizontally()
   3991                         && hresult == dx;
   3992                 final boolean fullyConsumedAny = (dx == 0 && dy == 0) || fullyConsumedHorizontal
   3993                         || fullyConsumedVertical;
   3994 
   3995                 if (scroller.isFinished() || !fullyConsumedAny) {
   3996                     setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
   3997                 } else {
   3998                     postOnAnimation();
   3999                 }
   4000             }
   4001             // call this after the onAnimation is complete not to have inconsistent callbacks etc.
   4002             if (smoothScroller != null) {
   4003                 if (smoothScroller.isPendingInitialRun()) {
   4004                     smoothScroller.onAnimation(0, 0);
   4005                 }
   4006                 if (!mReSchedulePostAnimationCallback) {
   4007                     smoothScroller.stop(); //stop if it does not trigger any scroll
   4008                 }
   4009             }
   4010             enableRunOnAnimationRequests();
   4011         }
   4012 
   4013         private void disableRunOnAnimationRequests() {
   4014             mReSchedulePostAnimationCallback = false;
   4015             mEatRunOnAnimationRequest = true;
   4016         }
   4017 
   4018         private void enableRunOnAnimationRequests() {
   4019             mEatRunOnAnimationRequest = false;
   4020             if (mReSchedulePostAnimationCallback) {
   4021                 postOnAnimation();
   4022             }
   4023         }
   4024 
   4025         void postOnAnimation() {
   4026             if (mEatRunOnAnimationRequest) {
   4027                 mReSchedulePostAnimationCallback = true;
   4028             } else {
   4029                 removeCallbacks(this);
   4030                 ViewCompat.postOnAnimation(RecyclerView.this, this);
   4031             }
   4032         }
   4033 
   4034         public void fling(int velocityX, int velocityY) {
   4035             setScrollState(SCROLL_STATE_SETTLING);
   4036             mLastFlingX = mLastFlingY = 0;
   4037             mScroller.fling(0, 0, velocityX, velocityY,
   4038                     Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
   4039             postOnAnimation();
   4040         }
   4041 
   4042         public void smoothScrollBy(int dx, int dy) {
   4043             smoothScrollBy(dx, dy, 0, 0);
   4044         }
   4045 
   4046         public void smoothScrollBy(int dx, int dy, int vx, int vy) {
   4047             smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
   4048         }
   4049 
   4050         private float distanceInfluenceForSnapDuration(float f) {
   4051             f -= 0.5f; // center the values about 0.
   4052             f *= 0.3f * Math.PI / 2.0f;
   4053             return (float) Math.sin(f);
   4054         }
   4055 
   4056         private int computeScrollDuration(int dx, int dy, int vx, int vy) {
   4057             final int absDx = Math.abs(dx);
   4058             final int absDy = Math.abs(dy);
   4059             final boolean horizontal = absDx > absDy;
   4060             final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
   4061             final int delta = (int) Math.sqrt(dx * dx + dy * dy);
   4062             final int containerSize = horizontal ? getWidth() : getHeight();
   4063             final int halfContainerSize = containerSize / 2;
   4064             final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
   4065             final float distance = halfContainerSize + halfContainerSize *
   4066                     distanceInfluenceForSnapDuration(distanceRatio);
   4067 
   4068             final int duration;
   4069             if (velocity > 0) {
   4070                 duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
   4071             } else {
   4072                 float absDelta = (float) (horizontal ? absDx : absDy);
   4073                 duration = (int) (((absDelta / containerSize) + 1) * 300);
   4074             }
   4075             return Math.min(duration, MAX_SCROLL_DURATION);
   4076         }
   4077 
   4078         public void smoothScrollBy(int dx, int dy, int duration) {
   4079             smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
   4080         }
   4081 
   4082         public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
   4083             if (mInterpolator != interpolator) {
   4084                 mInterpolator = interpolator;
   4085                 mScroller = ScrollerCompat.create(getContext(), interpolator);
   4086             }
   4087             setScrollState(SCROLL_STATE_SETTLING);
   4088             mLastFlingX = mLastFlingY = 0;
   4089             mScroller.startScroll(0, 0, dx, dy, duration);
   4090             postOnAnimation();
   4091         }
   4092 
   4093         public void stop() {
   4094             removeCallbacks(this);
   4095             mScroller.abortAnimation();
   4096         }
   4097 
   4098     }
   4099 
   4100     private class RecyclerViewDataObserver extends AdapterDataObserver {
   4101         @Override
   4102         public void onChanged() {
   4103             assertNotInLayoutOrScroll(null);
   4104             if (mAdapter.hasStableIds()) {
   4105                 // TODO Determine what actually changed.
   4106                 // This is more important to implement now since this callback will disable all
   4107                 // animations because we cannot rely on positions.
   4108                 mState.mStructureChanged = true;
   4109                 setDataSetChangedAfterLayout();
   4110             } else {
   4111                 mState.mStructureChanged = true;
   4112                 setDataSetChangedAfterLayout();
   4113             }
   4114             if (!mAdapterHelper.hasPendingUpdates()) {
   4115                 requestLayout();
   4116             }
   4117         }
   4118 
   4119         @Override
   4120         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   4121             assertNotInLayoutOrScroll(null);
   4122             if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
   4123                 triggerUpdateProcessor();
   4124             }
   4125         }
   4126 
   4127         @Override
   4128         public void onItemRangeInserted(int positionStart, int itemCount) {
   4129             assertNotInLayoutOrScroll(null);
   4130             if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
   4131                 triggerUpdateProcessor();
   4132             }
   4133         }
   4134 
   4135         @Override
   4136         public void onItemRangeRemoved(int positionStart, int itemCount) {
   4137             assertNotInLayoutOrScroll(null);
   4138             if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
   4139                 triggerUpdateProcessor();
   4140             }
   4141         }
   4142 
   4143         @Override
   4144         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   4145             assertNotInLayoutOrScroll(null);
   4146             if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
   4147                 triggerUpdateProcessor();
   4148             }
   4149         }
   4150 
   4151         void triggerUpdateProcessor() {
   4152             if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
   4153                 ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
   4154             } else {
   4155                 mAdapterUpdateDuringMeasure = true;
   4156                 requestLayout();
   4157             }
   4158         }
   4159     }
   4160 
   4161     /**
   4162      * RecycledViewPool lets you share Views between multiple RecyclerViews.
   4163      * <p>
   4164      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
   4165      * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
   4166      * <p>
   4167      * RecyclerView automatically creates a pool for itself if you don't provide one.
   4168      *
   4169      */
   4170     public static class RecycledViewPool {
   4171         private SparseArray<ArrayList<ViewHolder>> mScrap =
   4172                 new SparseArray<ArrayList<ViewHolder>>();
   4173         private SparseIntArray mMaxScrap = new SparseIntArray();
   4174         private int mAttachCount = 0;
   4175 
   4176         private static final int DEFAULT_MAX_SCRAP = 5;
   4177 
   4178         public void clear() {
   4179             mScrap.clear();
   4180         }
   4181 
   4182         public void setMaxRecycledViews(int viewType, int max) {
   4183             mMaxScrap.put(viewType, max);
   4184             final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
   4185             if (scrapHeap != null) {
   4186                 while (scrapHeap.size() > max) {
   4187                     scrapHeap.remove(scrapHeap.size() - 1);
   4188                 }
   4189             }
   4190         }
   4191 
   4192         public ViewHolder getRecycledView(int viewType) {
   4193             final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
   4194             if (scrapHeap != null && !scrapHeap.isEmpty()) {
   4195                 final int index = scrapHeap.size() - 1;
   4196                 final ViewHolder scrap = scrapHeap.get(index);
   4197                 scrapHeap.remove(index);
   4198                 return scrap;
   4199             }
   4200             return null;
   4201         }
   4202 
   4203         int size() {
   4204             int count = 0;
   4205             for (int i = 0; i < mScrap.size(); i ++) {
   4206                 ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i);
   4207                 if (viewHolders != null) {
   4208                     count += viewHolders.size();
   4209                 }
   4210             }
   4211             return count;
   4212         }
   4213 
   4214         public void putRecycledView(ViewHolder scrap) {
   4215             final int viewType = scrap.getItemViewType();
   4216             final ArrayList scrapHeap = getScrapHeapForType(viewType);
   4217             if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
   4218                 return;
   4219             }
   4220             if (DEBUG && scrapHeap.contains(scrap)) {
   4221                 throw new IllegalArgumentException("this scrap item already exists");
   4222             }
   4223             scrap.resetInternal();
   4224             scrapHeap.add(scrap);
   4225         }
   4226 
   4227         void attach(Adapter adapter) {
   4228             mAttachCount++;
   4229         }
   4230 
   4231         void detach() {
   4232             mAttachCount--;
   4233         }
   4234 
   4235 
   4236         /**
   4237          * Detaches the old adapter and attaches the new one.
   4238          * <p>
   4239          * RecycledViewPool will clear its cache if it has only one adapter attached and the new
   4240          * adapter uses a different ViewHolder than the oldAdapter.
   4241          *
   4242          * @param oldAdapter The previous adapter instance. Will be detached.
   4243          * @param newAdapter The new adapter instance. Will be attached.
   4244          * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
   4245          *                               ViewHolder and view types.
   4246          */
   4247         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   4248                 boolean compatibleWithPrevious) {
   4249             if (oldAdapter != null) {
   4250                 detach();
   4251             }
   4252             if (!compatibleWithPrevious && mAttachCount == 0) {
   4253                 clear();
   4254             }
   4255             if (newAdapter != null) {
   4256                 attach(newAdapter);
   4257             }
   4258         }
   4259 
   4260         private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {
   4261             ArrayList<ViewHolder> scrap = mScrap.get(viewType);
   4262             if (scrap == null) {
   4263                 scrap = new ArrayList<ViewHolder>();
   4264                 mScrap.put(viewType, scrap);
   4265                 if (mMaxScrap.indexOfKey(viewType) < 0) {
   4266                     mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
   4267                 }
   4268             }
   4269             return scrap;
   4270         }
   4271     }
   4272 
   4273     /**
   4274      * A Recycler is responsible for managing scrapped or detached item views for reuse.
   4275      *
   4276      * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
   4277      * that has been marked for removal or reuse.</p>
   4278      *
   4279      * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
   4280      * an adapter's data set representing the data at a given position or item ID.
   4281      * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
   4282      * If not, the view can be quickly reused by the LayoutManager with no further work.
   4283      * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
   4284      * may be repositioned by a LayoutManager without remeasurement.</p>
   4285      */
   4286     public final class Recycler {
   4287         final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<ViewHolder>();
   4288         private ArrayList<ViewHolder> mChangedScrap = null;
   4289 
   4290         final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
   4291 
   4292         private final List<ViewHolder>
   4293                 mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
   4294 
   4295         private int mViewCacheMax = DEFAULT_CACHE_SIZE;
   4296 
   4297         private RecycledViewPool mRecyclerPool;
   4298 
   4299         private ViewCacheExtension mViewCacheExtension;
   4300 
   4301         private static final int DEFAULT_CACHE_SIZE = 2;
   4302 
   4303         /**
   4304          * Clear scrap views out of this recycler. Detached views contained within a
   4305          * recycled view pool will remain.
   4306          */
   4307         public void clear() {
   4308             mAttachedScrap.clear();
   4309             recycleAndClearCachedViews();
   4310         }
   4311 
   4312         /**
   4313          * Set the maximum number of detached, valid views we should retain for later use.
   4314          *
   4315          * @param viewCount Number of views to keep before sending views to the shared pool
   4316          */
   4317         public void setViewCacheSize(int viewCount) {
   4318             mViewCacheMax = viewCount;
   4319             // first, try the views that can be recycled
   4320             for (int i = mCachedViews.size() - 1; i >= 0 && mCachedViews.size() > viewCount; i--) {
   4321                 recycleCachedViewAt(i);
   4322             }
   4323         }
   4324 
   4325         /**
   4326          * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
   4327          *
   4328          * @return List of ViewHolders in the scrap list.
   4329          */
   4330         public List<ViewHolder> getScrapList() {
   4331             return mUnmodifiableAttachedScrap;
   4332         }
   4333 
   4334         /**
   4335          * Helper method for getViewForPosition.
   4336          * <p>
   4337          * Checks whether a given view holder can be used for the provided position.
   4338          *
   4339          * @param holder ViewHolder
   4340          * @return true if ViewHolder matches the provided position, false otherwise
   4341          */
   4342         boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
   4343             // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
   4344             // if it is not removed, verify the type and id.
   4345             if (holder.isRemoved()) {
   4346                 return true;
   4347             }
   4348             if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
   4349                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
   4350                         + "adapter position" + holder);
   4351             }
   4352             if (!mState.isPreLayout()) {
   4353                 // don't check type if it is pre-layout.
   4354                 final int type = mAdapter.getItemViewType(holder.mPosition);
   4355                 if (type != holder.getItemViewType()) {
   4356                     return false;
   4357                 }
   4358             }
   4359             if (mAdapter.hasStableIds()) {
   4360                 return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
   4361             }
   4362             return true;
   4363         }
   4364 
   4365         /**
   4366          * Binds the given View to the position. The View can be a View previously retrieved via
   4367          * {@link #getViewForPosition(int)} or created by
   4368          * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
   4369          * <p>
   4370          * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
   4371          * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
   4372          * wants to handle its own recycling logic.
   4373          * <p>
   4374          * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
   4375          * you don't need to call this method unless you want to bind this View to another position.
   4376          *
   4377          * @param view The view to update.
   4378          * @param position The position of the item to bind to this View.
   4379          */
   4380         public void bindViewToPosition(View view, int position) {
   4381             ViewHolder holder = getChildViewHolderInt(view);
   4382             if (holder == null) {
   4383                 throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
   4384                         + " pass arbitrary views to this method, they should be created by the "
   4385                         + "Adapter");
   4386             }
   4387             final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   4388             if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   4389                 throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   4390                         + "position " + position + "(offset:" + offsetPosition + ")."
   4391                         + "state:" + mState.getItemCount());
   4392             }
   4393             holder.mOwnerRecyclerView = RecyclerView.this;
   4394             mAdapter.bindViewHolder(holder, offsetPosition);
   4395             attachAccessibilityDelegate(view);
   4396             if (mState.isPreLayout()) {
   4397                 holder.mPreLayoutPosition = position;
   4398             }
   4399 
   4400             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   4401             final LayoutParams rvLayoutParams;
   4402             if (lp == null) {
   4403                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   4404                 holder.itemView.setLayoutParams(rvLayoutParams);
   4405             } else if (!checkLayoutParams(lp)) {
   4406                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   4407                 holder.itemView.setLayoutParams(rvLayoutParams);
   4408             } else {
   4409                 rvLayoutParams = (LayoutParams) lp;
   4410             }
   4411 
   4412             rvLayoutParams.mInsetsDirty = true;
   4413             rvLayoutParams.mViewHolder = holder;
   4414             rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
   4415         }
   4416 
   4417         /**
   4418          * RecyclerView provides artificial position range (item count) in pre-layout state and
   4419          * automatically maps these positions to {@link Adapter} positions when
   4420          * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
   4421          * <p>
   4422          * Usually, LayoutManager does not need to worry about this. However, in some cases, your
   4423          * LayoutManager may need to call some custom component with item positions in which
   4424          * case you need the actual adapter position instead of the pre layout position. You
   4425          * can use this method to convert a pre-layout position to adapter (post layout) position.
   4426          * <p>
   4427          * Note that if the provided position belongs to a deleted ViewHolder, this method will
   4428          * return -1.
   4429          * <p>
   4430          * Calling this method in post-layout state returns the same value back.
   4431          *
   4432          * @param position The pre-layout position to convert. Must be greater or equal to 0 and
   4433          *                 less than {@link State#getItemCount()}.
   4434          */
   4435         public int convertPreLayoutPositionToPostLayout(int position) {
   4436             if (position < 0 || position >= mState.getItemCount()) {
   4437                 throw new IndexOutOfBoundsException("invalid position " + position + ". State "
   4438                         + "item count is " + mState.getItemCount());
   4439             }
   4440             if (!mState.isPreLayout()) {
   4441                 return position;
   4442             }
   4443             return mAdapterHelper.findPositionOffset(position);
   4444         }
   4445 
   4446         /**
   4447          * Obtain a view initialized for the given position.
   4448          *
   4449          * This method should be used by {@link LayoutManager} implementations to obtain
   4450          * views to represent data from an {@link Adapter}.
   4451          * <p>
   4452          * The Recycler may reuse a scrap or detached view from a shared pool if one is
   4453          * available for the correct view type. If the adapter has not indicated that the
   4454          * data at the given position has changed, the Recycler will attempt to hand back
   4455          * a scrap view that was previously initialized for that data without rebinding.
   4456          *
   4457          * @param position Position to obtain a view for
   4458          * @return A view representing the data at <code>position</code> from <code>adapter</code>
   4459          */
   4460         public View getViewForPosition(int position) {
   4461             return getViewForPosition(position, false);
   4462         }
   4463 
   4464         View getViewForPosition(int position, boolean dryRun) {
   4465             if (position < 0 || position >= mState.getItemCount()) {
   4466                 throw new IndexOutOfBoundsException("Invalid item position " + position
   4467                         + "(" + position + "). Item count:" + mState.getItemCount());
   4468             }
   4469             boolean fromScrap = false;
   4470             ViewHolder holder = null;
   4471             // 0) If there is a changed scrap, try to find from there
   4472             if (mState.isPreLayout()) {
   4473                 holder = getChangedScrapViewForPosition(position);
   4474                 fromScrap = holder != null;
   4475             }
   4476             // 1) Find from scrap by position
   4477             if (holder == null) {
   4478                 holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
   4479                 if (holder != null) {
   4480                     if (!validateViewHolderForOffsetPosition(holder)) {
   4481                         // recycle this scrap
   4482                         if (!dryRun) {
   4483                             // we would like to recycle this but need to make sure it is not used by
   4484                             // animation logic etc.
   4485                             holder.addFlags(ViewHolder.FLAG_INVALID);
   4486                             if (holder.isScrap()) {
   4487                                 removeDetachedView(holder.itemView, false);
   4488                                 holder.unScrap();
   4489                             } else if (holder.wasReturnedFromScrap()) {
   4490                                 holder.clearReturnedFromScrapFlag();
   4491                             }
   4492                             recycleViewHolderInternal(holder);
   4493                         }
   4494                         holder = null;
   4495                     } else {
   4496                         fromScrap = true;
   4497                     }
   4498                 }
   4499             }
   4500             if (holder == null) {
   4501                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   4502                 if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
   4503                     throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
   4504                             + "position " + position + "(offset:" + offsetPosition + ")."
   4505                             + "state:" + mState.getItemCount());
   4506                 }
   4507 
   4508                 final int type = mAdapter.getItemViewType(offsetPosition);
   4509                 // 2) Find from scrap via stable ids, if exists
   4510                 if (mAdapter.hasStableIds()) {
   4511                     holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
   4512                     if (holder != null) {
   4513                         // update position
   4514                         holder.mPosition = offsetPosition;
   4515                         fromScrap = true;
   4516                     }
   4517                 }
   4518                 if (holder == null && mViewCacheExtension != null) {
   4519                     // We are NOT sending the offsetPosition because LayoutManager does not
   4520                     // know it.
   4521                     final View view = mViewCacheExtension
   4522                             .getViewForPositionAndType(this, position, type);
   4523                     if (view != null) {
   4524                         holder = getChildViewHolder(view);
   4525                         if (holder == null) {
   4526                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   4527                                     + " a view which does not have a ViewHolder");
   4528                         } else if (holder.shouldIgnore()) {
   4529                             throw new IllegalArgumentException("getViewForPositionAndType returned"
   4530                                     + " a view that is ignored. You must call stopIgnoring before"
   4531                                     + " returning this view.");
   4532                         }
   4533                     }
   4534                 }
   4535                 if (holder == null) { // fallback to recycler
   4536                     // try recycler.
   4537                     // Head to the shared pool.
   4538                     if (DEBUG) {
   4539                         Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
   4540                                 + "pool");
   4541                     }
   4542                     holder = getRecycledViewPool().getRecycledView(type);
   4543                     if (holder != null) {
   4544                         holder.resetInternal();
   4545                         if (FORCE_INVALIDATE_DISPLAY_LIST) {
   4546                             invalidateDisplayListInt(holder);
   4547                         }
   4548                     }
   4549                 }
   4550                 if (holder == null) {
   4551                     holder = mAdapter.createViewHolder(RecyclerView.this, type);
   4552                     if (DEBUG) {
   4553                         Log.d(TAG, "getViewForPosition created new ViewHolder");
   4554                     }
   4555                 }
   4556             }
   4557             boolean bound = false;
   4558             if (mState.isPreLayout() && holder.isBound()) {
   4559                 // do not update unless we absolutely have to.
   4560                 holder.mPreLayoutPosition = position;
   4561             } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
   4562                 if (DEBUG && holder.isRemoved()) {
   4563                     throw new IllegalStateException("Removed holder should be bound and it should"
   4564                             + " come here only in pre-layout. Holder: " + holder);
   4565                 }
   4566                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   4567                 holder.mOwnerRecyclerView = RecyclerView.this;
   4568                 mAdapter.bindViewHolder(holder, offsetPosition);
   4569                 attachAccessibilityDelegate(holder.itemView);
   4570                 bound = true;
   4571                 if (mState.isPreLayout()) {
   4572                     holder.mPreLayoutPosition = position;
   4573                 }
   4574             }
   4575 
   4576             final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
   4577             final LayoutParams rvLayoutParams;
   4578             if (lp == null) {
   4579                 rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
   4580                 holder.itemView.setLayoutParams(rvLayoutParams);
   4581             } else if (!checkLayoutParams(lp)) {
   4582                 rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
   4583                 holder.itemView.setLayoutParams(rvLayoutParams);
   4584             } else {
   4585                 rvLayoutParams = (LayoutParams) lp;
   4586             }
   4587             rvLayoutParams.mViewHolder = holder;
   4588             rvLayoutParams.mPendingInvalidate = fromScrap && bound;
   4589             return holder.itemView;
   4590         }
   4591 
   4592         private void attachAccessibilityDelegate(View itemView) {
   4593             if (isAccessibilityEnabled()) {
   4594                 if (ViewCompat.getImportantForAccessibility(itemView) ==
   4595                         ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
   4596                     ViewCompat.setImportantForAccessibility(itemView,
   4597                             ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
   4598                 }
   4599                 if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
   4600                     ViewCompat.setAccessibilityDelegate(itemView,
   4601                             mAccessibilityDelegate.getItemDelegate());
   4602                 }
   4603             }
   4604         }
   4605 
   4606         private void invalidateDisplayListInt(ViewHolder holder) {
   4607             if (holder.itemView instanceof ViewGroup) {
   4608                 invalidateDisplayListInt((ViewGroup) holder.itemView, false);
   4609             }
   4610         }
   4611 
   4612         private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
   4613             for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
   4614                 final View view = viewGroup.getChildAt(i);
   4615                 if (view instanceof ViewGroup) {
   4616                     invalidateDisplayListInt((ViewGroup) view, true);
   4617                 }
   4618             }
   4619             if (!invalidateThis) {
   4620                 return;
   4621             }
   4622             // we need to force it to become invisible
   4623             if (viewGroup.getVisibility() == View.INVISIBLE) {
   4624                 viewGroup.setVisibility(View.VISIBLE);
   4625                 viewGroup.setVisibility(View.INVISIBLE);
   4626             } else {
   4627                 final int visibility = viewGroup.getVisibility();
   4628                 viewGroup.setVisibility(View.INVISIBLE);
   4629                 viewGroup.setVisibility(visibility);
   4630             }
   4631         }
   4632 
   4633         /**
   4634          * Recycle a detached view. The specified view will be added to a pool of views
   4635          * for later rebinding and reuse.
   4636          *
   4637          * <p>A view must be fully detached (removed from parent) before it may be recycled. If the
   4638          * View is scrapped, it will be removed from scrap list.</p>
   4639          *
   4640          * @param view Removed view for recycling
   4641          * @see LayoutManager#removeAndRecycleView(View, Recycler)
   4642          */
   4643         public void recycleView(View view) {
   4644             // This public recycle method tries to make view recycle-able since layout manager
   4645             // intended to recycle this view (e.g. even if it is in scrap or change cache)
   4646             ViewHolder holder = getChildViewHolderInt(view);
   4647             if (holder.isTmpDetached()) {
   4648                 removeDetachedView(view, false);
   4649             }
   4650             if (holder.isScrap()) {
   4651                 holder.unScrap();
   4652             } else if (holder.wasReturnedFromScrap()){
   4653                 holder.clearReturnedFromScrapFlag();
   4654             }
   4655             recycleViewHolderInternal(holder);
   4656         }
   4657 
   4658         /**
   4659          * Internally, use this method instead of {@link #recycleView(android.view.View)} to
   4660          * catch potential bugs.
   4661          * @param view
   4662          */
   4663         void recycleViewInternal(View view) {
   4664             recycleViewHolderInternal(getChildViewHolderInt(view));
   4665         }
   4666 
   4667         void recycleAndClearCachedViews() {
   4668             final int count = mCachedViews.size();
   4669             for (int i = count - 1; i >= 0; i--) {
   4670                 recycleCachedViewAt(i);
   4671             }
   4672             mCachedViews.clear();
   4673         }
   4674 
   4675         /**
   4676          * Recycles a cached view and removes the view from the list. Views are added to cache
   4677          * if and only if they are recyclable, so this method does not check it again.
   4678          * <p>
   4679          * A small exception to this rule is when the view does not have an animator reference
   4680          * but transient state is true (due to animations created outside ItemAnimator). In that
   4681          * case, adapter may choose to recycle it. From RecyclerView's perspective, the view is
   4682          * still recyclable since Adapter wants to do so.
   4683          *
   4684          * @param cachedViewIndex The index of the view in cached views list
   4685          */
   4686         void recycleCachedViewAt(int cachedViewIndex) {
   4687             if (DEBUG) {
   4688                 Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
   4689             }
   4690             ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
   4691             if (DEBUG) {
   4692                 Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
   4693             }
   4694             addViewHolderToRecycledViewPool(viewHolder);
   4695             mCachedViews.remove(cachedViewIndex);
   4696         }
   4697 
   4698         /**
   4699          * internal implementation checks if view is scrapped or attached and throws an exception
   4700          * if so.
   4701          * Public version un-scraps before calling recycle.
   4702          */
   4703         void recycleViewHolderInternal(ViewHolder holder) {
   4704             if (holder.isScrap() || holder.itemView.getParent() != null) {
   4705                 throw new IllegalArgumentException(
   4706                         "Scrapped or attached views may not be recycled. isScrap:"
   4707                                 + holder.isScrap() + " isAttached:"
   4708                                 + (holder.itemView.getParent() != null));
   4709             }
   4710 
   4711             if (holder.isTmpDetached()) {
   4712                 throw new IllegalArgumentException("Tmp detached view should be removed "
   4713                         + "from RecyclerView before it can be recycled: " + holder);
   4714             }
   4715 
   4716             if (holder.shouldIgnore()) {
   4717                 throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
   4718                         + " should first call stopIgnoringView(view) before calling recycle.");
   4719             }
   4720             //noinspection unchecked
   4721             final boolean transientStatePreventsRecycling = holder
   4722                     .doesTransientStatePreventRecycling();
   4723             final boolean forceRecycle = mAdapter != null
   4724                     && transientStatePreventsRecycling
   4725                     && mAdapter.onFailedToRecycleView(holder);
   4726             boolean cached = false;
   4727             boolean recycled = false;
   4728             if (DEBUG && mCachedViews.contains(holder)) {
   4729                 throw new IllegalArgumentException("cached view received recycle internal? " +
   4730                         holder);
   4731             }
   4732             if (forceRecycle || holder.isRecyclable()) {
   4733                 if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED |
   4734                         ViewHolder.FLAG_CHANGED | ViewHolder.FLAG_UPDATE)) {
   4735                     // Retire oldest cached view
   4736                     final int cachedViewSize = mCachedViews.size();
   4737                     if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) {
   4738                         recycleCachedViewAt(0);
   4739                     }
   4740                     if (cachedViewSize < mViewCacheMax) {
   4741                         mCachedViews.add(holder);
   4742                         cached = true;
   4743                     }
   4744                 }
   4745                 if (!cached) {
   4746                     addViewHolderToRecycledViewPool(holder);
   4747                     recycled = true;
   4748                 }
   4749             } else if (DEBUG) {
   4750                 Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
   4751                         + "re-visit here. We are still removing it from animation lists");
   4752             }
   4753             // even if the holder is not removed, we still call this method so that it is removed
   4754             // from view holder lists.
   4755             mState.onViewRecycled(holder);
   4756             if (!cached && !recycled && transientStatePreventsRecycling) {
   4757                 holder.mOwnerRecyclerView = null;
   4758             }
   4759         }
   4760 
   4761         void addViewHolderToRecycledViewPool(ViewHolder holder) {
   4762             ViewCompat.setAccessibilityDelegate(holder.itemView, null);
   4763             dispatchViewRecycled(holder);
   4764             holder.mOwnerRecyclerView = null;
   4765             getRecycledViewPool().putRecycledView(holder);
   4766         }
   4767 
   4768         /**
   4769          * Used as a fast path for unscrapping and recycling a view during a bulk operation.
   4770          * The caller must call {@link #clearScrap()} when it's done to update the recycler's
   4771          * internal bookkeeping.
   4772          */
   4773         void quickRecycleScrapView(View view) {
   4774             final ViewHolder holder = getChildViewHolderInt(view);
   4775             holder.mScrapContainer = null;
   4776             holder.clearReturnedFromScrapFlag();
   4777             recycleViewHolderInternal(holder);
   4778         }
   4779 
   4780         /**
   4781          * Mark an attached view as scrap.
   4782          *
   4783          * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible
   4784          * for rebinding and reuse. Requests for a view for a given position may return a
   4785          * reused or rebound scrap view instance.</p>
   4786          *
   4787          * @param view View to scrap
   4788          */
   4789         void scrapView(View view) {
   4790             final ViewHolder holder = getChildViewHolderInt(view);
   4791             holder.setScrapContainer(this);
   4792             if (!holder.isChanged() || !supportsChangeAnimations()) {
   4793                 if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
   4794                     throw new IllegalArgumentException("Called scrap view with an invalid view."
   4795                             + " Invalid views cannot be reused from scrap, they should rebound from"
   4796                             + " recycler pool.");
   4797                 }
   4798                 mAttachedScrap.add(holder);
   4799             } else {
   4800                 if (mChangedScrap == null) {
   4801                     mChangedScrap = new ArrayList<ViewHolder>();
   4802                 }
   4803                 mChangedScrap.add(holder);
   4804             }
   4805         }
   4806 
   4807         /**
   4808          * Remove a previously scrapped view from the pool of eligible scrap.
   4809          *
   4810          * <p>This view will no longer be eligible for reuse until re-scrapped or
   4811          * until it is explicitly removed and recycled.</p>
   4812          */
   4813         void unscrapView(ViewHolder holder) {
   4814             if (!holder.isChanged() || !supportsChangeAnimations() || mChangedScrap == null) {
   4815                 mAttachedScrap.remove(holder);
   4816             } else {
   4817                 mChangedScrap.remove(holder);
   4818             }
   4819             holder.mScrapContainer = null;
   4820             holder.clearReturnedFromScrapFlag();
   4821         }
   4822 
   4823         int getScrapCount() {
   4824             return mAttachedScrap.size();
   4825         }
   4826 
   4827         View getScrapViewAt(int index) {
   4828             return mAttachedScrap.get(index).itemView;
   4829         }
   4830 
   4831         void clearScrap() {
   4832             mAttachedScrap.clear();
   4833         }
   4834 
   4835         ViewHolder getChangedScrapViewForPosition(int position) {
   4836             // If pre-layout, check the changed scrap for an exact match.
   4837             final int changedScrapSize;
   4838             if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
   4839                 return null;
   4840             }
   4841             // find by position
   4842             for (int i = 0; i < changedScrapSize; i++) {
   4843                 final ViewHolder holder = mChangedScrap.get(i);
   4844                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position) {
   4845                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   4846                     return holder;
   4847                 }
   4848             }
   4849             // find by id
   4850             if (mAdapter.hasStableIds()) {
   4851                 final int offsetPosition = mAdapterHelper.findPositionOffset(position);
   4852                 if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
   4853                     final long id = mAdapter.getItemId(offsetPosition);
   4854                     for (int i = 0; i < changedScrapSize; i++) {
   4855                         final ViewHolder holder = mChangedScrap.get(i);
   4856                         if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
   4857                             holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   4858                             return holder;
   4859                         }
   4860                     }
   4861                 }
   4862             }
   4863             return null;
   4864         }
   4865 
   4866         /**
   4867          * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if
   4868          * ViewHolder's type matches the provided type.
   4869          *
   4870          * @param position Item position
   4871          * @param type View type
   4872          * @param dryRun  Does a dry run, finds the ViewHolder but does not remove
   4873          * @return a ViewHolder that can be re-used for this position.
   4874          */
   4875         ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
   4876             final int scrapCount = mAttachedScrap.size();
   4877 
   4878             // Try first for an exact, non-invalid match from scrap.
   4879             for (int i = 0; i < scrapCount; i++) {
   4880                 final ViewHolder holder = mAttachedScrap.get(i);
   4881                 if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
   4882                         && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
   4883                     if (type != INVALID_TYPE && holder.getItemViewType() != type) {
   4884                         Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
   4885                                 " wrong view type! (found " + holder.getItemViewType() +
   4886                                 " but expected " + type + ")");
   4887                         break;
   4888                     }
   4889                     holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   4890                     return holder;
   4891                 }
   4892             }
   4893 
   4894             if (!dryRun) {
   4895                 View view = mChildHelper.findHiddenNonRemovedView(position, type);
   4896                 if (view != null) {
   4897                     // ending the animation should cause it to get recycled before we reuse it
   4898                     mItemAnimator.endAnimation(getChildViewHolder(view));
   4899                 }
   4900             }
   4901 
   4902             // Search in our first-level recycled view cache.
   4903             final int cacheSize = mCachedViews.size();
   4904             for (int i = 0; i < cacheSize; i++) {
   4905                 final ViewHolder holder = mCachedViews.get(i);
   4906                 // invalid view holders may be in cache if adapter has stable ids as they can be
   4907                 // retrieved via getScrapViewForId
   4908                 if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
   4909                     if (!dryRun) {
   4910                         mCachedViews.remove(i);
   4911                     }
   4912                     if (DEBUG) {
   4913                         Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
   4914                                 ") found match in cache: " + holder);
   4915                     }
   4916                     return holder;
   4917                 }
   4918             }
   4919             return null;
   4920         }
   4921 
   4922         ViewHolder getScrapViewForId(long id, int type, boolean dryRun) {
   4923             // Look in our attached views first
   4924             final int count = mAttachedScrap.size();
   4925             for (int i = count - 1; i >= 0; i--) {
   4926                 final ViewHolder holder = mAttachedScrap.get(i);
   4927                 if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
   4928                     if (type == holder.getItemViewType()) {
   4929                         holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
   4930                         if (holder.isRemoved()) {
   4931                             // this might be valid in two cases:
   4932                             // > item is removed but we are in pre-layout pass
   4933                             // >> do nothing. return as is. make sure we don't rebind
   4934                             // > item is removed then added to another position and we are in
   4935                             // post layout.
   4936                             // >> remove removed and invalid flags, add update flag to rebind
   4937                             // because item was invisible to us and we don't know what happened in
   4938                             // between.
   4939                             if (!mState.isPreLayout()) {
   4940                                 holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE |
   4941                                         ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
   4942                             }
   4943                         }
   4944                         return holder;
   4945                     } else if (!dryRun) {
   4946                         // Recycle this scrap. Type mismatch.
   4947                         mAttachedScrap.remove(i);
   4948                         removeDetachedView(holder.itemView, false);
   4949                         quickRecycleScrapView(holder.itemView);
   4950                     }
   4951                 }
   4952             }
   4953 
   4954             // Search the first-level cache
   4955             final int cacheSize = mCachedViews.size();
   4956             for (int i = cacheSize - 1; i >= 0; i--) {
   4957                 final ViewHolder holder = mCachedViews.get(i);
   4958                 if (holder.getItemId() == id) {
   4959                     if (type == holder.getItemViewType()) {
   4960                         if (!dryRun) {
   4961                             mCachedViews.remove(i);
   4962                         }
   4963                         return holder;
   4964                     } else if (!dryRun) {
   4965                         recycleCachedViewAt(i);
   4966                     }
   4967                 }
   4968             }
   4969             return null;
   4970         }
   4971 
   4972         void dispatchViewRecycled(ViewHolder holder) {
   4973             if (mRecyclerListener != null) {
   4974                 mRecyclerListener.onViewRecycled(holder);
   4975             }
   4976             if (mAdapter != null) {
   4977                 mAdapter.onViewRecycled(holder);
   4978             }
   4979             if (mState != null) {
   4980                 mState.onViewRecycled(holder);
   4981             }
   4982             if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
   4983         }
   4984 
   4985         void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
   4986                 boolean compatibleWithPrevious) {
   4987             clear();
   4988             getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
   4989         }
   4990 
   4991         void offsetPositionRecordsForMove(int from, int to) {
   4992             final int start, end, inBetweenOffset;
   4993             if (from < to) {
   4994                 start = from;
   4995                 end = to;
   4996                 inBetweenOffset = -1;
   4997             } else {
   4998                 start = to;
   4999                 end = from;
   5000                 inBetweenOffset = 1;
   5001             }
   5002             final int cachedCount = mCachedViews.size();
   5003             for (int i = 0; i < cachedCount; i++) {
   5004                 final ViewHolder holder = mCachedViews.get(i);
   5005                 if (holder == null || holder.mPosition < start || holder.mPosition > end) {
   5006                     continue;
   5007                 }
   5008                 if (holder.mPosition == from) {
   5009                     holder.offsetPosition(to - from, false);
   5010                 } else {
   5011                     holder.offsetPosition(inBetweenOffset, false);
   5012                 }
   5013                 if (DEBUG) {
   5014                     Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " +
   5015                             holder);
   5016                 }
   5017             }
   5018         }
   5019 
   5020         void offsetPositionRecordsForInsert(int insertedAt, int count) {
   5021             final int cachedCount = mCachedViews.size();
   5022             for (int i = 0; i < cachedCount; i++) {
   5023                 final ViewHolder holder = mCachedViews.get(i);
   5024                 if (holder != null && holder.getLayoutPosition() >= insertedAt) {
   5025                     if (DEBUG) {
   5026                         Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
   5027                                 holder + " now at position " + (holder.mPosition + count));
   5028                     }
   5029                     holder.offsetPosition(count, true);
   5030                 }
   5031             }
   5032         }
   5033 
   5034         /**
   5035          * @param removedFrom Remove start index
   5036          * @param count Remove count
   5037          * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
   5038          *                         false, they'll be applied before the second layout pass
   5039          */
   5040         void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
   5041             final int removedEnd = removedFrom + count;
   5042             final int cachedCount = mCachedViews.size();
   5043             for (int i = cachedCount - 1; i >= 0; i--) {
   5044                 final ViewHolder holder = mCachedViews.get(i);
   5045                 if (holder != null) {
   5046                     if (holder.getLayoutPosition() >= removedEnd) {
   5047                         if (DEBUG) {
   5048                             Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
   5049                                     " holder " + holder + " now at position " +
   5050                                     (holder.mPosition - count));
   5051                         }
   5052                         holder.offsetPosition(-count, applyToPreLayout);
   5053                     } else if (holder.getLayoutPosition() >= removedFrom) {
   5054                         // Item for this view was removed. Dump it from the cache.
   5055                         holder.addFlags(ViewHolder.FLAG_REMOVED);
   5056                         recycleCachedViewAt(i);
   5057                     }
   5058                 }
   5059             }
   5060         }
   5061 
   5062         void setViewCacheExtension(ViewCacheExtension extension) {
   5063             mViewCacheExtension = extension;
   5064         }
   5065 
   5066         void setRecycledViewPool(RecycledViewPool pool) {
   5067             if (mRecyclerPool != null) {
   5068                 mRecyclerPool.detach();
   5069             }
   5070             mRecyclerPool = pool;
   5071             if (pool != null) {
   5072                 mRecyclerPool.attach(getAdapter());
   5073             }
   5074         }
   5075 
   5076         RecycledViewPool getRecycledViewPool() {
   5077             if (mRecyclerPool == null) {
   5078                 mRecyclerPool = new RecycledViewPool();
   5079             }
   5080             return mRecyclerPool;
   5081         }
   5082 
   5083         void viewRangeUpdate(int positionStart, int itemCount) {
   5084             final int positionEnd = positionStart + itemCount;
   5085             final int cachedCount = mCachedViews.size();
   5086             for (int i = cachedCount - 1; i >= 0; i--) {
   5087                 final ViewHolder holder = mCachedViews.get(i);
   5088                 if (holder == null) {
   5089                     continue;
   5090                 }
   5091 
   5092                 final int pos = holder.getLayoutPosition();
   5093                 if (pos >= positionStart && pos < positionEnd) {
   5094                     holder.addFlags(ViewHolder.FLAG_UPDATE);
   5095                     recycleCachedViewAt(i);
   5096                     // cached views should not be flagged as changed because this will cause them
   5097                     // to animate when they are returned from cache.
   5098                 }
   5099             }
   5100         }
   5101 
   5102         void setAdapterPositionsAsUnknown() {
   5103             final int cachedCount = mCachedViews.size();
   5104             for (int i = 0; i < cachedCount; i++) {
   5105                 final ViewHolder holder = mCachedViews.get(i);
   5106                 if (holder != null) {
   5107                     holder.addFlags(ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   5108                 }
   5109             }
   5110         }
   5111 
   5112         void markKnownViewsInvalid() {
   5113             if (mAdapter != null && mAdapter.hasStableIds()) {
   5114                 final int cachedCount = mCachedViews.size();
   5115                 for (int i = 0; i < cachedCount; i++) {
   5116                     final ViewHolder holder = mCachedViews.get(i);
   5117                     if (holder != null) {
   5118                         holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
   5119                         holder.addChangePayload(null);
   5120                     }
   5121                 }
   5122             } else {
   5123                 // we cannot re-use cached views in this case. Recycle them all
   5124                 recycleAndClearCachedViews();
   5125             }
   5126         }
   5127 
   5128         void clearOldPositions() {
   5129             final int cachedCount = mCachedViews.size();
   5130             for (int i = 0; i < cachedCount; i++) {
   5131                 final ViewHolder holder = mCachedViews.get(i);
   5132                 holder.clearOldPosition();
   5133             }
   5134             final int scrapCount = mAttachedScrap.size();
   5135             for (int i = 0; i < scrapCount; i++) {
   5136                 mAttachedScrap.get(i).clearOldPosition();
   5137             }
   5138             if (mChangedScrap != null) {
   5139                 final int changedScrapCount = mChangedScrap.size();
   5140                 for (int i = 0; i < changedScrapCount; i++) {
   5141                     mChangedScrap.get(i).clearOldPosition();
   5142                 }
   5143             }
   5144         }
   5145 
   5146         void markItemDecorInsetsDirty() {
   5147             final int cachedCount = mCachedViews.size();
   5148             for (int i = 0; i < cachedCount; i++) {
   5149                 final ViewHolder holder = mCachedViews.get(i);
   5150                 LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
   5151                 if (layoutParams != null) {
   5152                     layoutParams.mInsetsDirty = true;
   5153                 }
   5154             }
   5155         }
   5156     }
   5157 
   5158     /**
   5159      * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
   5160      * ben controlled by the developer.
   5161      * <p>
   5162      * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
   5163      * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
   5164      * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
   5165      * {@link RecycledViewPool}.
   5166      * <p>
   5167      * Note that, Recycler never sends Views to this method to be cached. It is developers
   5168      * responsibility to decide whether they want to keep their Views in this custom cache or let
   5169      * the default recycling policy handle it.
   5170      */
   5171     public abstract static class ViewCacheExtension {
   5172 
   5173         /**
   5174          * Returns a View that can be binded to the given Adapter position.
   5175          * <p>
   5176          * This method should <b>not</b> create a new View. Instead, it is expected to return
   5177          * an already created View that can be re-used for the given type and position.
   5178          * If the View is marked as ignored, it should first call
   5179          * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
   5180          * <p>
   5181          * RecyclerView will re-bind the returned View to the position if necessary.
   5182          *
   5183          * @param recycler The Recycler that can be used to bind the View
   5184          * @param position The adapter position
   5185          * @param type     The type of the View, defined by adapter
   5186          * @return A View that is bound to the given position or NULL if there is no View to re-use
   5187          * @see LayoutManager#ignoreView(View)
   5188          */
   5189         abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
   5190     }
   5191 
   5192     /**
   5193      * Base class for an Adapter
   5194      *
   5195      * <p>Adapters provide a binding from an app-specific data set to views that are displayed
   5196      * within a {@link RecyclerView}.</p>
   5197      */
   5198     public static abstract class Adapter<VH extends ViewHolder> {
   5199         private final AdapterDataObservable mObservable = new AdapterDataObservable();
   5200         private boolean mHasStableIds = false;
   5201 
   5202         /**
   5203          * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
   5204          * an item.
   5205          * <p>
   5206          * This new ViewHolder should be constructed with a new View that can represent the items
   5207          * of the given type. You can either create a new View manually or inflate it from an XML
   5208          * layout file.
   5209          * <p>
   5210          * The new ViewHolder will be used to display items of the adapter using
   5211          * {@link #onBindViewHolder(ViewHolder, int, List)}. Since it will be re-used to display
   5212          * different items in the data set, it is a good idea to cache references to sub views of
   5213          * the View to avoid unnecessary {@link View#findViewById(int)} calls.
   5214          *
   5215          * @param parent The ViewGroup into which the new View will be added after it is bound to
   5216          *               an adapter position.
   5217          * @param viewType The view type of the new View.
   5218          *
   5219          * @return A new ViewHolder that holds a View of the given view type.
   5220          * @see #getItemViewType(int)
   5221          * @see #onBindViewHolder(ViewHolder, int)
   5222          */
   5223         public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
   5224 
   5225         /**
   5226          * Called by RecyclerView to display the data at the specified position. This method should
   5227          * update the contents of the {@link ViewHolder#itemView} to reflect the item at the given
   5228          * position.
   5229          * <p>
   5230          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   5231          * again if the position of the item changes in the data set unless the item itself is
   5232          * invalidated or the new position cannot be determined. For this reason, you should only
   5233          * use the <code>position</code> parameter while acquiring the related data item inside
   5234          * this method and should not keep a copy of it. If you need the position of an item later
   5235          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   5236          * have the updated adapter position.
   5237          *
   5238          * Override {@link #onBindViewHolder(ViewHolder, int, List)} instead if Adapter can
   5239          * handle effcient partial bind.
   5240          *
   5241          * @param holder The ViewHolder which should be updated to represent the contents of the
   5242          *        item at the given position in the data set.
   5243          * @param position The position of the item within the adapter's data set.
   5244          */
   5245         public abstract void onBindViewHolder(VH holder, int position);
   5246 
   5247         /**
   5248          * Called by RecyclerView to display the data at the specified position. This method
   5249          * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
   5250          * the given position.
   5251          * <p>
   5252          * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this method
   5253          * again if the position of the item changes in the data set unless the item itself is
   5254          * invalidated or the new position cannot be determined. For this reason, you should only
   5255          * use the <code>position</code> parameter while acquiring the related data item inside
   5256          * this method and should not keep a copy of it. If you need the position of an item later
   5257          * on (e.g. in a click listener), use {@link ViewHolder#getAdapterPosition()} which will
   5258          * have the updated adapter position.
   5259          * <p>
   5260          * Partial bind vs full bind:
   5261          * <p>
   5262          * The payloads parameter is a merge list from {@link #notifyItemChanged(int, Object)} or
   5263          * {@link #notifyItemRangeChanged(int, int, Object)}.  If the payloads list is not empty,
   5264          * the ViewHolder is currently bound to old data and Adapter may run an efficient partial
   5265          * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
   5266          * Adapter should not assume that the payload passed in notify methods will be received by
   5267          * onBindViewHolder().  For example when the view is not attached to the screen, the
   5268          * payload in notifyItemChange() will be simply dropped.
   5269          *
   5270          * @param holder The ViewHolder which should be updated to represent the contents of the
   5271          *               item at the given position in the data set.
   5272          * @param position The position of the item within the adapter's data set.
   5273          * @param payloads A non-null list of merged payloads. Can be empty list if requires full
   5274          *                 update.
   5275          */
   5276         public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
   5277             onBindViewHolder(holder, position);
   5278         }
   5279 
   5280         /**
   5281          * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
   5282          * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
   5283          *
   5284          * @see #onCreateViewHolder(ViewGroup, int)
   5285          */
   5286         public final VH createViewHolder(ViewGroup parent, int viewType) {
   5287             TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
   5288             final VH holder = onCreateViewHolder(parent, viewType);
   5289             holder.mItemViewType = viewType;
   5290             TraceCompat.endSection();
   5291             return holder;
   5292         }
   5293 
   5294         /**
   5295          * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
   5296          * {@link ViewHolder} contents with the item at the given position and also sets up some
   5297          * private fields to be used by RecyclerView.
   5298          *
   5299          * @see #onBindViewHolder(ViewHolder, int)
   5300          */
   5301         public final void bindViewHolder(VH holder, int position) {
   5302             holder.mPosition = position;
   5303             if (hasStableIds()) {
   5304                 holder.mItemId = getItemId(position);
   5305             }
   5306             holder.setFlags(ViewHolder.FLAG_BOUND,
   5307                     ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
   5308                             | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
   5309             TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
   5310             onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
   5311             holder.clearPayload();
   5312             TraceCompat.endSection();
   5313         }
   5314 
   5315         /**
   5316          * Return the view type of the item at <code>position</code> for the purposes
   5317          * of view recycling.
   5318          *
   5319          * <p>The default implementation of this method returns 0, making the assumption of
   5320          * a single view type for the adapter. Unlike ListView adapters, types need not
   5321          * be contiguous. Consider using id resources to uniquely identify item view types.
   5322          *
   5323          * @param position position to query
   5324          * @return integer value identifying the type of the view needed to represent the item at
   5325          *                 <code>position</code>. Type codes need not be contiguous.
   5326          */
   5327         public int getItemViewType(int position) {
   5328             return 0;
   5329         }
   5330 
   5331         /**
   5332          * Indicates whether each item in the data set can be represented with a unique identifier
   5333          * of type {@link java.lang.Long}.
   5334          *
   5335          * @param hasStableIds Whether items in data set have unique identifiers or not.
   5336          * @see #hasStableIds()
   5337          * @see #getItemId(int)
   5338          */
   5339         public void setHasStableIds(boolean hasStableIds) {
   5340             if (hasObservers()) {
   5341                 throw new IllegalStateException("Cannot change whether this adapter has " +
   5342                         "stable IDs while the adapter has registered observers.");
   5343             }
   5344             mHasStableIds = hasStableIds;
   5345         }
   5346 
   5347         /**
   5348          * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}
   5349          * would return false this method should return {@link #NO_ID}. The default implementation
   5350          * of this method returns {@link #NO_ID}.
   5351          *
   5352          * @param position Adapter position to query
   5353          * @return the stable ID of the item at position
   5354          */
   5355         public long getItemId(int position) {
   5356             return NO_ID;
   5357         }
   5358 
   5359         /**
   5360          * Returns the total number of items in the data set hold by the adapter.
   5361          *
   5362          * @return The total number of items in this adapter.
   5363          */
   5364         public abstract int getItemCount();
   5365 
   5366         /**
   5367          * Returns true if this adapter publishes a unique <code>long</code> value that can
   5368          * act as a key for the item at a given position in the data set. If that item is relocated
   5369          * in the data set, the ID returned for that item should be the same.
   5370          *
   5371          * @return true if this adapter's items have stable IDs
   5372          */
   5373         public final boolean hasStableIds() {
   5374             return mHasStableIds;
   5375         }
   5376 
   5377         /**
   5378          * Called when a view created by this adapter has been recycled.
   5379          *
   5380          * <p>A view is recycled when a {@link LayoutManager} decides that it no longer
   5381          * needs to be attached to its parent {@link RecyclerView}. This can be because it has
   5382          * fallen out of visibility or a set of cached views represented by views still
   5383          * attached to the parent RecyclerView. If an item view has large or expensive data
   5384          * bound to it such as large bitmaps, this may be a good place to release those
   5385          * resources.</p>
   5386          * <p>
   5387          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   5388          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   5389          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   5390          * its adapter position.
   5391          *
   5392          * @param holder The ViewHolder for the view being recycled
   5393          */
   5394         public void onViewRecycled(VH holder) {
   5395         }
   5396 
   5397         /**
   5398          * Called by the RecyclerView if a ViewHolder created by this Adapter cannot be recycled
   5399          * due to its transient state. Upon receiving this callback, Adapter can clear the
   5400          * animation(s) that effect the View's transient state and return <code>true</code> so that
   5401          * the View can be recycled. Keep in mind that the View in question is already removed from
   5402          * the RecyclerView.
   5403          * <p>
   5404          * In some cases, it is acceptable to recycle a View although it has transient state. Most
   5405          * of the time, this is a case where the transient state will be cleared in
   5406          * {@link #onBindViewHolder(ViewHolder, int)} call when View is rebound to a new position.
   5407          * For this reason, RecyclerView leaves the decision to the Adapter and uses the return
   5408          * value of this method to decide whether the View should be recycled or not.
   5409          * <p>
   5410          * Note that when all animations are created by {@link RecyclerView.ItemAnimator}, you
   5411          * should never receive this callback because RecyclerView keeps those Views as children
   5412          * until their animations are complete. This callback is useful when children of the item
   5413          * views create animations which may not be easy to implement using an {@link ItemAnimator}.
   5414          * <p>
   5415          * You should <em>never</em> fix this issue by calling
   5416          * <code>holder.itemView.setHasTransientState(false);</code> unless you've previously called
   5417          * <code>holder.itemView.setHasTransientState(true);</code>. Each
   5418          * <code>View.setHasTransientState(true)</code> call must be matched by a
   5419          * <code>View.setHasTransientState(false)</code> call, otherwise, the state of the View
   5420          * may become inconsistent. You should always prefer to end or cancel animations that are
   5421          * triggering the transient state instead of handling it manually.
   5422          *
   5423          * @param holder The ViewHolder containing the View that could not be recycled due to its
   5424          *               transient state.
   5425          * @return True if the View should be recycled, false otherwise. Note that if this method
   5426          * returns <code>true</code>, RecyclerView <em>will ignore</em> the transient state of
   5427          * the View and recycle it regardless. If this method returns <code>false</code>,
   5428          * RecyclerView will check the View's transient state again before giving a final decision.
   5429          * Default implementation returns false.
   5430          */
   5431         public boolean onFailedToRecycleView(VH holder) {
   5432             return false;
   5433         }
   5434 
   5435         /**
   5436          * Called when a view created by this adapter has been attached to a window.
   5437          *
   5438          * <p>This can be used as a reasonable signal that the view is about to be seen
   5439          * by the user. If the adapter previously freed any resources in
   5440          * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
   5441          * those resources should be restored here.</p>
   5442          *
   5443          * @param holder Holder of the view being attached
   5444          */
   5445         public void onViewAttachedToWindow(VH holder) {
   5446         }
   5447 
   5448         /**
   5449          * Called when a view created by this adapter has been detached from its window.
   5450          *
   5451          * <p>Becoming detached from the window is not necessarily a permanent condition;
   5452          * the consumer of an Adapter's views may choose to cache views offscreen while they
   5453          * are not visible, attaching an detaching them as appropriate.</p>
   5454          *
   5455          * @param holder Holder of the view being detached
   5456          */
   5457         public void onViewDetachedFromWindow(VH holder) {
   5458         }
   5459 
   5460         /**
   5461          * Returns true if one or more observers are attached to this adapter.
   5462          *
   5463          * @return true if this adapter has observers
   5464          */
   5465         public final boolean hasObservers() {
   5466             return mObservable.hasObservers();
   5467         }
   5468 
   5469         /**
   5470          * Register a new observer to listen for data changes.
   5471          *
   5472          * <p>The adapter may publish a variety of events describing specific changes.
   5473          * Not all adapters may support all change types and some may fall back to a generic
   5474          * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
   5475          * "something changed"} event if more specific data is not available.</p>
   5476          *
   5477          * <p>Components registering observers with an adapter are responsible for
   5478          * {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   5479          * unregistering} those observers when finished.</p>
   5480          *
   5481          * @param observer Observer to register
   5482          *
   5483          * @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
   5484          */
   5485         public void registerAdapterDataObserver(AdapterDataObserver observer) {
   5486             mObservable.registerObserver(observer);
   5487         }
   5488 
   5489         /**
   5490          * Unregister an observer currently listening for data changes.
   5491          *
   5492          * <p>The unregistered observer will no longer receive events about changes
   5493          * to the adapter.</p>
   5494          *
   5495          * @param observer Observer to unregister
   5496          *
   5497          * @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
   5498          */
   5499         public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
   5500             mObservable.unregisterObserver(observer);
   5501         }
   5502 
   5503         /**
   5504          * Called by RecyclerView when it starts observing this Adapter.
   5505          * <p>
   5506          * Keep in mind that same adapter may be observed by multiple RecyclerViews.
   5507          *
   5508          * @param recyclerView The RecyclerView instance which started observing this adapter.
   5509          * @see #onDetachedFromRecyclerView(RecyclerView)
   5510          */
   5511         public void onAttachedToRecyclerView(RecyclerView recyclerView) {
   5512         }
   5513 
   5514         /**
   5515          * Called by RecyclerView when it stops observing this Adapter.
   5516          *
   5517          * @param recyclerView The RecyclerView instance which stopped observing this adapter.
   5518          * @see #onAttachedToRecyclerView(RecyclerView)
   5519          */
   5520         public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
   5521         }
   5522 
   5523         /**
   5524          * Notify any registered observers that the data set has changed.
   5525          *
   5526          * <p>There are two different classes of data change events, item changes and structural
   5527          * changes. Item changes are when a single item has its data updated but no positional
   5528          * changes have occurred. Structural changes are when items are inserted, removed or moved
   5529          * within the data set.</p>
   5530          *
   5531          * <p>This event does not specify what about the data set has changed, forcing
   5532          * any observers to assume that all existing items and structure may no longer be valid.
   5533          * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
   5534          *
   5535          * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
   5536          * for adapters that report that they have {@link #hasStableIds() stable IDs} when
   5537          * this method is used. This can help for the purposes of animation and visual
   5538          * object persistence but individual item views will still need to be rebound
   5539          * and relaid out.</p>
   5540          *
   5541          * <p>If you are writing an adapter it will always be more efficient to use the more
   5542          * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
   5543          * as a last resort.</p>
   5544          *
   5545          * @see #notifyItemChanged(int)
   5546          * @see #notifyItemInserted(int)
   5547          * @see #notifyItemRemoved(int)
   5548          * @see #notifyItemRangeChanged(int, int)
   5549          * @see #notifyItemRangeInserted(int, int)
   5550          * @see #notifyItemRangeRemoved(int, int)
   5551          */
   5552         public final void notifyDataSetChanged() {
   5553             mObservable.notifyChanged();
   5554         }
   5555 
   5556         /**
   5557          * Notify any registered observers that the item at <code>position</code> has changed.
   5558          * Equivalent to calling <code>notifyItemChanged(position, null);</code>.
   5559          *
   5560          * <p>This is an item change event, not a structural change event. It indicates that any
   5561          * reflection of the data at <code>position</code> is out of date and should be updated.
   5562          * The item at <code>position</code> retains the same identity.</p>
   5563          *
   5564          * @param position Position of the item that has changed
   5565          *
   5566          * @see #notifyItemRangeChanged(int, int)
   5567          */
   5568         public final void notifyItemChanged(int position) {
   5569             mObservable.notifyItemRangeChanged(position, 1);
   5570         }
   5571 
   5572         /**
   5573          * Notify any registered observers that the item at <code>position</code> has changed with an
   5574          * optional payload object.
   5575          *
   5576          * <p>This is an item change event, not a structural change event. It indicates that any
   5577          * reflection of the data at <code>position</code> is out of date and should be updated.
   5578          * The item at <code>position</code> retains the same identity.
   5579          * </p>
   5580          *
   5581          * <p>
   5582          * Client can optionally pass a payload for partial change. These payloads will be merged
   5583          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   5584          * item is already represented by a ViewHolder and it will be rebound to the same
   5585          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   5586          * payloads on that item and prevent future payload until
   5587          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   5588          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   5589          * attached, the payload will be simply dropped.
   5590          *
   5591          * @param position Position of the item that has changed
   5592          * @param payload Optional parameter, use null to identify a "full" update
   5593          *
   5594          * @see #notifyItemRangeChanged(int, int)
   5595          */
   5596         public final void notifyItemChanged(int position, Object payload) {
   5597             mObservable.notifyItemRangeChanged(position, 1, payload);
   5598         }
   5599 
   5600         /**
   5601          * Notify any registered observers that the <code>itemCount</code> items starting at
   5602          * position <code>positionStart</code> have changed.
   5603          * Equivalent to calling <code>notifyItemRangeChanged(position, itemCount, null);</code>.
   5604          *
   5605          * <p>This is an item change event, not a structural change event. It indicates that
   5606          * any reflection of the data in the given position range is out of date and should
   5607          * be updated. The items in the given range retain the same identity.</p>
   5608          *
   5609          * @param positionStart Position of the first item that has changed
   5610          * @param itemCount Number of items that have changed
   5611          *
   5612          * @see #notifyItemChanged(int)
   5613          */
   5614         public final void notifyItemRangeChanged(int positionStart, int itemCount) {
   5615             mObservable.notifyItemRangeChanged(positionStart, itemCount);
   5616         }
   5617 
   5618         /**
   5619          * Notify any registered observers that the <code>itemCount</code> items starting at
   5620          * position<code>positionStart</code> have changed. An optional payload can be
   5621          * passed to each changed item.
   5622          *
   5623          * <p>This is an item change event, not a structural change event. It indicates that any
   5624          * reflection of the data in the given position range is out of date and should be updated.
   5625          * The items in the given range retain the same identity.
   5626          * </p>
   5627          *
   5628          * <p>
   5629          * Client can optionally pass a payload for partial change. These payloads will be merged
   5630          * and may be passed to adapter's {@link #onBindViewHolder(ViewHolder, int, List)} if the
   5631          * item is already represented by a ViewHolder and it will be rebound to the same
   5632          * ViewHolder. A notifyItemRangeChanged() with null payload will clear all existing
   5633          * payloads on that item and prevent future payload until
   5634          * {@link #onBindViewHolder(ViewHolder, int, List)} is called. Adapter should not assume
   5635          * that the payload will always be passed to onBindViewHolder(), e.g. when the view is not
   5636          * attached, the payload will be simply dropped.
   5637          *
   5638          * @param positionStart Position of the first item that has changed
   5639          * @param itemCount Number of items that have changed
   5640          * @param payload  Optional parameter, use null to identify a "full" update
   5641          *
   5642          * @see #notifyItemChanged(int)
   5643          */
   5644         public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
   5645             mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
   5646         }
   5647 
   5648         /**
   5649          * Notify any registered observers that the item reflected at <code>position</code>
   5650          * has been newly inserted. The item previously at <code>position</code> is now at
   5651          * position <code>position + 1</code>.
   5652          *
   5653          * <p>This is a structural change event. Representations of other existing items in the
   5654          * data set are still considered up to date and will not be rebound, though their
   5655          * positions may be altered.</p>
   5656          *
   5657          * @param position Position of the newly inserted item in the data set
   5658          *
   5659          * @see #notifyItemRangeInserted(int, int)
   5660          */
   5661         public final void notifyItemInserted(int position) {
   5662             mObservable.notifyItemRangeInserted(position, 1);
   5663         }
   5664 
   5665         /**
   5666          * Notify any registered observers that the item reflected at <code>fromPosition</code>
   5667          * has been moved to <code>toPosition</code>.
   5668          *
   5669          * <p>This is a structural change event. Representations of other existing items in the
   5670          * data set are still considered up to date and will not be rebound, though their
   5671          * positions may be altered.</p>
   5672          *
   5673          * @param fromPosition Previous position of the item.
   5674          * @param toPosition New position of the item.
   5675          */
   5676         public final void notifyItemMoved(int fromPosition, int toPosition) {
   5677             mObservable.notifyItemMoved(fromPosition, toPosition);
   5678         }
   5679 
   5680         /**
   5681          * Notify any registered observers that the currently reflected <code>itemCount</code>
   5682          * items starting at <code>positionStart</code> have been newly inserted. The items
   5683          * previously located at <code>positionStart</code> and beyond can now be found starting
   5684          * at position <code>positionStart + itemCount</code>.
   5685          *
   5686          * <p>This is a structural change event. Representations of other existing items in the
   5687          * data set are still considered up to date and will not be rebound, though their positions
   5688          * may be altered.</p>
   5689          *
   5690          * @param positionStart Position of the first item that was inserted
   5691          * @param itemCount Number of items inserted
   5692          *
   5693          * @see #notifyItemInserted(int)
   5694          */
   5695         public final void notifyItemRangeInserted(int positionStart, int itemCount) {
   5696             mObservable.notifyItemRangeInserted(positionStart, itemCount);
   5697         }
   5698 
   5699         /**
   5700          * Notify any registered observers that the item previously located at <code>position</code>
   5701          * has been removed from the data set. The items previously located at and after
   5702          * <code>position</code> may now be found at <code>oldPosition - 1</code>.
   5703          *
   5704          * <p>This is a structural change event. Representations of other existing items in the
   5705          * data set are still considered up to date and will not be rebound, though their positions
   5706          * may be altered.</p>
   5707          *
   5708          * @param position Position of the item that has now been removed
   5709          *
   5710          * @see #notifyItemRangeRemoved(int, int)
   5711          */
   5712         public final void notifyItemRemoved(int position) {
   5713             mObservable.notifyItemRangeRemoved(position, 1);
   5714         }
   5715 
   5716         /**
   5717          * Notify any registered observers that the <code>itemCount</code> items previously
   5718          * located at <code>positionStart</code> have been removed from the data set. The items
   5719          * previously located at and after <code>positionStart + itemCount</code> may now be found
   5720          * at <code>oldPosition - itemCount</code>.
   5721          *
   5722          * <p>This is a structural change event. Representations of other existing items in the data
   5723          * set are still considered up to date and will not be rebound, though their positions
   5724          * may be altered.</p>
   5725          *
   5726          * @param positionStart Previous position of the first item that was removed
   5727          * @param itemCount Number of items removed from the data set
   5728          */
   5729         public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
   5730             mObservable.notifyItemRangeRemoved(positionStart, itemCount);
   5731         }
   5732     }
   5733 
   5734     private void dispatchChildDetached(View child) {
   5735         final ViewHolder viewHolder = getChildViewHolderInt(child);
   5736         onChildDetachedFromWindow(child);
   5737         if (mAdapter != null && viewHolder != null) {
   5738             mAdapter.onViewDetachedFromWindow(viewHolder);
   5739         }
   5740         if (mOnChildAttachStateListeners != null) {
   5741             final int cnt = mOnChildAttachStateListeners.size();
   5742             for (int i = cnt - 1; i >= 0; i--) {
   5743                 mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
   5744             }
   5745         }
   5746     }
   5747 
   5748     private void dispatchChildAttached(View child) {
   5749         final ViewHolder viewHolder = getChildViewHolderInt(child);
   5750         onChildAttachedToWindow(child);
   5751         if (mAdapter != null && viewHolder != null) {
   5752             mAdapter.onViewAttachedToWindow(viewHolder);
   5753         }
   5754         if (mOnChildAttachStateListeners != null) {
   5755             final int cnt = mOnChildAttachStateListeners.size();
   5756             for (int i = cnt - 1; i >= 0; i--) {
   5757                 mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
   5758             }
   5759         }
   5760 
   5761     }
   5762 
   5763     /**
   5764      * A <code>LayoutManager</code> is responsible for measuring and positioning item views
   5765      * within a <code>RecyclerView</code> as well as determining the policy for when to recycle
   5766      * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>
   5767      * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
   5768      * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
   5769      * layout managers are provided for general use.
   5770      * <p/>
   5771      * If the LayoutManager specifies a default constructor or one with the signature
   5772      * ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
   5773      * instantiate and set the LayoutManager when being inflated. Most used properties can
   5774      * be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
   5775      * a LayoutManager specifies both constructors, the non-default constructor will take
   5776      * precedence.
   5777      *
   5778      */
   5779     public static abstract class LayoutManager {
   5780         ChildHelper mChildHelper;
   5781         RecyclerView mRecyclerView;
   5782 
   5783         @Nullable
   5784         SmoothScroller mSmoothScroller;
   5785 
   5786         private boolean mRequestedSimpleAnimations = false;
   5787 
   5788         private boolean mIsAttachedToWindow = false;
   5789 
   5790         void setRecyclerView(RecyclerView recyclerView) {
   5791             if (recyclerView == null) {
   5792                 mRecyclerView = null;
   5793                 mChildHelper = null;
   5794             } else {
   5795                 mRecyclerView = recyclerView;
   5796                 mChildHelper = recyclerView.mChildHelper;
   5797             }
   5798 
   5799         }
   5800 
   5801         /**
   5802          * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
   5803          */
   5804         public void requestLayout() {
   5805             if(mRecyclerView != null) {
   5806                 mRecyclerView.requestLayout();
   5807             }
   5808         }
   5809 
   5810         /**
   5811          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   5812          * {@link IllegalStateException} if it <b>is not</b>.
   5813          *
   5814          * @param message The message for the exception. Can be null.
   5815          * @see #assertNotInLayoutOrScroll(String)
   5816          */
   5817         public void assertInLayoutOrScroll(String message) {
   5818             if (mRecyclerView != null) {
   5819                 mRecyclerView.assertInLayoutOrScroll(message);
   5820             }
   5821         }
   5822 
   5823         /**
   5824          * Checks if RecyclerView is in the middle of a layout or scroll and throws an
   5825          * {@link IllegalStateException} if it <b>is</b>.
   5826          *
   5827          * @param message The message for the exception. Can be null.
   5828          * @see #assertInLayoutOrScroll(String)
   5829          */
   5830         public void assertNotInLayoutOrScroll(String message) {
   5831             if (mRecyclerView != null) {
   5832                 mRecyclerView.assertNotInLayoutOrScroll(message);
   5833             }
   5834         }
   5835 
   5836         /**
   5837          * Returns whether this LayoutManager supports automatic item animations.
   5838          * A LayoutManager wishing to support item animations should obey certain
   5839          * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
   5840          * The default return value is <code>false</code>, so subclasses of LayoutManager
   5841          * will not get predictive item animations by default.
   5842          *
   5843          * <p>Whether item animations are enabled in a RecyclerView is determined both
   5844          * by the return value from this method and the
   5845          * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
   5846          * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
   5847          * method returns false, then simple item animations will be enabled, in which
   5848          * views that are moving onto or off of the screen are simply faded in/out. If
   5849          * the RecyclerView has a non-null ItemAnimator and this method returns true,
   5850          * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
   5851          * setup up the information needed to more intelligently predict where appearing
   5852          * and disappearing views should be animated from/to.</p>
   5853          *
   5854          * @return true if predictive item animations should be enabled, false otherwise
   5855          */
   5856         public boolean supportsPredictiveItemAnimations() {
   5857             return false;
   5858         }
   5859 
   5860         void dispatchAttachedToWindow(RecyclerView view) {
   5861             mIsAttachedToWindow = true;
   5862             onAttachedToWindow(view);
   5863         }
   5864 
   5865         void dispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
   5866             mIsAttachedToWindow = false;
   5867             onDetachedFromWindow(view, recycler);
   5868         }
   5869 
   5870         /**
   5871          * Returns whether LayoutManager is currently attached to a RecyclerView which is attached
   5872          * to a window.
   5873          *
   5874          * @return True if this LayoutManager is controlling a RecyclerView and the RecyclerView
   5875          * is attached to window.
   5876          */
   5877         public boolean isAttachedToWindow() {
   5878             return mIsAttachedToWindow;
   5879         }
   5880 
   5881         /**
   5882          * Causes the Runnable to execute on the next animation time step.
   5883          * The runnable will be run on the user interface thread.
   5884          * <p>
   5885          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   5886          *
   5887          * @param action The Runnable that will be executed.
   5888          *
   5889          * @see #removeCallbacks
   5890          */
   5891         public void postOnAnimation(Runnable action) {
   5892             if (mRecyclerView != null) {
   5893                 ViewCompat.postOnAnimation(mRecyclerView, action);
   5894             }
   5895         }
   5896 
   5897         /**
   5898          * Removes the specified Runnable from the message queue.
   5899          * <p>
   5900          * Calling this method when LayoutManager is not attached to a RecyclerView has no effect.
   5901          *
   5902          * @param action The Runnable to remove from the message handling queue
   5903          *
   5904          * @return true if RecyclerView could ask the Handler to remove the Runnable,
   5905          *         false otherwise. When the returned value is true, the Runnable
   5906          *         may or may not have been actually removed from the message queue
   5907          *         (for instance, if the Runnable was not in the queue already.)
   5908          *
   5909          * @see #postOnAnimation
   5910          */
   5911         public boolean removeCallbacks(Runnable action) {
   5912             if (mRecyclerView != null) {
   5913                 return mRecyclerView.removeCallbacks(action);
   5914             }
   5915             return false;
   5916         }
   5917         /**
   5918          * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
   5919          * is attached to a window.
   5920          *
   5921          * <p>Subclass implementations should always call through to the superclass implementation.
   5922          * </p>
   5923          *
   5924          * @param view The RecyclerView this LayoutManager is bound to
   5925          */
   5926         @CallSuper
   5927         public void onAttachedToWindow(RecyclerView view) {
   5928         }
   5929 
   5930         /**
   5931          * @deprecated
   5932          * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
   5933          */
   5934         @Deprecated
   5935         public void onDetachedFromWindow(RecyclerView view) {
   5936 
   5937         }
   5938 
   5939         /**
   5940          * Called when this LayoutManager is detached from its parent RecyclerView or when
   5941          * its parent RecyclerView is detached from its window.
   5942          *
   5943          * <p>Subclass implementations should always call through to the superclass implementation.
   5944          * </p>
   5945          *
   5946          * @param view The RecyclerView this LayoutManager is bound to
   5947          * @param recycler The recycler to use if you prefer to recycle your children instead of
   5948          *                 keeping them around.
   5949          */
   5950         @CallSuper
   5951         public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
   5952             onDetachedFromWindow(view);
   5953         }
   5954 
   5955         /**
   5956          * Check if the RecyclerView is configured to clip child views to its padding.
   5957          *
   5958          * @return true if this RecyclerView clips children to its padding, false otherwise
   5959          */
   5960         public boolean getClipToPadding() {
   5961             return mRecyclerView != null && mRecyclerView.mClipToPadding;
   5962         }
   5963 
   5964         /**
   5965          * Lay out all relevant child views from the given adapter.
   5966          *
   5967          * The LayoutManager is in charge of the behavior of item animations. By default,
   5968          * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
   5969          * item animations are enabled. This means that add/remove operations on the
   5970          * adapter will result in animations to add new or appearing items, removed or
   5971          * disappearing items, and moved items. If a LayoutManager returns false from
   5972          * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
   5973          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
   5974          * RecyclerView will have enough information to run those animations in a simple
   5975          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
   5976          * simply fade views in and out, whether they are actually added/removed or whether
   5977          * they are moved on or off the screen due to other add/remove operations.
   5978          *
   5979          * <p>A LayoutManager wanting a better item animation experience, where items can be
   5980          * animated onto and off of the screen according to where the items exist when they
   5981          * are not on screen, then the LayoutManager should return true from
   5982          * {@link #supportsPredictiveItemAnimations()} and add additional logic to
   5983          * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
   5984          * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
   5985          * once as a "pre" layout step to determine where items would have been prior to
   5986          * a real layout, and again to do the "real" layout. In the pre-layout phase,
   5987          * items will remember their pre-layout positions to allow them to be laid out
   5988          * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
   5989          * be returned from the scrap to help determine correct placement of other items.
   5990          * These removed items should not be added to the child list, but should be used
   5991          * to help calculate correct positioning of other views, including views that
   5992          * were not previously onscreen (referred to as APPEARING views), but whose
   5993          * pre-layout offscreen position can be determined given the extra
   5994          * information about the pre-layout removed views.</p>
   5995          *
   5996          * <p>The second layout pass is the real layout in which only non-removed views
   5997          * will be used. The only additional requirement during this pass is, if
   5998          * {@link #supportsPredictiveItemAnimations()} returns true, to note which
   5999          * views exist in the child list prior to layout and which are not there after
   6000          * layout (referred to as DISAPPEARING views), and to position/layout those views
   6001          * appropriately, without regard to the actual bounds of the RecyclerView. This allows
   6002          * the animation system to know the location to which to animate these disappearing
   6003          * views.</p>
   6004          *
   6005          * <p>The default LayoutManager implementations for RecyclerView handle all of these
   6006          * requirements for animations already. Clients of RecyclerView can either use one
   6007          * of these layout managers directly or look at their implementations of
   6008          * onLayoutChildren() to see how they account for the APPEARING and
   6009          * DISAPPEARING views.</p>
   6010          *
   6011          * @param recycler         Recycler to use for fetching potentially cached views for a
   6012          *                         position
   6013          * @param state            Transient state of RecyclerView
   6014          */
   6015         public void onLayoutChildren(Recycler recycler, State state) {
   6016             Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
   6017         }
   6018 
   6019         /**
   6020          * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.
   6021          *
   6022          * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type
   6023          * to store extra information specific to the layout. Client code should subclass
   6024          * {@link RecyclerView.LayoutParams} for this purpose.</p>
   6025          *
   6026          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   6027          * you must also override
   6028          * {@link #checkLayoutParams(LayoutParams)},
   6029          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   6030          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   6031          *
   6032          * @return A new LayoutParams for a child view
   6033          */
   6034         public abstract LayoutParams generateDefaultLayoutParams();
   6035 
   6036         /**
   6037          * Determines the validity of the supplied LayoutParams object.
   6038          *
   6039          * <p>This should check to make sure that the object is of the correct type
   6040          * and all values are within acceptable ranges. The default implementation
   6041          * returns <code>true</code> for non-null params.</p>
   6042          *
   6043          * @param lp LayoutParams object to check
   6044          * @return true if this LayoutParams object is valid, false otherwise
   6045          */
   6046         public boolean checkLayoutParams(LayoutParams lp) {
   6047             return lp != null;
   6048         }
   6049 
   6050         /**
   6051          * Create a LayoutParams object suitable for this LayoutManager, copying relevant
   6052          * values from the supplied LayoutParams object if possible.
   6053          *
   6054          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   6055          * you must also override
   6056          * {@link #checkLayoutParams(LayoutParams)},
   6057          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   6058          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   6059          *
   6060          * @param lp Source LayoutParams object to copy values from
   6061          * @return a new LayoutParams object
   6062          */
   6063         public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
   6064             if (lp instanceof LayoutParams) {
   6065                 return new LayoutParams((LayoutParams) lp);
   6066             } else if (lp instanceof MarginLayoutParams) {
   6067                 return new LayoutParams((MarginLayoutParams) lp);
   6068             } else {
   6069                 return new LayoutParams(lp);
   6070             }
   6071         }
   6072 
   6073         /**
   6074          * Create a LayoutParams object suitable for this LayoutManager from
   6075          * an inflated layout resource.
   6076          *
   6077          * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type
   6078          * you must also override
   6079          * {@link #checkLayoutParams(LayoutParams)},
   6080          * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and
   6081          * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>
   6082          *
   6083          * @param c Context for obtaining styled attributes
   6084          * @param attrs AttributeSet describing the supplied arguments
   6085          * @return a new LayoutParams object
   6086          */
   6087         public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
   6088             return new LayoutParams(c, attrs);
   6089         }
   6090 
   6091         /**
   6092          * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
   6093          * The default implementation does nothing and returns 0.
   6094          *
   6095          * @param dx            distance to scroll by in pixels. X increases as scroll position
   6096          *                      approaches the right.
   6097          * @param recycler      Recycler to use for fetching potentially cached views for a
   6098          *                      position
   6099          * @param state         Transient state of RecyclerView
   6100          * @return The actual distance scrolled. The return value will be negative if dx was
   6101          * negative and scrolling proceeeded in that direction.
   6102          * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.
   6103          */
   6104         public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
   6105             return 0;
   6106         }
   6107 
   6108         /**
   6109          * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
   6110          * The default implementation does nothing and returns 0.
   6111          *
   6112          * @param dy            distance to scroll in pixels. Y increases as scroll position
   6113          *                      approaches the bottom.
   6114          * @param recycler      Recycler to use for fetching potentially cached views for a
   6115          *                      position
   6116          * @param state         Transient state of RecyclerView
   6117          * @return The actual distance scrolled. The return value will be negative if dy was
   6118          * negative and scrolling proceeeded in that direction.
   6119          * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.
   6120          */
   6121         public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
   6122             return 0;
   6123         }
   6124 
   6125         /**
   6126          * Query if horizontal scrolling is currently supported. The default implementation
   6127          * returns false.
   6128          *
   6129          * @return True if this LayoutManager can scroll the current contents horizontally
   6130          */
   6131         public boolean canScrollHorizontally() {
   6132             return false;
   6133         }
   6134 
   6135         /**
   6136          * Query if vertical scrolling is currently supported. The default implementation
   6137          * returns false.
   6138          *
   6139          * @return True if this LayoutManager can scroll the current contents vertically
   6140          */
   6141         public boolean canScrollVertically() {
   6142             return false;
   6143         }
   6144 
   6145         /**
   6146          * Scroll to the specified adapter position.
   6147          *
   6148          * Actual position of the item on the screen depends on the LayoutManager implementation.
   6149          * @param position Scroll to this adapter position.
   6150          */
   6151         public void scrollToPosition(int position) {
   6152             if (DEBUG) {
   6153                 Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
   6154             }
   6155         }
   6156 
   6157         /**
   6158          * <p>Smooth scroll to the specified adapter position.</p>
   6159          * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}
   6160          * instance and call {@link #startSmoothScroll(SmoothScroller)}.
   6161          * </p>
   6162          * @param recyclerView The RecyclerView to which this layout manager is attached
   6163          * @param state    Current State of RecyclerView
   6164          * @param position Scroll to this adapter position.
   6165          */
   6166         public void smoothScrollToPosition(RecyclerView recyclerView, State state,
   6167                 int position) {
   6168             Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
   6169         }
   6170 
   6171         /**
   6172          * <p>Starts a smooth scroll using the provided SmoothScroller.</p>
   6173          * <p>Calling this method will cancel any previous smooth scroll request.</p>
   6174          * @param smoothScroller Unstance which defines how smooth scroll should be animated
   6175          */
   6176         public void startSmoothScroll(SmoothScroller smoothScroller) {
   6177             if (mSmoothScroller != null && smoothScroller != mSmoothScroller
   6178                     && mSmoothScroller.isRunning()) {
   6179                 mSmoothScroller.stop();
   6180             }
   6181             mSmoothScroller = smoothScroller;
   6182             mSmoothScroller.start(mRecyclerView, this);
   6183         }
   6184 
   6185         /**
   6186          * @return true if RecycylerView is currently in the state of smooth scrolling.
   6187          */
   6188         public boolean isSmoothScrolling() {
   6189             return mSmoothScroller != null && mSmoothScroller.isRunning();
   6190         }
   6191 
   6192 
   6193         /**
   6194          * Returns the resolved layout direction for this RecyclerView.
   6195          *
   6196          * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
   6197          * direction is RTL or returns
   6198          * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
   6199          * is not RTL.
   6200          */
   6201         public int getLayoutDirection() {
   6202             return ViewCompat.getLayoutDirection(mRecyclerView);
   6203         }
   6204 
   6205         /**
   6206          * Ends all animations on the view created by the {@link ItemAnimator}.
   6207          *
   6208          * @param view The View for which the animations should be ended.
   6209          * @see RecyclerView.ItemAnimator#endAnimations()
   6210          */
   6211         public void endAnimation(View view) {
   6212             if (mRecyclerView.mItemAnimator != null) {
   6213                 mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
   6214             }
   6215         }
   6216 
   6217         /**
   6218          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   6219          * to the layout that is known to be going away, either because it has been
   6220          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   6221          * visible portion of the container but is being laid out in order to inform RecyclerView
   6222          * in how to animate the item out of view.
   6223          * <p>
   6224          * Views added via this method are going to be invisible to LayoutManager after the
   6225          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   6226          * or won't be included in {@link #getChildCount()} method.
   6227          *
   6228          * @param child View to add and then remove with animation.
   6229          */
   6230         public void addDisappearingView(View child) {
   6231             addDisappearingView(child, -1);
   6232         }
   6233 
   6234         /**
   6235          * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
   6236          * to the layout that is known to be going away, either because it has been
   6237          * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
   6238          * visible portion of the container but is being laid out in order to inform RecyclerView
   6239          * in how to animate the item out of view.
   6240          * <p>
   6241          * Views added via this method are going to be invisible to LayoutManager after the
   6242          * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
   6243          * or won't be included in {@link #getChildCount()} method.
   6244          *
   6245          * @param child View to add and then remove with animation.
   6246          * @param index Index of the view.
   6247          */
   6248         public void addDisappearingView(View child, int index) {
   6249             addViewInt(child, index, true);
   6250         }
   6251 
   6252         /**
   6253          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   6254          * use this method to add views obtained from a {@link Recycler} using
   6255          * {@link Recycler#getViewForPosition(int)}.
   6256          *
   6257          * @param child View to add
   6258          */
   6259         public void addView(View child) {
   6260             addView(child, -1);
   6261         }
   6262 
   6263         /**
   6264          * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
   6265          * use this method to add views obtained from a {@link Recycler} using
   6266          * {@link Recycler#getViewForPosition(int)}.
   6267          *
   6268          * @param child View to add
   6269          * @param index Index to add child at
   6270          */
   6271         public void addView(View child, int index) {
   6272             addViewInt(child, index, false);
   6273         }
   6274 
   6275         private void addViewInt(View child, int index, boolean disappearing) {
   6276             final ViewHolder holder = getChildViewHolderInt(child);
   6277             if (disappearing || holder.isRemoved()) {
   6278                 // these views will be hidden at the end of the layout pass.
   6279                 mRecyclerView.mState.addToDisappearingList(child);
   6280             } else {
   6281                 // This may look like unnecessary but may happen if layout manager supports
   6282                 // predictive layouts and adapter removed then re-added the same item.
   6283                 // In this case, added version will be visible in the post layout (because add is
   6284                 // deferred) but RV will still bind it to the same View.
   6285                 // So if a View re-appears in post layout pass, remove it from disappearing list.
   6286                 mRecyclerView.mState.removeFromDisappearingList(child);
   6287             }
   6288             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   6289             if (holder.wasReturnedFromScrap() || holder.isScrap()) {
   6290                 if (holder.isScrap()) {
   6291                     holder.unScrap();
   6292                 } else {
   6293                     holder.clearReturnedFromScrapFlag();
   6294                 }
   6295                 mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
   6296                 if (DISPATCH_TEMP_DETACH) {
   6297                     ViewCompat.dispatchFinishTemporaryDetach(child);
   6298                 }
   6299             } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
   6300                 // ensure in correct position
   6301                 int currentIndex = mChildHelper.indexOfChild(child);
   6302                 if (index == -1) {
   6303                     index = mChildHelper.getChildCount();
   6304                 }
   6305                 if (currentIndex == -1) {
   6306                     throw new IllegalStateException("Added View has RecyclerView as parent but"
   6307                             + " view is not a real child. Unfiltered index:"
   6308                             + mRecyclerView.indexOfChild(child));
   6309                 }
   6310                 if (currentIndex != index) {
   6311                     mRecyclerView.mLayout.moveView(currentIndex, index);
   6312                 }
   6313             } else {
   6314                 mChildHelper.addView(child, index, false);
   6315                 lp.mInsetsDirty = true;
   6316                 if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
   6317                     mSmoothScroller.onChildAttachedToWindow(child);
   6318                 }
   6319             }
   6320             if (lp.mPendingInvalidate) {
   6321                 if (DEBUG) {
   6322                     Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
   6323                 }
   6324                 holder.itemView.invalidate();
   6325                 lp.mPendingInvalidate = false;
   6326             }
   6327         }
   6328 
   6329         /**
   6330          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   6331          * use this method to completely remove a child view that is no longer needed.
   6332          * LayoutManagers should strongly consider recycling removed views using
   6333          * {@link Recycler#recycleView(android.view.View)}.
   6334          *
   6335          * @param child View to remove
   6336          */
   6337         public void removeView(View child) {
   6338             mChildHelper.removeView(child);
   6339         }
   6340 
   6341         /**
   6342          * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
   6343          * use this method to completely remove a child view that is no longer needed.
   6344          * LayoutManagers should strongly consider recycling removed views using
   6345          * {@link Recycler#recycleView(android.view.View)}.
   6346          *
   6347          * @param index Index of the child view to remove
   6348          */
   6349         public void removeViewAt(int index) {
   6350             final View child = getChildAt(index);
   6351             if (child != null) {
   6352                 mChildHelper.removeViewAt(index);
   6353             }
   6354         }
   6355 
   6356         /**
   6357          * Remove all views from the currently attached RecyclerView. This will not recycle
   6358          * any of the affected views; the LayoutManager is responsible for doing so if desired.
   6359          */
   6360         public void removeAllViews() {
   6361             // Only remove non-animating views
   6362             final int childCount = getChildCount();
   6363             for (int i = childCount - 1; i >= 0; i--) {
   6364                 mChildHelper.removeViewAt(i);
   6365             }
   6366         }
   6367 
   6368         /**
   6369          * Returns offset of the RecyclerView's text baseline from the its top boundary.
   6370          *
   6371          * @return The offset of the RecyclerView's text baseline from the its top boundary; -1 if
   6372          * there is no baseline.
   6373          */
   6374         public int getBaseline() {
   6375             return -1;
   6376         }
   6377 
   6378         /**
   6379          * Returns the adapter position of the item represented by the given View. This does not
   6380          * contain any adapter changes that might have happened after the last layout.
   6381          *
   6382          * @param view The view to query
   6383          * @return The adapter position of the item which is rendered by this View.
   6384          */
   6385         public int getPosition(View view) {
   6386             return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
   6387         }
   6388 
   6389         /**
   6390          * Returns the View type defined by the adapter.
   6391          *
   6392          * @param view The view to query
   6393          * @return The type of the view assigned by the adapter.
   6394          */
   6395         public int getItemViewType(View view) {
   6396             return getChildViewHolderInt(view).getItemViewType();
   6397         }
   6398 
   6399         /**
   6400          * Finds the view which represents the given adapter position.
   6401          * <p>
   6402          * This method traverses each child since it has no information about child order.
   6403          * Override this method to improve performance if your LayoutManager keeps data about
   6404          * child views.
   6405          * <p>
   6406          * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
   6407          *
   6408          * @param position Position of the item in adapter
   6409          * @return The child view that represents the given position or null if the position is not
   6410          * laid out
   6411          */
   6412         public View findViewByPosition(int position) {
   6413             final int childCount = getChildCount();
   6414             for (int i = 0; i < childCount; i++) {
   6415                 View child = getChildAt(i);
   6416                 ViewHolder vh = getChildViewHolderInt(child);
   6417                 if (vh == null) {
   6418                     continue;
   6419                 }
   6420                 if (vh.getLayoutPosition() == position && !vh.shouldIgnore() &&
   6421                         (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
   6422                     return child;
   6423                 }
   6424             }
   6425             return null;
   6426         }
   6427 
   6428         /**
   6429          * Temporarily detach a child view.
   6430          *
   6431          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   6432          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   6433          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   6434          * so that the detached view may be rebound and reused.</p>
   6435          *
   6436          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   6437          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   6438          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   6439          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   6440          *
   6441          * @param child Child to detach
   6442          */
   6443         public void detachView(View child) {
   6444             final int ind = mChildHelper.indexOfChild(child);
   6445             if (ind >= 0) {
   6446                 detachViewInternal(ind, child);
   6447             }
   6448         }
   6449 
   6450         /**
   6451          * Temporarily detach a child view.
   6452          *
   6453          * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange
   6454          * views currently attached to the RecyclerView. Generally LayoutManager implementations
   6455          * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}
   6456          * so that the detached view may be rebound and reused.</p>
   6457          *
   6458          * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>
   6459          * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}
   6460          * or {@link #removeDetachedView(android.view.View) fully remove} the detached view
   6461          * before the LayoutManager entry point method called by RecyclerView returns.</p>
   6462          *
   6463          * @param index Index of the child to detach
   6464          */
   6465         public void detachViewAt(int index) {
   6466             detachViewInternal(index, getChildAt(index));
   6467         }
   6468 
   6469         private void detachViewInternal(int index, View view) {
   6470             if (DISPATCH_TEMP_DETACH) {
   6471                 ViewCompat.dispatchStartTemporaryDetach(view);
   6472             }
   6473             mChildHelper.detachViewFromParent(index);
   6474         }
   6475 
   6476         /**
   6477          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   6478          * This method should not be used to reattach views that were previously
   6479          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   6480          *
   6481          * @param child Child to reattach
   6482          * @param index Intended child index for child
   6483          * @param lp LayoutParams for child
   6484          */
   6485         public void attachView(View child, int index, LayoutParams lp) {
   6486             ViewHolder vh = getChildViewHolderInt(child);
   6487             if (vh.isRemoved()) {
   6488                 mRecyclerView.mState.addToDisappearingList(child);
   6489             } else {
   6490                 mRecyclerView.mState.removeFromDisappearingList(child);
   6491             }
   6492             mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
   6493             if (DISPATCH_TEMP_DETACH)  {
   6494                 ViewCompat.dispatchFinishTemporaryDetach(child);
   6495             }
   6496         }
   6497 
   6498         /**
   6499          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   6500          * This method should not be used to reattach views that were previously
   6501          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   6502          *
   6503          * @param child Child to reattach
   6504          * @param index Intended child index for child
   6505          */
   6506         public void attachView(View child, int index) {
   6507             attachView(child, index, (LayoutParams) child.getLayoutParams());
   6508         }
   6509 
   6510         /**
   6511          * Reattach a previously {@link #detachView(android.view.View) detached} view.
   6512          * This method should not be used to reattach views that were previously
   6513          * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.
   6514          *
   6515          * @param child Child to reattach
   6516          */
   6517         public void attachView(View child) {
   6518             attachView(child, -1);
   6519         }
   6520 
   6521         /**
   6522          * Finish removing a view that was previously temporarily
   6523          * {@link #detachView(android.view.View) detached}.
   6524          *
   6525          * @param child Detached child to remove
   6526          */
   6527         public void removeDetachedView(View child) {
   6528             mRecyclerView.removeDetachedView(child, false);
   6529         }
   6530 
   6531         /**
   6532          * Moves a View from one position to another.
   6533          *
   6534          * @param fromIndex The View's initial index
   6535          * @param toIndex The View's target index
   6536          */
   6537         public void moveView(int fromIndex, int toIndex) {
   6538             View view = getChildAt(fromIndex);
   6539             if (view == null) {
   6540                 throw new IllegalArgumentException("Cannot move a child from non-existing index:"
   6541                         + fromIndex);
   6542             }
   6543             detachViewAt(fromIndex);
   6544             attachView(view, toIndex);
   6545         }
   6546 
   6547         /**
   6548          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
   6549          *
   6550          * <p>Scrapping a view allows it to be rebound and reused to show updated or
   6551          * different data.</p>
   6552          *
   6553          * @param child Child to detach and scrap
   6554          * @param recycler Recycler to deposit the new scrap view into
   6555          */
   6556         public void detachAndScrapView(View child, Recycler recycler) {
   6557             int index = mChildHelper.indexOfChild(child);
   6558             scrapOrRecycleView(recycler, index, child);
   6559         }
   6560 
   6561         /**
   6562          * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
   6563          *
   6564          * <p>Scrapping a view allows it to be rebound and reused to show updated or
   6565          * different data.</p>
   6566          *
   6567          * @param index Index of child to detach and scrap
   6568          * @param recycler Recycler to deposit the new scrap view into
   6569          */
   6570         public void detachAndScrapViewAt(int index, Recycler recycler) {
   6571             final View child = getChildAt(index);
   6572             scrapOrRecycleView(recycler, index, child);
   6573         }
   6574 
   6575         /**
   6576          * Remove a child view and recycle it using the given Recycler.
   6577          *
   6578          * @param child Child to remove and recycle
   6579          * @param recycler Recycler to use to recycle child
   6580          */
   6581         public void removeAndRecycleView(View child, Recycler recycler) {
   6582             removeView(child);
   6583             recycler.recycleView(child);
   6584         }
   6585 
   6586         /**
   6587          * Remove a child view and recycle it using the given Recycler.
   6588          *
   6589          * @param index Index of child to remove and recycle
   6590          * @param recycler Recycler to use to recycle child
   6591          */
   6592         public void removeAndRecycleViewAt(int index, Recycler recycler) {
   6593             final View view = getChildAt(index);
   6594             removeViewAt(index);
   6595             recycler.recycleView(view);
   6596         }
   6597 
   6598         /**
   6599          * Return the current number of child views attached to the parent RecyclerView.
   6600          * This does not include child views that were temporarily detached and/or scrapped.
   6601          *
   6602          * @return Number of attached children
   6603          */
   6604         public int getChildCount() {
   6605             return mChildHelper != null ? mChildHelper.getChildCount() : 0;
   6606         }
   6607 
   6608         /**
   6609          * Return the child view at the given index
   6610          * @param index Index of child to return
   6611          * @return Child view at index
   6612          */
   6613         public View getChildAt(int index) {
   6614             return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
   6615         }
   6616 
   6617         /**
   6618          * Return the width of the parent RecyclerView
   6619          *
   6620          * @return Width in pixels
   6621          */
   6622         public int getWidth() {
   6623             return mRecyclerView != null ? mRecyclerView.getWidth() : 0;
   6624         }
   6625 
   6626         /**
   6627          * Return the height of the parent RecyclerView
   6628          *
   6629          * @return Height in pixels
   6630          */
   6631         public int getHeight() {
   6632             return mRecyclerView != null ? mRecyclerView.getHeight() : 0;
   6633         }
   6634 
   6635         /**
   6636          * Return the left padding of the parent RecyclerView
   6637          *
   6638          * @return Padding in pixels
   6639          */
   6640         public int getPaddingLeft() {
   6641             return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
   6642         }
   6643 
   6644         /**
   6645          * Return the top padding of the parent RecyclerView
   6646          *
   6647          * @return Padding in pixels
   6648          */
   6649         public int getPaddingTop() {
   6650             return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
   6651         }
   6652 
   6653         /**
   6654          * Return the right padding of the parent RecyclerView
   6655          *
   6656          * @return Padding in pixels
   6657          */
   6658         public int getPaddingRight() {
   6659             return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
   6660         }
   6661 
   6662         /**
   6663          * Return the bottom padding of the parent RecyclerView
   6664          *
   6665          * @return Padding in pixels
   6666          */
   6667         public int getPaddingBottom() {
   6668             return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
   6669         }
   6670 
   6671         /**
   6672          * Return the start padding of the parent RecyclerView
   6673          *
   6674          * @return Padding in pixels
   6675          */
   6676         public int getPaddingStart() {
   6677             return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
   6678         }
   6679 
   6680         /**
   6681          * Return the end padding of the parent RecyclerView
   6682          *
   6683          * @return Padding in pixels
   6684          */
   6685         public int getPaddingEnd() {
   6686             return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
   6687         }
   6688 
   6689         /**
   6690          * Returns true if the RecyclerView this LayoutManager is bound to has focus.
   6691          *
   6692          * @return True if the RecyclerView has focus, false otherwise.
   6693          * @see View#isFocused()
   6694          */
   6695         public boolean isFocused() {
   6696             return mRecyclerView != null && mRecyclerView.isFocused();
   6697         }
   6698 
   6699         /**
   6700          * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
   6701          *
   6702          * @return true if the RecyclerView has or contains focus
   6703          * @see View#hasFocus()
   6704          */
   6705         public boolean hasFocus() {
   6706             return mRecyclerView != null && mRecyclerView.hasFocus();
   6707         }
   6708 
   6709         /**
   6710          * Returns the item View which has or contains focus.
   6711          *
   6712          * @return A direct child of RecyclerView which has focus or contains the focused child.
   6713          */
   6714         public View getFocusedChild() {
   6715             if (mRecyclerView == null) {
   6716                 return null;
   6717             }
   6718             final View focused = mRecyclerView.getFocusedChild();
   6719             if (focused == null || mChildHelper.isHidden(focused)) {
   6720                 return null;
   6721             }
   6722             return focused;
   6723         }
   6724 
   6725         /**
   6726          * Returns the number of items in the adapter bound to the parent RecyclerView.
   6727          * <p>
   6728          * Note that this number is not necessarily equal to {@link State#getItemCount()}. In
   6729          * methods where State is available, you should use {@link State#getItemCount()} instead.
   6730          * For more details, check the documentation for {@link State#getItemCount()}.
   6731          *
   6732          * @return The number of items in the bound adapter
   6733          * @see State#getItemCount()
   6734          */
   6735         public int getItemCount() {
   6736             final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
   6737             return a != null ? a.getItemCount() : 0;
   6738         }
   6739 
   6740         /**
   6741          * Offset all child views attached to the parent RecyclerView by dx pixels along
   6742          * the horizontal axis.
   6743          *
   6744          * @param dx Pixels to offset by
   6745          */
   6746         public void offsetChildrenHorizontal(int dx) {
   6747             if (mRecyclerView != null) {
   6748                 mRecyclerView.offsetChildrenHorizontal(dx);
   6749             }
   6750         }
   6751 
   6752         /**
   6753          * Offset all child views attached to the parent RecyclerView by dy pixels along
   6754          * the vertical axis.
   6755          *
   6756          * @param dy Pixels to offset by
   6757          */
   6758         public void offsetChildrenVertical(int dy) {
   6759             if (mRecyclerView != null) {
   6760                 mRecyclerView.offsetChildrenVertical(dy);
   6761             }
   6762         }
   6763 
   6764         /**
   6765          * Flags a view so that it will not be scrapped or recycled.
   6766          * <p>
   6767          * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
   6768          * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
   6769          * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
   6770          * ignore the child.
   6771          * <p>
   6772          * Before this child can be recycled again, you have to call
   6773          * {@link #stopIgnoringView(View)}.
   6774          * <p>
   6775          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   6776          *
   6777          * @param view View to ignore.
   6778          * @see #stopIgnoringView(View)
   6779          */
   6780         public void ignoreView(View view) {
   6781             if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
   6782                 // checking this because calling this method on a recycled or detached view may
   6783                 // cause loss of state.
   6784                 throw new IllegalArgumentException("View should be fully attached to be ignored");
   6785             }
   6786             final ViewHolder vh = getChildViewHolderInt(view);
   6787             vh.addFlags(ViewHolder.FLAG_IGNORE);
   6788             mRecyclerView.mState.onViewIgnored(vh);
   6789         }
   6790 
   6791         /**
   6792          * View can be scrapped and recycled again.
   6793          * <p>
   6794          * Note that calling this method removes all information in the view holder.
   6795          * <p>
   6796          * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
   6797          *
   6798          * @param view View to ignore.
   6799          */
   6800         public void stopIgnoringView(View view) {
   6801             final ViewHolder vh = getChildViewHolderInt(view);
   6802             vh.stopIgnoring();
   6803             vh.resetInternal();
   6804             vh.addFlags(ViewHolder.FLAG_INVALID);
   6805         }
   6806 
   6807         /**
   6808          * Temporarily detach and scrap all currently attached child views. Views will be scrapped
   6809          * into the given Recycler. The Recycler may prefer to reuse scrap views before
   6810          * other views that were previously recycled.
   6811          *
   6812          * @param recycler Recycler to scrap views into
   6813          */
   6814         public void detachAndScrapAttachedViews(Recycler recycler) {
   6815             final int childCount = getChildCount();
   6816             for (int i = childCount - 1; i >= 0; i--) {
   6817                 final View v = getChildAt(i);
   6818                 scrapOrRecycleView(recycler, i, v);
   6819             }
   6820         }
   6821 
   6822         private void scrapOrRecycleView(Recycler recycler, int index, View view) {
   6823             final ViewHolder viewHolder = getChildViewHolderInt(view);
   6824             if (viewHolder.shouldIgnore()) {
   6825                 if (DEBUG) {
   6826                     Log.d(TAG, "ignoring view " + viewHolder);
   6827                 }
   6828                 return;
   6829             }
   6830             if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !viewHolder.isChanged() &&
   6831                     !mRecyclerView.mAdapter.hasStableIds()) {
   6832                 removeViewAt(index);
   6833                 recycler.recycleViewHolderInternal(viewHolder);
   6834             } else {
   6835                 detachViewAt(index);
   6836                 recycler.scrapView(view);
   6837             }
   6838         }
   6839 
   6840         /**
   6841          * Recycles the scrapped views.
   6842          * <p>
   6843          * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
   6844          * the expected behavior if scrapped views are used for animations. Otherwise, we need to
   6845          * call remove and invalidate RecyclerView to ensure UI update.
   6846          *
   6847          * @param recycler Recycler
   6848          */
   6849         void removeAndRecycleScrapInt(Recycler recycler) {
   6850             final int scrapCount = recycler.getScrapCount();
   6851             // Loop backward, recycler might be changed by removeDetachedView()
   6852             for (int i = scrapCount - 1; i >= 0; i--) {
   6853                 final View scrap = recycler.getScrapViewAt(i);
   6854                 final ViewHolder vh = getChildViewHolderInt(scrap);
   6855                 if (vh.shouldIgnore()) {
   6856                     continue;
   6857                 }
   6858                 // If the scrap view is animating, we need to cancel them first. If we cancel it
   6859                 // here, ItemAnimator callback may recycle it which will cause double recycling.
   6860                 // To avoid this, we mark it as not recycleable before calling the item animator.
   6861                 // Since removeDetachedView calls a user API, a common mistake (ending animations on
   6862                 // the view) may recycle it too, so we guard it before we call user APIs.
   6863                 vh.setIsRecyclable(false);
   6864                 if (vh.isTmpDetached()) {
   6865                     mRecyclerView.removeDetachedView(scrap, false);
   6866                 }
   6867                 if (mRecyclerView.mItemAnimator != null) {
   6868                     mRecyclerView.mItemAnimator.endAnimation(vh);
   6869                 }
   6870                 vh.setIsRecyclable(true);
   6871                 recycler.quickRecycleScrapView(scrap);
   6872             }
   6873             recycler.clearScrap();
   6874             if (scrapCount > 0) {
   6875                 mRecyclerView.invalidate();
   6876             }
   6877         }
   6878 
   6879 
   6880         /**
   6881          * Measure a child view using standard measurement policy, taking the padding
   6882          * of the parent RecyclerView and any added item decorations into account.
   6883          *
   6884          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   6885          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   6886          *
   6887          * @param child Child view to measure
   6888          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   6889          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   6890          */
   6891         public void measureChild(View child, int widthUsed, int heightUsed) {
   6892             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   6893 
   6894             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   6895             widthUsed += insets.left + insets.right;
   6896             heightUsed += insets.top + insets.bottom;
   6897 
   6898             final int widthSpec = getChildMeasureSpec(getWidth(),
   6899                     getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
   6900                     canScrollHorizontally());
   6901             final int heightSpec = getChildMeasureSpec(getHeight(),
   6902                     getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
   6903                     canScrollVertically());
   6904             child.measure(widthSpec, heightSpec);
   6905         }
   6906 
   6907         /**
   6908          * Measure a child view using standard measurement policy, taking the padding
   6909          * of the parent RecyclerView, any added item decorations and the child margins
   6910          * into account.
   6911          *
   6912          * <p>If the RecyclerView can be scrolled in either dimension the caller may
   6913          * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>
   6914          *
   6915          * @param child Child view to measure
   6916          * @param widthUsed Width in pixels currently consumed by other views, if relevant
   6917          * @param heightUsed Height in pixels currently consumed by other views, if relevant
   6918          */
   6919         public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
   6920             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
   6921 
   6922             final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   6923             widthUsed += insets.left + insets.right;
   6924             heightUsed += insets.top + insets.bottom;
   6925 
   6926             final int widthSpec = getChildMeasureSpec(getWidth(),
   6927                     getPaddingLeft() + getPaddingRight() +
   6928                             lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
   6929                     canScrollHorizontally());
   6930             final int heightSpec = getChildMeasureSpec(getHeight(),
   6931                     getPaddingTop() + getPaddingBottom() +
   6932                             lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
   6933                     canScrollVertically());
   6934             child.measure(widthSpec, heightSpec);
   6935         }
   6936 
   6937         /**
   6938          * Calculate a MeasureSpec value for measuring a child view in one dimension.
   6939          *
   6940          * @param parentSize Size of the parent view where the child will be placed
   6941          * @param padding Total space currently consumed by other elements of parent
   6942          * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
   6943          *                       Generally obtained from the child view's LayoutParams
   6944          * @param canScroll true if the parent RecyclerView can scroll in this dimension
   6945          *
   6946          * @return a MeasureSpec value for the child view
   6947          */
   6948         public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
   6949                 boolean canScroll) {
   6950             int size = Math.max(0, parentSize - padding);
   6951             int resultSize = 0;
   6952             int resultMode = 0;
   6953 
   6954             if (canScroll) {
   6955                 if (childDimension >= 0) {
   6956                     resultSize = childDimension;
   6957                     resultMode = MeasureSpec.EXACTLY;
   6958                 } else {
   6959                     // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
   6960                     // instead using UNSPECIFIED.
   6961                     resultSize = 0;
   6962                     resultMode = MeasureSpec.UNSPECIFIED;
   6963                 }
   6964             } else {
   6965                 if (childDimension >= 0) {
   6966                     resultSize = childDimension;
   6967                     resultMode = MeasureSpec.EXACTLY;
   6968                 } else if (childDimension == LayoutParams.FILL_PARENT) {
   6969                     resultSize = size;
   6970                     resultMode = MeasureSpec.EXACTLY;
   6971                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {
   6972                     resultSize = size;
   6973                     resultMode = MeasureSpec.AT_MOST;
   6974                 }
   6975             }
   6976             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
   6977         }
   6978 
   6979         /**
   6980          * Returns the measured width of the given child, plus the additional size of
   6981          * any insets applied by {@link ItemDecoration ItemDecorations}.
   6982          *
   6983          * @param child Child view to query
   6984          * @return child's measured width plus <code>ItemDecoration</code> insets
   6985          *
   6986          * @see View#getMeasuredWidth()
   6987          */
   6988         public int getDecoratedMeasuredWidth(View child) {
   6989             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   6990             return child.getMeasuredWidth() + insets.left + insets.right;
   6991         }
   6992 
   6993         /**
   6994          * Returns the measured height of the given child, plus the additional size of
   6995          * any insets applied by {@link ItemDecoration ItemDecorations}.
   6996          *
   6997          * @param child Child view to query
   6998          * @return child's measured height plus <code>ItemDecoration</code> insets
   6999          *
   7000          * @see View#getMeasuredHeight()
   7001          */
   7002         public int getDecoratedMeasuredHeight(View child) {
   7003             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   7004             return child.getMeasuredHeight() + insets.top + insets.bottom;
   7005         }
   7006 
   7007         /**
   7008          * Lay out the given child view within the RecyclerView using coordinates that
   7009          * include any current {@link ItemDecoration ItemDecorations}.
   7010          *
   7011          * <p>LayoutManagers should prefer working in sizes and coordinates that include
   7012          * item decoration insets whenever possible. This allows the LayoutManager to effectively
   7013          * ignore decoration insets within measurement and layout code. See the following
   7014          * methods:</p>
   7015          * <ul>
   7016          *     <li>{@link #measureChild(View, int, int)}</li>
   7017          *     <li>{@link #measureChildWithMargins(View, int, int)}</li>
   7018          *     <li>{@link #getDecoratedLeft(View)}</li>
   7019          *     <li>{@link #getDecoratedTop(View)}</li>
   7020          *     <li>{@link #getDecoratedRight(View)}</li>
   7021          *     <li>{@link #getDecoratedBottom(View)}</li>
   7022          *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>
   7023          *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>
   7024          * </ul>
   7025          *
   7026          * @param child Child to lay out
   7027          * @param left Left edge, with item decoration insets included
   7028          * @param top Top edge, with item decoration insets included
   7029          * @param right Right edge, with item decoration insets included
   7030          * @param bottom Bottom edge, with item decoration insets included
   7031          *
   7032          * @see View#layout(int, int, int, int)
   7033          */
   7034         public void layoutDecorated(View child, int left, int top, int right, int bottom) {
   7035             final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
   7036             child.layout(left + insets.left, top + insets.top, right - insets.right,
   7037                     bottom - insets.bottom);
   7038         }
   7039 
   7040         /**
   7041          * Returns the left edge of the given child view within its parent, offset by any applied
   7042          * {@link ItemDecoration ItemDecorations}.
   7043          *
   7044          * @param child Child to query
   7045          * @return Child left edge with offsets applied
   7046          * @see #getLeftDecorationWidth(View)
   7047          */
   7048         public int getDecoratedLeft(View child) {
   7049             return child.getLeft() - getLeftDecorationWidth(child);
   7050         }
   7051 
   7052         /**
   7053          * Returns the top edge of the given child view within its parent, offset by any applied
   7054          * {@link ItemDecoration ItemDecorations}.
   7055          *
   7056          * @param child Child to query
   7057          * @return Child top edge with offsets applied
   7058          * @see #getTopDecorationHeight(View)
   7059          */
   7060         public int getDecoratedTop(View child) {
   7061             return child.getTop() - getTopDecorationHeight(child);
   7062         }
   7063 
   7064         /**
   7065          * Returns the right edge of the given child view within its parent, offset by any applied
   7066          * {@link ItemDecoration ItemDecorations}.
   7067          *
   7068          * @param child Child to query
   7069          * @return Child right edge with offsets applied
   7070          * @see #getRightDecorationWidth(View)
   7071          */
   7072         public int getDecoratedRight(View child) {
   7073             return child.getRight() + getRightDecorationWidth(child);
   7074         }
   7075 
   7076         /**
   7077          * Returns the bottom edge of the given child view within its parent, offset by any applied
   7078          * {@link ItemDecoration ItemDecorations}.
   7079          *
   7080          * @param child Child to query
   7081          * @return Child bottom edge with offsets applied
   7082          * @see #getBottomDecorationHeight(View)
   7083          */
   7084         public int getDecoratedBottom(View child) {
   7085             return child.getBottom() + getBottomDecorationHeight(child);
   7086         }
   7087 
   7088         /**
   7089          * Calculates the item decor insets applied to the given child and updates the provided
   7090          * Rect instance with the inset values.
   7091          * <ul>
   7092          *     <li>The Rect's left is set to the total width of left decorations.</li>
   7093          *     <li>The Rect's top is set to the total height of top decorations.</li>
   7094          *     <li>The Rect's right is set to the total width of right decorations.</li>
   7095          *     <li>The Rect's bottom is set to total height of bottom decorations.</li>
   7096          * </ul>
   7097          * <p>
   7098          * Note that item decorations are automatically calculated when one of the LayoutManager's
   7099          * measure child methods is called. If you need to measure the child with custom specs via
   7100          * {@link View#measure(int, int)}, you can use this method to get decorations.
   7101          *
   7102          * @param child The child view whose decorations should be calculated
   7103          * @param outRect The Rect to hold result values
   7104          */
   7105         public void calculateItemDecorationsForChild(View child, Rect outRect) {
   7106             if (mRecyclerView == null) {
   7107                 outRect.set(0, 0, 0, 0);
   7108                 return;
   7109             }
   7110             Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
   7111             outRect.set(insets);
   7112         }
   7113 
   7114         /**
   7115          * Returns the total height of item decorations applied to child's top.
   7116          * <p>
   7117          * Note that this value is not updated until the View is measured or
   7118          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   7119          *
   7120          * @param child Child to query
   7121          * @return The total height of item decorations applied to the child's top.
   7122          * @see #getDecoratedTop(View)
   7123          * @see #calculateItemDecorationsForChild(View, Rect)
   7124          */
   7125         public int getTopDecorationHeight(View child) {
   7126             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
   7127         }
   7128 
   7129         /**
   7130          * Returns the total height of item decorations applied to child's bottom.
   7131          * <p>
   7132          * Note that this value is not updated until the View is measured or
   7133          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   7134          *
   7135          * @param child Child to query
   7136          * @return The total height of item decorations applied to the child's bottom.
   7137          * @see #getDecoratedBottom(View)
   7138          * @see #calculateItemDecorationsForChild(View, Rect)
   7139          */
   7140         public int getBottomDecorationHeight(View child) {
   7141             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
   7142         }
   7143 
   7144         /**
   7145          * Returns the total width of item decorations applied to child's left.
   7146          * <p>
   7147          * Note that this value is not updated until the View is measured or
   7148          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   7149          *
   7150          * @param child Child to query
   7151          * @return The total width of item decorations applied to the child's left.
   7152          * @see #getDecoratedLeft(View)
   7153          * @see #calculateItemDecorationsForChild(View, Rect)
   7154          */
   7155         public int getLeftDecorationWidth(View child) {
   7156             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
   7157         }
   7158 
   7159         /**
   7160          * Returns the total width of item decorations applied to child's right.
   7161          * <p>
   7162          * Note that this value is not updated until the View is measured or
   7163          * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
   7164          *
   7165          * @param child Child to query
   7166          * @return The total width of item decorations applied to the child's right.
   7167          * @see #getDecoratedRight(View)
   7168          * @see #calculateItemDecorationsForChild(View, Rect)
   7169          */
   7170         public int getRightDecorationWidth(View child) {
   7171             return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
   7172         }
   7173 
   7174         /**
   7175          * Called when searching for a focusable view in the given direction has failed
   7176          * for the current content of the RecyclerView.
   7177          *
   7178          * <p>This is the LayoutManager's opportunity to populate views in the given direction
   7179          * to fulfill the request if it can. The LayoutManager should attach and return
   7180          * the view to be focused. The default implementation returns null.</p>
   7181          *
   7182          * @param focused   The currently focused view
   7183          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   7184          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   7185          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   7186          *                  or 0 for not applicable
   7187          * @param recycler  The recycler to use for obtaining views for currently offscreen items
   7188          * @param state     Transient state of RecyclerView
   7189          * @return The chosen view to be focused
   7190          */
   7191         @Nullable
   7192         public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
   7193                 State state) {
   7194             return null;
   7195         }
   7196 
   7197         /**
   7198          * This method gives a LayoutManager an opportunity to intercept the initial focus search
   7199          * before the default behavior of {@link FocusFinder} is used. If this method returns
   7200          * null FocusFinder will attempt to find a focusable child view. If it fails
   7201          * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}
   7202          * will be called to give the LayoutManager an opportunity to add new views for items
   7203          * that did not have attached views representing them. The LayoutManager should not add
   7204          * or remove views from this method.
   7205          *
   7206          * @param focused The currently focused view
   7207          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   7208          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   7209          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   7210          * @return A descendant view to focus or null to fall back to default behavior.
   7211          *         The default implementation returns null.
   7212          */
   7213         public View onInterceptFocusSearch(View focused, int direction) {
   7214             return null;
   7215         }
   7216 
   7217         /**
   7218          * Called when a child of the RecyclerView wants a particular rectangle to be positioned
   7219          * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,
   7220          * android.graphics.Rect, boolean)} for more details.
   7221          *
   7222          * <p>The base implementation will attempt to perform a standard programmatic scroll
   7223          * to bring the given rect into view, within the padded area of the RecyclerView.</p>
   7224          *
   7225          * @param child The direct child making the request.
   7226          * @param rect  The rectangle in the child's coordinates the child
   7227          *              wishes to be on the screen.
   7228          * @param immediate True to forbid animated or delayed scrolling,
   7229          *                  false otherwise
   7230          * @return Whether the group scrolled to handle the operation
   7231          */
   7232         public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
   7233                 boolean immediate) {
   7234             final int parentLeft = getPaddingLeft();
   7235             final int parentTop = getPaddingTop();
   7236             final int parentRight = getWidth() - getPaddingRight();
   7237             final int parentBottom = getHeight() - getPaddingBottom();
   7238             final int childLeft = child.getLeft() + rect.left;
   7239             final int childTop = child.getTop() + rect.top;
   7240             final int childRight = childLeft + rect.width();
   7241             final int childBottom = childTop + rect.height();
   7242 
   7243             final int offScreenLeft = Math.min(0, childLeft - parentLeft);
   7244             final int offScreenTop = Math.min(0, childTop - parentTop);
   7245             final int offScreenRight = Math.max(0, childRight - parentRight);
   7246             final int offScreenBottom = Math.max(0, childBottom - parentBottom);
   7247 
   7248             // Favor the "start" layout direction over the end when bringing one side or the other
   7249             // of a large rect into view. If we decide to bring in end because start is already
   7250             // visible, limit the scroll such that start won't go out of bounds.
   7251             final int dx;
   7252             if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
   7253                 dx = offScreenRight != 0 ? offScreenRight
   7254                         : Math.max(offScreenLeft, childRight - parentRight);
   7255             } else {
   7256                 dx = offScreenLeft != 0 ? offScreenLeft
   7257                         : Math.min(childLeft - parentLeft, offScreenRight);
   7258             }
   7259 
   7260             // Favor bringing the top into view over the bottom. If top is already visible and
   7261             // we should scroll to make bottom visible, make sure top does not go out of bounds.
   7262             final int dy = offScreenTop != 0 ? offScreenTop
   7263                     : Math.min(childTop - parentTop, offScreenBottom);
   7264 
   7265             if (dx != 0 || dy != 0) {
   7266                 if (immediate) {
   7267                     parent.scrollBy(dx, dy);
   7268                 } else {
   7269                     parent.smoothScrollBy(dx, dy);
   7270                 }
   7271                 return true;
   7272             }
   7273             return false;
   7274         }
   7275 
   7276         /**
   7277          * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
   7278          */
   7279         @Deprecated
   7280         public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
   7281             // eat the request if we are in the middle of a scroll or layout
   7282             return isSmoothScrolling() || parent.isComputingLayout();
   7283         }
   7284 
   7285         /**
   7286          * Called when a descendant view of the RecyclerView requests focus.
   7287          *
   7288          * <p>A LayoutManager wishing to keep focused views aligned in a specific
   7289          * portion of the view may implement that behavior in an override of this method.</p>
   7290          *
   7291          * <p>If the LayoutManager executes different behavior that should override the default
   7292          * behavior of scrolling the focused child on screen instead of running alongside it,
   7293          * this method should return true.</p>
   7294          *
   7295          * @param parent  The RecyclerView hosting this LayoutManager
   7296          * @param state   Current state of RecyclerView
   7297          * @param child   Direct child of the RecyclerView containing the newly focused view
   7298          * @param focused The newly focused view. This may be the same view as child or it may be
   7299          *                null
   7300          * @return true if the default scroll behavior should be suppressed
   7301          */
   7302         public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
   7303                 View focused) {
   7304             return onRequestChildFocus(parent, child, focused);
   7305         }
   7306 
   7307         /**
   7308          * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
   7309          * The LayoutManager may use this opportunity to clear caches and configure state such
   7310          * that it can relayout appropriately with the new data and potentially new view types.
   7311          *
   7312          * <p>The default implementation removes all currently attached views.</p>
   7313          *
   7314          * @param oldAdapter The previous adapter instance. Will be null if there was previously no
   7315          *                   adapter.
   7316          * @param newAdapter The new adapter instance. Might be null if
   7317          *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.
   7318          */
   7319         public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
   7320         }
   7321 
   7322         /**
   7323          * Called to populate focusable views within the RecyclerView.
   7324          *
   7325          * <p>The LayoutManager implementation should return <code>true</code> if the default
   7326          * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be
   7327          * suppressed.</p>
   7328          *
   7329          * <p>The default implementation returns <code>false</code> to trigger RecyclerView
   7330          * to fall back to the default ViewGroup behavior.</p>
   7331          *
   7332          * @param recyclerView The RecyclerView hosting this LayoutManager
   7333          * @param views List of output views. This method should add valid focusable views
   7334          *              to this list.
   7335          * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
   7336          *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
   7337          *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
   7338          * @param focusableMode The type of focusables to be added.
   7339          *
   7340          * @return true to suppress the default behavior, false to add default focusables after
   7341          *         this method returns.
   7342          *
   7343          * @see #FOCUSABLES_ALL
   7344          * @see #FOCUSABLES_TOUCH_MODE
   7345          */
   7346         public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,
   7347                 int direction, int focusableMode) {
   7348             return false;
   7349         }
   7350 
   7351         /**
   7352          * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
   7353          * detailed information on what has actually changed.
   7354          *
   7355          * @param recyclerView
   7356          */
   7357         public void onItemsChanged(RecyclerView recyclerView) {
   7358         }
   7359 
   7360         /**
   7361          * Called when items have been added to the adapter. The LayoutManager may choose to
   7362          * requestLayout if the inserted items would require refreshing the currently visible set
   7363          * of child views. (e.g. currently empty space would be filled by appended items, etc.)
   7364          *
   7365          * @param recyclerView
   7366          * @param positionStart
   7367          * @param itemCount
   7368          */
   7369         public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
   7370         }
   7371 
   7372         /**
   7373          * Called when items have been removed from the adapter.
   7374          *
   7375          * @param recyclerView
   7376          * @param positionStart
   7377          * @param itemCount
   7378          */
   7379         public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
   7380         }
   7381 
   7382         /**
   7383          * Called when items have been changed in the adapter.
   7384          * To receive payload,  override {@link #onItemsUpdated(RecyclerView, int, int, Object)}
   7385          * instead, then this callback will not be invoked.
   7386          *
   7387          * @param recyclerView
   7388          * @param positionStart
   7389          * @param itemCount
   7390          */
   7391         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
   7392         }
   7393 
   7394         /**
   7395          * Called when items have been changed in the adapter and with optional payload.
   7396          * Default implementation calls {@link #onItemsUpdated(RecyclerView, int, int)}.
   7397          *
   7398          * @param recyclerView
   7399          * @param positionStart
   7400          * @param itemCount
   7401          * @param payload
   7402          */
   7403         public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
   7404                 Object payload) {
   7405             onItemsUpdated(recyclerView, positionStart, itemCount);
   7406         }
   7407 
   7408         /**
   7409          * Called when an item is moved withing the adapter.
   7410          * <p>
   7411          * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
   7412          * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
   7413          * is called.
   7414          *
   7415          * @param recyclerView
   7416          * @param from
   7417          * @param to
   7418          * @param itemCount
   7419          */
   7420         public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
   7421 
   7422         }
   7423 
   7424 
   7425         /**
   7426          * <p>Override this method if you want to support scroll bars.</p>
   7427          *
   7428          * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>
   7429          *
   7430          * <p>Default implementation returns 0.</p>
   7431          *
   7432          * @param state Current state of RecyclerView
   7433          * @return The horizontal extent of the scrollbar's thumb
   7434          * @see RecyclerView#computeHorizontalScrollExtent()
   7435          */
   7436         public int computeHorizontalScrollExtent(State state) {
   7437             return 0;
   7438         }
   7439 
   7440         /**
   7441          * <p>Override this method if you want to support scroll bars.</p>
   7442          *
   7443          * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>
   7444          *
   7445          * <p>Default implementation returns 0.</p>
   7446          *
   7447          * @param state Current State of RecyclerView where you can find total item count
   7448          * @return The horizontal offset of the scrollbar's thumb
   7449          * @see RecyclerView#computeHorizontalScrollOffset()
   7450          */
   7451         public int computeHorizontalScrollOffset(State state) {
   7452             return 0;
   7453         }
   7454 
   7455         /**
   7456          * <p>Override this method if you want to support scroll bars.</p>
   7457          *
   7458          * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>
   7459          *
   7460          * <p>Default implementation returns 0.</p>
   7461          *
   7462          * @param state Current State of RecyclerView where you can find total item count
   7463          * @return The total horizontal range represented by the vertical scrollbar
   7464          * @see RecyclerView#computeHorizontalScrollRange()
   7465          */
   7466         public int computeHorizontalScrollRange(State state) {
   7467             return 0;
   7468         }
   7469 
   7470         /**
   7471          * <p>Override this method if you want to support scroll bars.</p>
   7472          *
   7473          * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>
   7474          *
   7475          * <p>Default implementation returns 0.</p>
   7476          *
   7477          * @param state Current state of RecyclerView
   7478          * @return The vertical extent of the scrollbar's thumb
   7479          * @see RecyclerView#computeVerticalScrollExtent()
   7480          */
   7481         public int computeVerticalScrollExtent(State state) {
   7482             return 0;
   7483         }
   7484 
   7485         /**
   7486          * <p>Override this method if you want to support scroll bars.</p>
   7487          *
   7488          * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>
   7489          *
   7490          * <p>Default implementation returns 0.</p>
   7491          *
   7492          * @param state Current State of RecyclerView where you can find total item count
   7493          * @return The vertical offset of the scrollbar's thumb
   7494          * @see RecyclerView#computeVerticalScrollOffset()
   7495          */
   7496         public int computeVerticalScrollOffset(State state) {
   7497             return 0;
   7498         }
   7499 
   7500         /**
   7501          * <p>Override this method if you want to support scroll bars.</p>
   7502          *
   7503          * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>
   7504          *
   7505          * <p>Default implementation returns 0.</p>
   7506          *
   7507          * @param state Current State of RecyclerView where you can find total item count
   7508          * @return The total vertical range represented by the vertical scrollbar
   7509          * @see RecyclerView#computeVerticalScrollRange()
   7510          */
   7511         public int computeVerticalScrollRange(State state) {
   7512             return 0;
   7513         }
   7514 
   7515         /**
   7516          * Measure the attached RecyclerView. Implementations must call
   7517          * {@link #setMeasuredDimension(int, int)} before returning.
   7518          *
   7519          * <p>The default implementation will handle EXACTLY measurements and respect
   7520          * the minimum width and height properties of the host RecyclerView if measured
   7521          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
   7522          * will consume all available space.</p>
   7523          *
   7524          * @param recycler Recycler
   7525          * @param state Transient state of RecyclerView
   7526          * @param widthSpec Width {@link android.view.View.MeasureSpec}
   7527          * @param heightSpec Height {@link android.view.View.MeasureSpec}
   7528          */
   7529         public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
   7530             mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
   7531         }
   7532 
   7533         /**
   7534          * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
   7535          * host RecyclerView.
   7536          *
   7537          * @param widthSize Measured width
   7538          * @param heightSize Measured height
   7539          */
   7540         public void setMeasuredDimension(int widthSize, int heightSize) {
   7541             mRecyclerView.setMeasuredDimension(widthSize, heightSize);
   7542         }
   7543 
   7544         /**
   7545          * @return The host RecyclerView's {@link View#getMinimumWidth()}
   7546          */
   7547         public int getMinimumWidth() {
   7548             return ViewCompat.getMinimumWidth(mRecyclerView);
   7549         }
   7550 
   7551         /**
   7552          * @return The host RecyclerView's {@link View#getMinimumHeight()}
   7553          */
   7554         public int getMinimumHeight() {
   7555             return ViewCompat.getMinimumHeight(mRecyclerView);
   7556         }
   7557         /**
   7558          * <p>Called when the LayoutManager should save its state. This is a good time to save your
   7559          * scroll position, configuration and anything else that may be required to restore the same
   7560          * layout state if the LayoutManager is recreated.</p>
   7561          * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and
   7562          * restore. This will let you share information between your LayoutManagers but it is also
   7563          * your responsibility to make sure they use the same parcelable class.</p>
   7564          *
   7565          * @return Necessary information for LayoutManager to be able to restore its state
   7566          */
   7567         public Parcelable onSaveInstanceState() {
   7568             return null;
   7569         }
   7570 
   7571 
   7572         public void onRestoreInstanceState(Parcelable state) {
   7573 
   7574         }
   7575 
   7576         void stopSmoothScroller() {
   7577             if (mSmoothScroller != null) {
   7578                 mSmoothScroller.stop();
   7579             }
   7580         }
   7581 
   7582         private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
   7583             if (mSmoothScroller == smoothScroller) {
   7584                 mSmoothScroller = null;
   7585             }
   7586         }
   7587 
   7588         /**
   7589          * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
   7590          *
   7591          * @param state The new scroll state for RecyclerView
   7592          */
   7593         public void onScrollStateChanged(int state) {
   7594         }
   7595 
   7596         /**
   7597          * Removes all views and recycles them using the given recycler.
   7598          * <p>
   7599          * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
   7600          * <p>
   7601          * If a View is marked as "ignored", it is not removed nor recycled.
   7602          *
   7603          * @param recycler Recycler to use to recycle children
   7604          * @see #removeAndRecycleView(View, Recycler)
   7605          * @see #removeAndRecycleViewAt(int, Recycler)
   7606          * @see #ignoreView(View)
   7607          */
   7608         public void removeAndRecycleAllViews(Recycler recycler) {
   7609             for (int i = getChildCount() - 1; i >= 0; i--) {
   7610                 final View view = getChildAt(i);
   7611                 if (!getChildViewHolderInt(view).shouldIgnore()) {
   7612                     removeAndRecycleViewAt(i, recycler);
   7613                 }
   7614             }
   7615         }
   7616 
   7617         // called by accessibility delegate
   7618         void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
   7619             onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState, info);
   7620         }
   7621 
   7622         /**
   7623          * Called by the AccessibilityDelegate when the information about the current layout should
   7624          * be populated.
   7625          * <p>
   7626          * Default implementation adds a {@link
   7627          * android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat}.
   7628          * <p>
   7629          * You should override
   7630          * {@link #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   7631          * {@link #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)},
   7632          * {@link #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)} and
   7633          * {@link #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)} for
   7634          * more accurate accessibility information.
   7635          *
   7636          * @param recycler The Recycler that can be used to convert view positions into adapter
   7637          *                 positions
   7638          * @param state    The current state of RecyclerView
   7639          * @param info     The info that should be filled by the LayoutManager
   7640          * @see View#onInitializeAccessibilityNodeInfo(
   7641          *android.view.accessibility.AccessibilityNodeInfo)
   7642          * @see #getRowCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   7643          * @see #getColumnCountForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   7644          * @see #isLayoutHierarchical(RecyclerView.Recycler, RecyclerView.State)
   7645          * @see #getSelectionModeForAccessibility(RecyclerView.Recycler, RecyclerView.State)
   7646          */
   7647         public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
   7648                 AccessibilityNodeInfoCompat info) {
   7649             if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
   7650                     ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
   7651                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
   7652                 info.setScrollable(true);
   7653             }
   7654             if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
   7655                     ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
   7656                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
   7657                 info.setScrollable(true);
   7658             }
   7659             final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
   7660                     = AccessibilityNodeInfoCompat.CollectionInfoCompat
   7661                     .obtain(getRowCountForAccessibility(recycler, state),
   7662                             getColumnCountForAccessibility(recycler, state),
   7663                             isLayoutHierarchical(recycler, state),
   7664                             getSelectionModeForAccessibility(recycler, state));
   7665             info.setCollectionInfo(collectionInfo);
   7666         }
   7667 
   7668         // called by accessibility delegate
   7669         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
   7670             onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
   7671         }
   7672 
   7673         /**
   7674          * Called by the accessibility delegate to initialize an accessibility event.
   7675          * <p>
   7676          * Default implementation adds item count and scroll information to the event.
   7677          *
   7678          * @param recycler The Recycler that can be used to convert view positions into adapter
   7679          *                 positions
   7680          * @param state    The current state of RecyclerView
   7681          * @param event    The event instance to initialize
   7682          * @see View#onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent)
   7683          */
   7684         public void onInitializeAccessibilityEvent(Recycler recycler, State state,
   7685                 AccessibilityEvent event) {
   7686             final AccessibilityRecordCompat record = AccessibilityEventCompat
   7687                     .asRecord(event);
   7688             if (mRecyclerView == null || record == null) {
   7689                 return;
   7690             }
   7691             record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
   7692                     || ViewCompat.canScrollVertically(mRecyclerView, -1)
   7693                     || ViewCompat.canScrollHorizontally(mRecyclerView, -1)
   7694                     || ViewCompat.canScrollHorizontally(mRecyclerView, 1));
   7695 
   7696             if (mRecyclerView.mAdapter != null) {
   7697                 record.setItemCount(mRecyclerView.mAdapter.getItemCount());
   7698             }
   7699         }
   7700 
   7701         // called by accessibility delegate
   7702         void onInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompat info) {
   7703             final ViewHolder vh = getChildViewHolderInt(host);
   7704             // avoid trying to create accessibility node info for removed children
   7705             if (vh != null && !vh.isRemoved() && !mChildHelper.isHidden(vh.itemView)) {
   7706                 onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
   7707                         mRecyclerView.mState, host, info);
   7708             }
   7709         }
   7710 
   7711         /**
   7712          * Called by the AccessibilityDelegate when the accessibility information for a specific
   7713          * item should be populated.
   7714          * <p>
   7715          * Default implementation adds basic positioning information about the item.
   7716          *
   7717          * @param recycler The Recycler that can be used to convert view positions into adapter
   7718          *                 positions
   7719          * @param state    The current state of RecyclerView
   7720          * @param host     The child for which accessibility node info should be populated
   7721          * @param info     The info to fill out about the item
   7722          * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
   7723          * android.view.accessibility.AccessibilityNodeInfo)
   7724          */
   7725         public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
   7726                 View host, AccessibilityNodeInfoCompat info) {
   7727             int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
   7728             int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
   7729             final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
   7730                     = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
   7731                     columnIndexGuess, 1, false, false);
   7732             info.setCollectionItemInfo(itemInfo);
   7733         }
   7734 
   7735         /**
   7736          * A LayoutManager can call this method to force RecyclerView to run simple animations in
   7737          * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
   7738          * change).
   7739          * <p>
   7740          * Note that, calling this method will not guarantee that RecyclerView will run animations
   7741          * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
   7742          * not run any animations but will still clear this flag after the layout is complete.
   7743          *
   7744          */
   7745         public void requestSimpleAnimationsInNextLayout() {
   7746             mRequestedSimpleAnimations = true;
   7747         }
   7748 
   7749         /**
   7750          * Returns the selection mode for accessibility. Should be
   7751          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
   7752          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
   7753          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
   7754          * <p>
   7755          * Default implementation returns
   7756          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
   7757          *
   7758          * @param recycler The Recycler that can be used to convert view positions into adapter
   7759          *                 positions
   7760          * @param state    The current state of RecyclerView
   7761          * @return Selection mode for accessibility. Default implementation returns
   7762          * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
   7763          */
   7764         public int getSelectionModeForAccessibility(Recycler recycler, State state) {
   7765             return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
   7766         }
   7767 
   7768         /**
   7769          * Returns the number of rows for accessibility.
   7770          * <p>
   7771          * Default implementation returns the number of items in the adapter if LayoutManager
   7772          * supports vertical scrolling or 1 if LayoutManager does not support vertical
   7773          * scrolling.
   7774          *
   7775          * @param recycler The Recycler that can be used to convert view positions into adapter
   7776          *                 positions
   7777          * @param state    The current state of RecyclerView
   7778          * @return The number of rows in LayoutManager for accessibility.
   7779          */
   7780         public int getRowCountForAccessibility(Recycler recycler, State state) {
   7781             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   7782                 return 1;
   7783             }
   7784             return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
   7785         }
   7786 
   7787         /**
   7788          * Returns the number of columns for accessibility.
   7789          * <p>
   7790          * Default implementation returns the number of items in the adapter if LayoutManager
   7791          * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
   7792          * scrolling.
   7793          *
   7794          * @param recycler The Recycler that can be used to convert view positions into adapter
   7795          *                 positions
   7796          * @param state    The current state of RecyclerView
   7797          * @return The number of rows in LayoutManager for accessibility.
   7798          */
   7799         public int getColumnCountForAccessibility(Recycler recycler, State state) {
   7800             if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
   7801                 return 1;
   7802             }
   7803             return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
   7804         }
   7805 
   7806         /**
   7807          * Returns whether layout is hierarchical or not to be used for accessibility.
   7808          * <p>
   7809          * Default implementation returns false.
   7810          *
   7811          * @param recycler The Recycler that can be used to convert view positions into adapter
   7812          *                 positions
   7813          * @param state    The current state of RecyclerView
   7814          * @return True if layout is hierarchical.
   7815          */
   7816         public boolean isLayoutHierarchical(Recycler recycler, State state) {
   7817             return false;
   7818         }
   7819 
   7820         // called by accessibility delegate
   7821         boolean performAccessibilityAction(int action, Bundle args) {
   7822             return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
   7823                     action, args);
   7824         }
   7825 
   7826         /**
   7827          * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
   7828          *
   7829          * @param recycler  The Recycler that can be used to convert view positions into adapter
   7830          *                  positions
   7831          * @param state     The current state of RecyclerView
   7832          * @param action    The action to perform
   7833          * @param args      Optional action arguments
   7834          * @see View#performAccessibilityAction(int, android.os.Bundle)
   7835          */
   7836         public boolean performAccessibilityAction(Recycler recycler, State state, int action,
   7837                 Bundle args) {
   7838             if (mRecyclerView == null) {
   7839                 return false;
   7840             }
   7841             int vScroll = 0, hScroll = 0;
   7842             switch (action) {
   7843                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
   7844                     if (ViewCompat.canScrollVertically(mRecyclerView, -1)) {
   7845                         vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
   7846                     }
   7847                     if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
   7848                         hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
   7849                     }
   7850                     break;
   7851                 case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
   7852                     if (ViewCompat.canScrollVertically(mRecyclerView, 1)) {
   7853                         vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
   7854                     }
   7855                     if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
   7856                         hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
   7857                     }
   7858                     break;
   7859             }
   7860             if (vScroll == 0 && hScroll == 0) {
   7861                 return false;
   7862             }
   7863             mRecyclerView.scrollBy(hScroll, vScroll);
   7864             return true;
   7865         }
   7866 
   7867         // called by accessibility delegate
   7868         boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
   7869             return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
   7870                     view, action, args);
   7871         }
   7872 
   7873         /**
   7874          * Called by AccessibilityDelegate when an accessibility action is requested on one of the
   7875          * children of LayoutManager.
   7876          * <p>
   7877          * Default implementation does not do anything.
   7878          *
   7879          * @param recycler The Recycler that can be used to convert view positions into adapter
   7880          *                 positions
   7881          * @param state    The current state of RecyclerView
   7882          * @param view     The child view on which the action is performed
   7883          * @param action   The action to perform
   7884          * @param args     Optional action arguments
   7885          * @return true if action is handled
   7886          * @see View#performAccessibilityAction(int, android.os.Bundle)
   7887          */
   7888         public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
   7889                 int action, Bundle args) {
   7890             return false;
   7891         }
   7892 
   7893         /**
   7894          * Parse the xml attributes to get the most common properties used by layout managers.
   7895          *
   7896          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
   7897          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
   7898          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
   7899          * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
   7900          *
   7901          * @return an object containing the properties as specified in the attrs.
   7902          */
   7903         public static Properties getProperties(Context context, AttributeSet attrs,
   7904                 int defStyleAttr, int defStyleRes) {
   7905             Properties properties = new Properties();
   7906             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
   7907                     defStyleAttr, defStyleRes);
   7908             properties.orientation = a.getInt(R.styleable.RecyclerView_android_orientation, VERTICAL);
   7909             properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
   7910             properties.reverseLayout = a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
   7911             properties.stackFromEnd = a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
   7912             a.recycle();
   7913             return properties;
   7914         }
   7915 
   7916         /**
   7917          * Some general properties that a LayoutManager may want to use.
   7918          */
   7919         public static class Properties {
   7920             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
   7921             public int orientation;
   7922             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
   7923             public int spanCount;
   7924             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
   7925             public boolean reverseLayout;
   7926             /** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
   7927             public boolean stackFromEnd;
   7928         }
   7929     }
   7930 
   7931     /**
   7932      * An ItemDecoration allows the application to add a special drawing and layout offset
   7933      * to specific item views from the adapter's data set. This can be useful for drawing dividers
   7934      * between items, highlights, visual grouping boundaries and more.
   7935      *
   7936      * <p>All ItemDecorations are drawn in the order they were added, before the item
   7937      * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()}
   7938      * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
   7939      * RecyclerView.State)}.</p>
   7940      */
   7941     public static abstract class ItemDecoration {
   7942         /**
   7943          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   7944          * Any content drawn by this method will be drawn before the item views are drawn,
   7945          * and will thus appear underneath the views.
   7946          *
   7947          * @param c Canvas to draw into
   7948          * @param parent RecyclerView this ItemDecoration is drawing into
   7949          * @param state The current state of RecyclerView
   7950          */
   7951         public void onDraw(Canvas c, RecyclerView parent, State state) {
   7952             onDraw(c, parent);
   7953         }
   7954 
   7955         /**
   7956          * @deprecated
   7957          * Override {@link #onDraw(Canvas, RecyclerView, RecyclerView.State)}
   7958          */
   7959         @Deprecated
   7960         public void onDraw(Canvas c, RecyclerView parent) {
   7961         }
   7962 
   7963         /**
   7964          * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
   7965          * Any content drawn by this method will be drawn after the item views are drawn
   7966          * and will thus appear over the views.
   7967          *
   7968          * @param c Canvas to draw into
   7969          * @param parent RecyclerView this ItemDecoration is drawing into
   7970          * @param state The current state of RecyclerView.
   7971          */
   7972         public void onDrawOver(Canvas c, RecyclerView parent, State state) {
   7973             onDrawOver(c, parent);
   7974         }
   7975 
   7976         /**
   7977          * @deprecated
   7978          * Override {@link #onDrawOver(Canvas, RecyclerView, RecyclerView.State)}
   7979          */
   7980         @Deprecated
   7981         public void onDrawOver(Canvas c, RecyclerView parent) {
   7982         }
   7983 
   7984 
   7985         /**
   7986          * @deprecated
   7987          * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
   7988          */
   7989         @Deprecated
   7990         public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
   7991             outRect.set(0, 0, 0, 0);
   7992         }
   7993 
   7994         /**
   7995          * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
   7996          * the number of pixels that the item view should be inset by, similar to padding or margin.
   7997          * The default implementation sets the bounds of outRect to 0 and returns.
   7998          *
   7999          * <p>
   8000          * If this ItemDecoration does not affect the positioning of item views, it should set
   8001          * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
   8002          * before returning.
   8003          *
   8004          * <p>
   8005          * If you need to access Adapter for additional data, you can call
   8006          * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
   8007          * View.
   8008          *
   8009          * @param outRect Rect to receive the output.
   8010          * @param view    The child view to decorate
   8011          * @param parent  RecyclerView this ItemDecoration is decorating
   8012          * @param state   The current state of RecyclerView.
   8013          */
   8014         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
   8015             getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
   8016                     parent);
   8017         }
   8018     }
   8019 
   8020     /**
   8021      * An OnItemTouchListener allows the application to intercept touch events in progress at the
   8022      * view hierarchy level of the RecyclerView before those touch events are considered for
   8023      * RecyclerView's own scrolling behavior.
   8024      *
   8025      * <p>This can be useful for applications that wish to implement various forms of gestural
   8026      * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
   8027      * a touch interaction already in progress even if the RecyclerView is already handling that
   8028      * gesture stream itself for the purposes of scrolling.</p>
   8029      *
   8030      * @see SimpleOnItemTouchListener
   8031      */
   8032     public static interface OnItemTouchListener {
   8033         /**
   8034          * Silently observe and/or take over touch events sent to the RecyclerView
   8035          * before they are handled by either the RecyclerView itself or its child views.
   8036          *
   8037          * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
   8038          * in the order in which each listener was added, before any other touch processing
   8039          * by the RecyclerView itself or child views occurs.</p>
   8040          *
   8041          * @param e MotionEvent describing the touch event. All coordinates are in
   8042          *          the RecyclerView's coordinate system.
   8043          * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
   8044          *         to continue with the current behavior and continue observing future events in
   8045          *         the gesture.
   8046          */
   8047         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
   8048 
   8049         /**
   8050          * Process a touch event as part of a gesture that was claimed by returning true from
   8051          * a previous call to {@link #onInterceptTouchEvent}.
   8052          *
   8053          * @param e MotionEvent describing the touch event. All coordinates are in
   8054          *          the RecyclerView's coordinate system.
   8055          */
   8056         public void onTouchEvent(RecyclerView rv, MotionEvent e);
   8057 
   8058         /**
   8059          * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
   8060          * intercept touch events with
   8061          * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
   8062          *
   8063          * @param disallowIntercept True if the child does not want the parent to
   8064          *            intercept touch events.
   8065          * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
   8066          */
   8067         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
   8068     }
   8069 
   8070     /**
   8071      * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
   8072      * default return values.
   8073      * <p>
   8074      * You may prefer to extend this class if you don't need to override all methods. Another
   8075      * benefit of using this class is future compatibility. As the interface may change, we'll
   8076      * always provide a default implementation on this class so that your code won't break when
   8077      * you update to a new version of the support library.
   8078      */
   8079     public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
   8080         @Override
   8081         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
   8082             return false;
   8083         }
   8084 
   8085         @Override
   8086         public void onTouchEvent(RecyclerView rv, MotionEvent e) {
   8087         }
   8088 
   8089         @Override
   8090         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
   8091         }
   8092     }
   8093 
   8094 
   8095     /**
   8096      * An OnScrollListener can be set on a RecyclerView to receive messages
   8097      * when a scrolling event has occurred on that RecyclerView.
   8098      *
   8099      * @see RecyclerView#setOnScrollListener(OnScrollListener) and
   8100      * RecyclerView#addOnScrollListener(OnScrollListener)
   8101      *
   8102      * If you are planning to have several listeners at the same time, use
   8103      * RecyclerView#addOnScrollListener. If there will be only one listener at the time and you
   8104      * want your components to be able to easily replace the listener use
   8105      * RecyclerView#setOnScrollListener.
   8106      */
   8107     public abstract static class OnScrollListener {
   8108         /**
   8109          * Callback method to be invoked when RecyclerView's scroll state changes.
   8110          *
   8111          * @param recyclerView The RecyclerView whose scroll state has changed.
   8112          * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
   8113          *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
   8114          */
   8115         public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
   8116 
   8117         /**
   8118          * Callback method to be invoked when the RecyclerView has been scrolled. This will be
   8119          * called after the scroll has completed.
   8120          * <p>
   8121          * This callback will also be called if visible item range changes after a layout
   8122          * calculation. In that case, dx and dy will be 0.
   8123          *
   8124          * @param recyclerView The RecyclerView which scrolled.
   8125          * @param dx The amount of horizontal scroll.
   8126          * @param dy The amount of vertical scroll.
   8127          */
   8128         public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
   8129     }
   8130 
   8131     /**
   8132      * A RecyclerListener can be set on a RecyclerView to receive messages whenever
   8133      * a view is recycled.
   8134      *
   8135      * @see RecyclerView#setRecyclerListener(RecyclerListener)
   8136      */
   8137     public interface RecyclerListener {
   8138 
   8139         /**
   8140          * This method is called whenever the view in the ViewHolder is recycled.
   8141          *
   8142          * RecyclerView calls this method right before clearing ViewHolder's internal data and
   8143          * sending it to RecycledViewPool. This way, if ViewHolder was holding valid information
   8144          * before being recycled, you can call {@link ViewHolder#getAdapterPosition()} to get
   8145          * its adapter position.
   8146          *
   8147          * @param holder The ViewHolder containing the view that was recycled
   8148          */
   8149         public void onViewRecycled(ViewHolder holder);
   8150     }
   8151 
   8152     /**
   8153      * A Listener interface that can be attached to a RecylcerView to get notified
   8154      * whenever a ViewHolder is attached to or detached from RecyclerView.
   8155      */
   8156     public interface OnChildAttachStateChangeListener {
   8157 
   8158         /**
   8159          * Called when a view is attached to the RecyclerView.
   8160          *
   8161          * @param view The View which is attached to the RecyclerView
   8162          */
   8163         public void onChildViewAttachedToWindow(View view);
   8164 
   8165         /**
   8166          * Called when a view is detached from RecyclerView.
   8167          *
   8168          * @param view The View which is being detached from the RecyclerView
   8169          */
   8170         public void onChildViewDetachedFromWindow(View view);
   8171     }
   8172 
   8173     /**
   8174      * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
   8175      *
   8176      * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching
   8177      * potentially expensive {@link View#findViewById(int)} results.</p>
   8178      *
   8179      * <p>While {@link LayoutParams} belong to the {@link LayoutManager},
   8180      * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
   8181      * their own custom ViewHolder implementations to store data that makes binding view contents
   8182      * easier. Implementations should assume that individual item views will hold strong references
   8183      * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold
   8184      * strong references to extra off-screen item views for caching purposes</p>
   8185      */
   8186     public static abstract class ViewHolder {
   8187         public final View itemView;
   8188         int mPosition = NO_POSITION;
   8189         int mOldPosition = NO_POSITION;
   8190         long mItemId = NO_ID;
   8191         int mItemViewType = INVALID_TYPE;
   8192         int mPreLayoutPosition = NO_POSITION;
   8193 
   8194         // The item that this holder is shadowing during an item change event/animation
   8195         ViewHolder mShadowedHolder = null;
   8196         // The item that is shadowing this holder during an item change event/animation
   8197         ViewHolder mShadowingHolder = null;
   8198 
   8199         /**
   8200          * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
   8201          * are all valid.
   8202          */
   8203         static final int FLAG_BOUND = 1 << 0;
   8204 
   8205         /**
   8206          * The data this ViewHolder's view reflects is stale and needs to be rebound
   8207          * by the adapter. mPosition and mItemId are consistent.
   8208          */
   8209         static final int FLAG_UPDATE = 1 << 1;
   8210 
   8211         /**
   8212          * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
   8213          * are not to be trusted and may no longer match the item view type.
   8214          * This ViewHolder must be fully rebound to different data.
   8215          */
   8216         static final int FLAG_INVALID = 1 << 2;
   8217 
   8218         /**
   8219          * This ViewHolder points at data that represents an item previously removed from the
   8220          * data set. Its view may still be used for things like outgoing animations.
   8221          */
   8222         static final int FLAG_REMOVED = 1 << 3;
   8223 
   8224         /**
   8225          * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
   8226          * and is intended to keep views around during animations.
   8227          */
   8228         static final int FLAG_NOT_RECYCLABLE = 1 << 4;
   8229 
   8230         /**
   8231          * This ViewHolder is returned from scrap which means we are expecting an addView call
   8232          * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
   8233          * the end of the layout pass and then recycled by RecyclerView if it is not added back to
   8234          * the RecyclerView.
   8235          */
   8236         static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
   8237 
   8238         /**
   8239          * This ViewHolder's contents have changed. This flag is used as an indication that
   8240          * change animations may be used, if supported by the ItemAnimator.
   8241          */
   8242         static final int FLAG_CHANGED = 1 << 6;
   8243 
   8244         /**
   8245          * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
   8246          * it unless LayoutManager is replaced.
   8247          * It is still fully visible to the LayoutManager.
   8248          */
   8249         static final int FLAG_IGNORE = 1 << 7;
   8250 
   8251         /**
   8252          * When the View is detached form the parent, we set this flag so that we can take correct
   8253          * action when we need to remove it or add it back.
   8254          */
   8255         static final int FLAG_TMP_DETACHED = 1 << 8;
   8256 
   8257         /**
   8258          * Set when we can no longer determine the adapter position of this ViewHolder until it is
   8259          * rebound to a new position. It is different than FLAG_INVALID because FLAG_INVALID is
   8260          * set even when the type does not match. Also, FLAG_ADAPTER_POSITION_UNKNOWN is set as soon
   8261          * as adapter notification arrives vs FLAG_INVALID is set lazily before layout is
   8262          * re-calculated.
   8263          */
   8264         static final int FLAG_ADAPTER_POSITION_UNKNOWN = 1 << 9;
   8265 
   8266         /**
   8267          * Set when a addChangePayload(null) is called
   8268          */
   8269         static final int FLAG_ADAPTER_FULLUPDATE = 1 << 10;
   8270 
   8271         private int mFlags;
   8272 
   8273         private static final List<Object> FULLUPDATE_PAYLOADS = Collections.EMPTY_LIST;
   8274 
   8275         List<Object> mPayloads = null;
   8276         List<Object> mUnmodifiedPayloads = null;
   8277 
   8278         private int mIsRecyclableCount = 0;
   8279 
   8280         // If non-null, view is currently considered scrap and may be reused for other data by the
   8281         // scrap container.
   8282         private Recycler mScrapContainer = null;
   8283 
   8284         // Saves isImportantForAccessibility value for the view item while it's in hidden state and
   8285         // marked as unimportant for accessibility.
   8286         private int mWasImportantForAccessibilityBeforeHidden =
   8287                 ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   8288 
   8289         /**
   8290          * Is set when VH is bound from the adapter and cleaned right before it is sent to
   8291          * {@link RecycledViewPool}.
   8292          */
   8293         RecyclerView mOwnerRecyclerView;
   8294 
   8295         public ViewHolder(View itemView) {
   8296             if (itemView == null) {
   8297                 throw new IllegalArgumentException("itemView may not be null");
   8298             }
   8299             this.itemView = itemView;
   8300         }
   8301 
   8302         void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
   8303             addFlags(ViewHolder.FLAG_REMOVED);
   8304             offsetPosition(offset, applyToPreLayout);
   8305             mPosition = mNewPosition;
   8306         }
   8307 
   8308         void offsetPosition(int offset, boolean applyToPreLayout) {
   8309             if (mOldPosition == NO_POSITION) {
   8310                 mOldPosition = mPosition;
   8311             }
   8312             if (mPreLayoutPosition == NO_POSITION) {
   8313                 mPreLayoutPosition = mPosition;
   8314             }
   8315             if (applyToPreLayout) {
   8316                 mPreLayoutPosition += offset;
   8317             }
   8318             mPosition += offset;
   8319             if (itemView.getLayoutParams() != null) {
   8320                 ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
   8321             }
   8322         }
   8323 
   8324         void clearOldPosition() {
   8325             mOldPosition = NO_POSITION;
   8326             mPreLayoutPosition = NO_POSITION;
   8327         }
   8328 
   8329         void saveOldPosition() {
   8330             if (mOldPosition == NO_POSITION) {
   8331                 mOldPosition = mPosition;
   8332             }
   8333         }
   8334 
   8335         boolean shouldIgnore() {
   8336             return (mFlags & FLAG_IGNORE) != 0;
   8337         }
   8338 
   8339         /**
   8340          * @deprecated This method is deprecated because its meaning is ambiguous due to the async
   8341          * handling of adapter updates. Please use {@link #getLayoutPosition()} or
   8342          * {@link #getAdapterPosition()} depending on your use case.
   8343          *
   8344          * @see #getLayoutPosition()
   8345          * @see #getAdapterPosition()
   8346          */
   8347         @Deprecated
   8348         public final int getPosition() {
   8349             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   8350         }
   8351 
   8352         /**
   8353          * Returns the position of the ViewHolder in terms of the latest layout pass.
   8354          * <p>
   8355          * This position is mostly used by RecyclerView components to be consistent while
   8356          * RecyclerView lazily processes adapter updates.
   8357          * <p>
   8358          * For performance and animation reasons, RecyclerView batches all adapter updates until the
   8359          * next layout pass. This may cause mismatches between the Adapter position of the item and
   8360          * the position it had in the latest layout calculations.
   8361          * <p>
   8362          * LayoutManagers should always call this method while doing calculations based on item
   8363          * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
   8364          * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
   8365          * of the item.
   8366          * <p>
   8367          * If LayoutManager needs to call an external method that requires the adapter position of
   8368          * the item, it can use {@link #getAdapterPosition()} or
   8369          * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
   8370          *
   8371          * @return Returns the adapter position of the ViewHolder in the latest layout pass.
   8372          * @see #getAdapterPosition()
   8373          */
   8374         public final int getLayoutPosition() {
   8375             return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
   8376         }
   8377 
   8378         /**
   8379          * Returns the Adapter position of the item represented by this ViewHolder.
   8380          * <p>
   8381          * Note that this might be different than the {@link #getLayoutPosition()} if there are
   8382          * pending adapter updates but a new layout pass has not happened yet.
   8383          * <p>
   8384          * RecyclerView does not handle any adapter updates until the next layout traversal. This
   8385          * may create temporary inconsistencies between what user sees on the screen and what
   8386          * adapter contents have. This inconsistency is not important since it will be less than
   8387          * 16ms but it might be a problem if you want to use ViewHolder position to access the
   8388          * adapter. Sometimes, you may need to get the exact adapter position to do
   8389          * some actions in response to user events. In that case, you should use this method which
   8390          * will calculate the Adapter position of the ViewHolder.
   8391          * <p>
   8392          * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
   8393          * next layout pass, the return value of this method will be {@link #NO_POSITION}.
   8394          *
   8395          * @return The adapter position of the item if it still exists in the adapter.
   8396          * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
   8397          * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
   8398          * layout pass or the ViewHolder has already been recycled.
   8399          */
   8400         public final int getAdapterPosition() {
   8401             if (mOwnerRecyclerView == null) {
   8402                 return NO_POSITION;
   8403             }
   8404             return mOwnerRecyclerView.getAdapterPositionFor(this);
   8405         }
   8406 
   8407         /**
   8408          * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
   8409          * to perform animations.
   8410          * <p>
   8411          * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
   8412          * adapter index in the previous layout.
   8413          *
   8414          * @return The previous adapter index of the Item represented by this ViewHolder or
   8415          * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
   8416          * complete).
   8417          */
   8418         public final int getOldPosition() {
   8419             return mOldPosition;
   8420         }
   8421 
   8422         /**
   8423          * Returns The itemId represented by this ViewHolder.
   8424          *
   8425          * @return The the item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
   8426          * otherwise
   8427          */
   8428         public final long getItemId() {
   8429             return mItemId;
   8430         }
   8431 
   8432         /**
   8433          * @return The view type of this ViewHolder.
   8434          */
   8435         public final int getItemViewType() {
   8436             return mItemViewType;
   8437         }
   8438 
   8439         boolean isScrap() {
   8440             return mScrapContainer != null;
   8441         }
   8442 
   8443         void unScrap() {
   8444             mScrapContainer.unscrapView(this);
   8445         }
   8446 
   8447         boolean wasReturnedFromScrap() {
   8448             return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
   8449         }
   8450 
   8451         void clearReturnedFromScrapFlag() {
   8452             mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
   8453         }
   8454 
   8455         void clearTmpDetachFlag() {
   8456             mFlags = mFlags & ~FLAG_TMP_DETACHED;
   8457         }
   8458 
   8459         void stopIgnoring() {
   8460             mFlags = mFlags & ~FLAG_IGNORE;
   8461         }
   8462 
   8463         void setScrapContainer(Recycler recycler) {
   8464             mScrapContainer = recycler;
   8465         }
   8466 
   8467         boolean isInvalid() {
   8468             return (mFlags & FLAG_INVALID) != 0;
   8469         }
   8470 
   8471         boolean needsUpdate() {
   8472             return (mFlags & FLAG_UPDATE) != 0;
   8473         }
   8474 
   8475         boolean isChanged() {
   8476             return (mFlags & FLAG_CHANGED) != 0;
   8477         }
   8478 
   8479         boolean isBound() {
   8480             return (mFlags & FLAG_BOUND) != 0;
   8481         }
   8482 
   8483         boolean isRemoved() {
   8484             return (mFlags & FLAG_REMOVED) != 0;
   8485         }
   8486 
   8487         boolean hasAnyOfTheFlags(int flags) {
   8488             return (mFlags & flags) != 0;
   8489         }
   8490 
   8491         boolean isTmpDetached() {
   8492             return (mFlags & FLAG_TMP_DETACHED) != 0;
   8493         }
   8494 
   8495         boolean isAdapterPositionUnknown() {
   8496             return (mFlags & FLAG_ADAPTER_POSITION_UNKNOWN) != 0 || isInvalid();
   8497         }
   8498 
   8499         void setFlags(int flags, int mask) {
   8500             mFlags = (mFlags & ~mask) | (flags & mask);
   8501         }
   8502 
   8503         void addFlags(int flags) {
   8504             mFlags |= flags;
   8505         }
   8506 
   8507         void addChangePayload(Object payload) {
   8508             if (payload == null) {
   8509                 addFlags(FLAG_ADAPTER_FULLUPDATE);
   8510             } else if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   8511                 createPayloadsIfNeeded();
   8512                 mPayloads.add(payload);
   8513             }
   8514         }
   8515 
   8516         private void createPayloadsIfNeeded() {
   8517             if (mPayloads == null) {
   8518                 mPayloads = new ArrayList<Object>();
   8519                 mUnmodifiedPayloads = Collections.unmodifiableList(mPayloads);
   8520             }
   8521         }
   8522 
   8523         void clearPayload() {
   8524             if (mPayloads != null) {
   8525                 mPayloads.clear();
   8526             }
   8527             mFlags = mFlags & ~FLAG_ADAPTER_FULLUPDATE;
   8528         }
   8529 
   8530         List<Object> getUnmodifiedPayloads() {
   8531             if ((mFlags & FLAG_ADAPTER_FULLUPDATE) == 0) {
   8532                 if (mPayloads == null || mPayloads.size() == 0) {
   8533                     // Initial state,  no update being called.
   8534                     return FULLUPDATE_PAYLOADS;
   8535                 }
   8536                 // there are none-null payloads
   8537                 return mUnmodifiedPayloads;
   8538             } else {
   8539                 // a full update has been called.
   8540                 return FULLUPDATE_PAYLOADS;
   8541             }
   8542         }
   8543 
   8544         void resetInternal() {
   8545             mFlags = 0;
   8546             mPosition = NO_POSITION;
   8547             mOldPosition = NO_POSITION;
   8548             mItemId = NO_ID;
   8549             mPreLayoutPosition = NO_POSITION;
   8550             mIsRecyclableCount = 0;
   8551             mShadowedHolder = null;
   8552             mShadowingHolder = null;
   8553             clearPayload();
   8554             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   8555         }
   8556 
   8557         /**
   8558          * Called when the child view enters the hidden state
   8559          */
   8560         private void onEnteredHiddenState() {
   8561             // While the view item is in hidden state, make it invisible for the accessibility.
   8562             mWasImportantForAccessibilityBeforeHidden =
   8563                     ViewCompat.getImportantForAccessibility(itemView);
   8564             ViewCompat.setImportantForAccessibility(itemView,
   8565                     ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
   8566         }
   8567 
   8568         /**
   8569          * Called when the child view leaves the hidden state
   8570          */
   8571         private void onLeftHiddenState() {
   8572             ViewCompat.setImportantForAccessibility(
   8573                     itemView, mWasImportantForAccessibilityBeforeHidden);
   8574             mWasImportantForAccessibilityBeforeHidden = ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
   8575         }
   8576 
   8577         @Override
   8578         public String toString() {
   8579             final StringBuilder sb = new StringBuilder("ViewHolder{" +
   8580                     Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
   8581                     ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
   8582             if (isScrap()) sb.append(" scrap");
   8583             if (isInvalid()) sb.append(" invalid");
   8584             if (!isBound()) sb.append(" unbound");
   8585             if (needsUpdate()) sb.append(" update");
   8586             if (isRemoved()) sb.append(" removed");
   8587             if (shouldIgnore()) sb.append(" ignored");
   8588             if (isChanged()) sb.append(" changed");
   8589             if (isTmpDetached()) sb.append(" tmpDetached");
   8590             if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
   8591             if (isAdapterPositionUnknown()) sb.append("undefined adapter position");
   8592 
   8593             if (itemView.getParent() == null) sb.append(" no parent");
   8594             sb.append("}");
   8595             return sb.toString();
   8596         }
   8597 
   8598         /**
   8599          * Informs the recycler whether this item can be recycled. Views which are not
   8600          * recyclable will not be reused for other items until setIsRecyclable() is
   8601          * later set to true. Calls to setIsRecyclable() should always be paired (one
   8602          * call to setIsRecyclabe(false) should always be matched with a later call to
   8603          * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
   8604          * reference-counted.
   8605          *
   8606          * @param recyclable Whether this item is available to be recycled. Default value
   8607          * is true.
   8608          */
   8609         public final void setIsRecyclable(boolean recyclable) {
   8610             mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
   8611             if (mIsRecyclableCount < 0) {
   8612                 mIsRecyclableCount = 0;
   8613                 if (DEBUG) {
   8614                     throw new RuntimeException("isRecyclable decremented below 0: " +
   8615                             "unmatched pair of setIsRecyable() calls for " + this);
   8616                 }
   8617                 Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
   8618                         "unmatched pair of setIsRecyable() calls for " + this);
   8619             } else if (!recyclable && mIsRecyclableCount == 1) {
   8620                 mFlags |= FLAG_NOT_RECYCLABLE;
   8621             } else if (recyclable && mIsRecyclableCount == 0) {
   8622                 mFlags &= ~FLAG_NOT_RECYCLABLE;
   8623             }
   8624             if (DEBUG) {
   8625                 Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
   8626             }
   8627         }
   8628 
   8629         /**
   8630          * @see {@link #setIsRecyclable(boolean)}
   8631          *
   8632          * @return true if this item is available to be recycled, false otherwise.
   8633          */
   8634         public final boolean isRecyclable() {
   8635             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
   8636                     !ViewCompat.hasTransientState(itemView);
   8637         }
   8638 
   8639         /**
   8640          * Returns whether we have animations referring to this view holder or not.
   8641          * This is similar to isRecyclable flag but does not check transient state.
   8642          */
   8643         private boolean shouldBeKeptAsChild() {
   8644             return (mFlags & FLAG_NOT_RECYCLABLE) != 0;
   8645         }
   8646 
   8647         /**
   8648          * @return True if ViewHolder is not refenrenced by RecyclerView animations but has
   8649          * transient state which will prevent it from being recycled.
   8650          */
   8651         private boolean doesTransientStatePreventRecycling() {
   8652             return (mFlags & FLAG_NOT_RECYCLABLE) == 0 && ViewCompat.hasTransientState(itemView);
   8653         }
   8654     }
   8655 
   8656     private int getAdapterPositionFor(ViewHolder viewHolder) {
   8657         if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |
   8658                 ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
   8659                 || !viewHolder.isBound()) {
   8660             return RecyclerView.NO_POSITION;
   8661         }
   8662         return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
   8663     }
   8664 
   8665     // NestedScrollingChild
   8666 
   8667     @Override
   8668     public void setNestedScrollingEnabled(boolean enabled) {
   8669         mScrollingChildHelper.setNestedScrollingEnabled(enabled);
   8670     }
   8671 
   8672     @Override
   8673     public boolean isNestedScrollingEnabled() {
   8674         return mScrollingChildHelper.isNestedScrollingEnabled();
   8675     }
   8676 
   8677     @Override
   8678     public boolean startNestedScroll(int axes) {
   8679         return mScrollingChildHelper.startNestedScroll(axes);
   8680     }
   8681 
   8682     @Override
   8683     public void stopNestedScroll() {
   8684         mScrollingChildHelper.stopNestedScroll();
   8685     }
   8686 
   8687     @Override
   8688     public boolean hasNestedScrollingParent() {
   8689         return mScrollingChildHelper.hasNestedScrollingParent();
   8690     }
   8691 
   8692     @Override
   8693     public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
   8694             int dyUnconsumed, int[] offsetInWindow) {
   8695         return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
   8696                 dxUnconsumed, dyUnconsumed, offsetInWindow);
   8697     }
   8698 
   8699     @Override
   8700     public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
   8701         return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
   8702     }
   8703 
   8704     @Override
   8705     public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
   8706         return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
   8707     }
   8708 
   8709     @Override
   8710     public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
   8711         return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
   8712     }
   8713 
   8714     /**
   8715      * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
   8716      * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
   8717      * to create their own subclass of this <code>LayoutParams</code> class
   8718      * to store any additional required per-child view metadata about the layout.
   8719      */
   8720     public static class LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
   8721         ViewHolder mViewHolder;
   8722         final Rect mDecorInsets = new Rect();
   8723         boolean mInsetsDirty = true;
   8724         // Flag is set to true if the view is bound while it is detached from RV.
   8725         // In this case, we need to manually call invalidate after view is added to guarantee that
   8726         // invalidation is populated through the View hierarchy
   8727         boolean mPendingInvalidate = false;
   8728 
   8729         public LayoutParams(Context c, AttributeSet attrs) {
   8730             super(c, attrs);
   8731         }
   8732 
   8733         public LayoutParams(int width, int height) {
   8734             super(width, height);
   8735         }
   8736 
   8737         public LayoutParams(MarginLayoutParams source) {
   8738             super(source);
   8739         }
   8740 
   8741         public LayoutParams(ViewGroup.LayoutParams source) {
   8742             super(source);
   8743         }
   8744 
   8745         public LayoutParams(LayoutParams source) {
   8746             super((ViewGroup.LayoutParams) source);
   8747         }
   8748 
   8749         /**
   8750          * Returns true if the view this LayoutParams is attached to needs to have its content
   8751          * updated from the corresponding adapter.
   8752          *
   8753          * @return true if the view should have its content updated
   8754          */
   8755         public boolean viewNeedsUpdate() {
   8756             return mViewHolder.needsUpdate();
   8757         }
   8758 
   8759         /**
   8760          * Returns true if the view this LayoutParams is attached to is now representing
   8761          * potentially invalid data. A LayoutManager should scrap/recycle it.
   8762          *
   8763          * @return true if the view is invalid
   8764          */
   8765         public boolean isViewInvalid() {
   8766             return mViewHolder.isInvalid();
   8767         }
   8768 
   8769         /**
   8770          * Returns true if the adapter data item corresponding to the view this LayoutParams
   8771          * is attached to has been removed from the data set. A LayoutManager may choose to
   8772          * treat it differently in order to animate its outgoing or disappearing state.
   8773          *
   8774          * @return true if the item the view corresponds to was removed from the data set
   8775          */
   8776         public boolean isItemRemoved() {
   8777             return mViewHolder.isRemoved();
   8778         }
   8779 
   8780         /**
   8781          * Returns true if the adapter data item corresponding to the view this LayoutParams
   8782          * is attached to has been changed in the data set. A LayoutManager may choose to
   8783          * treat it differently in order to animate its changing state.
   8784          *
   8785          * @return true if the item the view corresponds to was changed in the data set
   8786          */
   8787         public boolean isItemChanged() {
   8788             return mViewHolder.isChanged();
   8789         }
   8790 
   8791         /**
   8792          * @deprecated use {@link #getViewLayoutPosition()} or {@link #getViewAdapterPosition()}
   8793          */
   8794         public int getViewPosition() {
   8795             return mViewHolder.getPosition();
   8796         }
   8797 
   8798         /**
   8799          * Returns the adapter position that the view this LayoutParams is attached to corresponds
   8800          * to as of latest layout calculation.
   8801          *
   8802          * @return the adapter position this view as of latest layout pass
   8803          */
   8804         public int getViewLayoutPosition() {
   8805             return mViewHolder.getLayoutPosition();
   8806         }
   8807 
   8808         /**
   8809          * Returns the up-to-date adapter position that the view this LayoutParams is attached to
   8810          * corresponds to.
   8811          *
   8812          * @return the up-to-date adapter position this view. It may return
   8813          * {@link RecyclerView#NO_POSITION} if item represented by this View has been removed or
   8814          * its up-to-date position cannot be calculated.
   8815          */
   8816         public int getViewAdapterPosition() {
   8817             return mViewHolder.getAdapterPosition();
   8818         }
   8819     }
   8820 
   8821     /**
   8822      * Observer base class for watching changes to an {@link Adapter}.
   8823      * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
   8824      */
   8825     public static abstract class AdapterDataObserver {
   8826         public void onChanged() {
   8827             // Do nothing
   8828         }
   8829 
   8830         public void onItemRangeChanged(int positionStart, int itemCount) {
   8831             // do nothing
   8832         }
   8833 
   8834         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
   8835             // fallback to onItemRangeChanged(positionStart, itemCount) if app
   8836             // does not override this method.
   8837             onItemRangeChanged(positionStart, itemCount);
   8838         }
   8839 
   8840         public void onItemRangeInserted(int positionStart, int itemCount) {
   8841             // do nothing
   8842         }
   8843 
   8844         public void onItemRangeRemoved(int positionStart, int itemCount) {
   8845             // do nothing
   8846         }
   8847 
   8848         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
   8849             // do nothing
   8850         }
   8851     }
   8852 
   8853     /**
   8854      * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and
   8855      * provides methods to trigger a programmatic scroll.</p>
   8856      *
   8857      * @see LinearSmoothScroller
   8858      */
   8859     public static abstract class SmoothScroller {
   8860 
   8861         private int mTargetPosition = RecyclerView.NO_POSITION;
   8862 
   8863         private RecyclerView mRecyclerView;
   8864 
   8865         private LayoutManager mLayoutManager;
   8866 
   8867         private boolean mPendingInitialRun;
   8868 
   8869         private boolean mRunning;
   8870 
   8871         private View mTargetView;
   8872 
   8873         private final Action mRecyclingAction;
   8874 
   8875         public SmoothScroller() {
   8876             mRecyclingAction = new Action(0, 0);
   8877         }
   8878 
   8879         /**
   8880          * Starts a smooth scroll for the given target position.
   8881          * <p>In each animation step, {@link RecyclerView} will check
   8882          * for the target view and call either
   8883          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   8884          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until
   8885          * SmoothScroller is stopped.</p>
   8886          *
   8887          * <p>Note that if RecyclerView finds the target view, it will automatically stop the
   8888          * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will
   8889          * stop calling SmoothScroller in each animation step.</p>
   8890          */
   8891         void start(RecyclerView recyclerView, LayoutManager layoutManager) {
   8892             mRecyclerView = recyclerView;
   8893             mLayoutManager = layoutManager;
   8894             if (mTargetPosition == RecyclerView.NO_POSITION) {
   8895                 throw new IllegalArgumentException("Invalid target position");
   8896             }
   8897             mRecyclerView.mState.mTargetPosition = mTargetPosition;
   8898             mRunning = true;
   8899             mPendingInitialRun = true;
   8900             mTargetView = findViewByPosition(getTargetPosition());
   8901             onStart();
   8902             mRecyclerView.mViewFlinger.postOnAnimation();
   8903         }
   8904 
   8905         public void setTargetPosition(int targetPosition) {
   8906             mTargetPosition = targetPosition;
   8907         }
   8908 
   8909         /**
   8910          * @return The LayoutManager to which this SmoothScroller is attached. Will return
   8911          * <code>null</code> after the SmoothScroller is stopped.
   8912          */
   8913         @Nullable
   8914         public LayoutManager getLayoutManager() {
   8915             return mLayoutManager;
   8916         }
   8917 
   8918         /**
   8919          * Stops running the SmoothScroller in each animation callback. Note that this does not
   8920          * cancel any existing {@link Action} updated by
   8921          * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or
   8922          * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.
   8923          */
   8924         final protected void stop() {
   8925             if (!mRunning) {
   8926                 return;
   8927             }
   8928             onStop();
   8929             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
   8930             mTargetView = null;
   8931             mTargetPosition = RecyclerView.NO_POSITION;
   8932             mPendingInitialRun = false;
   8933             mRunning = false;
   8934             // trigger a cleanup
   8935             mLayoutManager.onSmoothScrollerStopped(this);
   8936             // clear references to avoid any potential leak by a custom smooth scroller
   8937             mLayoutManager = null;
   8938             mRecyclerView = null;
   8939         }
   8940 
   8941         /**
   8942          * Returns true if SmoothScroller has been started but has not received the first
   8943          * animation
   8944          * callback yet.
   8945          *
   8946          * @return True if this SmoothScroller is waiting to start
   8947          */
   8948         public boolean isPendingInitialRun() {
   8949             return mPendingInitialRun;
   8950         }
   8951 
   8952 
   8953         /**
   8954          * @return True if SmoothScroller is currently active
   8955          */
   8956         public boolean isRunning() {
   8957             return mRunning;
   8958         }
   8959 
   8960         /**
   8961          * Returns the adapter position of the target item
   8962          *
   8963          * @return Adapter position of the target item or
   8964          * {@link RecyclerView#NO_POSITION} if no target view is set.
   8965          */
   8966         public int getTargetPosition() {
   8967             return mTargetPosition;
   8968         }
   8969 
   8970         private void onAnimation(int dx, int dy) {
   8971             final RecyclerView recyclerView = mRecyclerView;
   8972             if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION || recyclerView == null) {
   8973                 stop();
   8974             }
   8975             mPendingInitialRun = false;
   8976             if (mTargetView != null) {
   8977                 // verify target position
   8978                 if (getChildPosition(mTargetView) == mTargetPosition) {
   8979                     onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
   8980                     mRecyclingAction.runIfNecessary(recyclerView);
   8981                     stop();
   8982                 } else {
   8983                     Log.e(TAG, "Passed over target position while smooth scrolling.");
   8984                     mTargetView = null;
   8985                 }
   8986             }
   8987             if (mRunning) {
   8988                 onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
   8989                 boolean hadJumpTarget = mRecyclingAction.hasJumpTarget();
   8990                 mRecyclingAction.runIfNecessary(recyclerView);
   8991                 if (hadJumpTarget) {
   8992                     // It is not stopped so needs to be restarted
   8993                     if (mRunning) {
   8994                         mPendingInitialRun = true;
   8995                         recyclerView.mViewFlinger.postOnAnimation();
   8996                     } else {
   8997                         stop(); // done
   8998                     }
   8999                 }
   9000             }
   9001         }
   9002 
   9003         /**
   9004          * @see RecyclerView#getChildLayoutPosition(android.view.View)
   9005          */
   9006         public int getChildPosition(View view) {
   9007             return mRecyclerView.getChildLayoutPosition(view);
   9008         }
   9009 
   9010         /**
   9011          * @see RecyclerView.LayoutManager#getChildCount()
   9012          */
   9013         public int getChildCount() {
   9014             return mRecyclerView.mLayout.getChildCount();
   9015         }
   9016 
   9017         /**
   9018          * @see RecyclerView.LayoutManager#findViewByPosition(int)
   9019          */
   9020         public View findViewByPosition(int position) {
   9021             return mRecyclerView.mLayout.findViewByPosition(position);
   9022         }
   9023 
   9024         /**
   9025          * @see RecyclerView#scrollToPosition(int)
   9026          * @deprecated Use {@link Action#jumpTo(int)}.
   9027          */
   9028         @Deprecated
   9029         public void instantScrollToPosition(int position) {
   9030             mRecyclerView.scrollToPosition(position);
   9031         }
   9032 
   9033         protected void onChildAttachedToWindow(View child) {
   9034             if (getChildPosition(child) == getTargetPosition()) {
   9035                 mTargetView = child;
   9036                 if (DEBUG) {
   9037                     Log.d(TAG, "smooth scroll target view has been attached");
   9038                 }
   9039             }
   9040         }
   9041 
   9042         /**
   9043          * Normalizes the vector.
   9044          * @param scrollVector The vector that points to the target scroll position
   9045          */
   9046         protected void normalize(PointF scrollVector) {
   9047             final double magnitute = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y *
   9048                     scrollVector.y);
   9049             scrollVector.x /= magnitute;
   9050             scrollVector.y /= magnitute;
   9051         }
   9052 
   9053         /**
   9054          * Called when smooth scroll is started. This might be a good time to do setup.
   9055          */
   9056         abstract protected void onStart();
   9057 
   9058         /**
   9059          * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
   9060          * @see #stop()
   9061          */
   9062         abstract protected void onStop();
   9063 
   9064         /**
   9065          * <p>RecyclerView will call this method each time it scrolls until it can find the target
   9066          * position in the layout.</p>
   9067          * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the
   9068          * provided {@link Action} to define the next scroll.</p>
   9069          *
   9070          * @param dx        Last scroll amount horizontally
   9071          * @param dy        Last scroll amount verticaully
   9072          * @param state     Transient state of RecyclerView
   9073          * @param action    If you want to trigger a new smooth scroll and cancel the previous one,
   9074          *                  update this object.
   9075          */
   9076         abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);
   9077 
   9078         /**
   9079          * Called when the target position is laid out. This is the last callback SmoothScroller
   9080          * will receive and it should update the provided {@link Action} to define the scroll
   9081          * details towards the target view.
   9082          * @param targetView    The view element which render the target position.
   9083          * @param state         Transient state of RecyclerView
   9084          * @param action        Action instance that you should update to define final scroll action
   9085          *                      towards the targetView
   9086          */
   9087         abstract protected void onTargetFound(View targetView, State state, Action action);
   9088 
   9089         /**
   9090          * Holds information about a smooth scroll request by a {@link SmoothScroller}.
   9091          */
   9092         public static class Action {
   9093 
   9094             public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
   9095 
   9096             private int mDx;
   9097 
   9098             private int mDy;
   9099 
   9100             private int mDuration;
   9101 
   9102             private int mJumpToPosition = NO_POSITION;
   9103 
   9104             private Interpolator mInterpolator;
   9105 
   9106             private boolean changed = false;
   9107 
   9108             // we track this variable to inform custom implementer if they are updating the action
   9109             // in every animation callback
   9110             private int consecutiveUpdates = 0;
   9111 
   9112             /**
   9113              * @param dx Pixels to scroll horizontally
   9114              * @param dy Pixels to scroll vertically
   9115              */
   9116             public Action(int dx, int dy) {
   9117                 this(dx, dy, UNDEFINED_DURATION, null);
   9118             }
   9119 
   9120             /**
   9121              * @param dx       Pixels to scroll horizontally
   9122              * @param dy       Pixels to scroll vertically
   9123              * @param duration Duration of the animation in milliseconds
   9124              */
   9125             public Action(int dx, int dy, int duration) {
   9126                 this(dx, dy, duration, null);
   9127             }
   9128 
   9129             /**
   9130              * @param dx           Pixels to scroll horizontally
   9131              * @param dy           Pixels to scroll vertically
   9132              * @param duration     Duration of the animation in milliseconds
   9133              * @param interpolator Interpolator to be used when calculating scroll position in each
   9134              *                     animation step
   9135              */
   9136             public Action(int dx, int dy, int duration, Interpolator interpolator) {
   9137                 mDx = dx;
   9138                 mDy = dy;
   9139                 mDuration = duration;
   9140                 mInterpolator = interpolator;
   9141             }
   9142 
   9143             /**
   9144              * Instead of specifying pixels to scroll, use the target position to jump using
   9145              * {@link RecyclerView#scrollToPosition(int)}.
   9146              * <p>
   9147              * You may prefer using this method if scroll target is really far away and you prefer
   9148              * to jump to a location and smooth scroll afterwards.
   9149              * <p>
   9150              * Note that calling this method takes priority over other update methods such as
   9151              * {@link #update(int, int, int, Interpolator)}, {@link #setX(float)},
   9152              * {@link #setY(float)} and #{@link #setInterpolator(Interpolator)}. If you call
   9153              * {@link #jumpTo(int)}, the other changes will not be considered for this animation
   9154              * frame.
   9155              *
   9156              * @param targetPosition The target item position to scroll to using instant scrolling.
   9157              */
   9158             public void jumpTo(int targetPosition) {
   9159                 mJumpToPosition = targetPosition;
   9160             }
   9161 
   9162             boolean hasJumpTarget() {
   9163                 return mJumpToPosition >= 0;
   9164             }
   9165 
   9166             private void runIfNecessary(RecyclerView recyclerView) {
   9167                 if (mJumpToPosition >= 0) {
   9168                     final int position = mJumpToPosition;
   9169                     mJumpToPosition = NO_POSITION;
   9170                     recyclerView.jumpToPositionForSmoothScroller(position);
   9171                     changed = false;
   9172                     return;
   9173                 }
   9174                 if (changed) {
   9175                     validate();
   9176                     if (mInterpolator == null) {
   9177                         if (mDuration == UNDEFINED_DURATION) {
   9178                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
   9179                         } else {
   9180                             recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
   9181                         }
   9182                     } else {
   9183                         recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
   9184                     }
   9185                     consecutiveUpdates ++;
   9186                     if (consecutiveUpdates > 10) {
   9187                         // A new action is being set in every animation step. This looks like a bad
   9188                         // implementation. Inform developer.
   9189                         Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
   9190                                 + " you are not changing it unless necessary");
   9191                     }
   9192                     changed = false;
   9193                 } else {
   9194                     consecutiveUpdates = 0;
   9195                 }
   9196             }
   9197 
   9198             private void validate() {
   9199                 if (mInterpolator != null && mDuration < 1) {
   9200                     throw new IllegalStateException("If you provide an interpolator, you must"
   9201                             + " set a positive duration");
   9202                 } else if (mDuration < 1) {
   9203                     throw new IllegalStateException("Scroll duration must be a positive number");
   9204                 }
   9205             }
   9206 
   9207             public int getDx() {
   9208                 return mDx;
   9209             }
   9210 
   9211             public void setDx(int dx) {
   9212                 changed = true;
   9213                 mDx = dx;
   9214             }
   9215 
   9216             public int getDy() {
   9217                 return mDy;
   9218             }
   9219 
   9220             public void setDy(int dy) {
   9221                 changed = true;
   9222                 mDy = dy;
   9223             }
   9224 
   9225             public int getDuration() {
   9226                 return mDuration;
   9227             }
   9228 
   9229             public void setDuration(int duration) {
   9230                 changed = true;
   9231                 mDuration = duration;
   9232             }
   9233 
   9234             public Interpolator getInterpolator() {
   9235                 return mInterpolator;
   9236             }
   9237 
   9238             /**
   9239              * Sets the interpolator to calculate scroll steps
   9240              * @param interpolator The interpolator to use. If you specify an interpolator, you must
   9241              *                     also set the duration.
   9242              * @see #setDuration(int)
   9243              */
   9244             public void setInterpolator(Interpolator interpolator) {
   9245                 changed = true;
   9246                 mInterpolator = interpolator;
   9247             }
   9248 
   9249             /**
   9250              * Updates the action with given parameters.
   9251              * @param dx Pixels to scroll horizontally
   9252              * @param dy Pixels to scroll vertically
   9253              * @param duration Duration of the animation in milliseconds
   9254              * @param interpolator Interpolator to be used when calculating scroll position in each
   9255              *                     animation step
   9256              */
   9257             public void update(int dx, int dy, int duration, Interpolator interpolator) {
   9258                 mDx = dx;
   9259                 mDy = dy;
   9260                 mDuration = duration;
   9261                 mInterpolator = interpolator;
   9262                 changed = true;
   9263             }
   9264         }
   9265     }
   9266 
   9267     static class AdapterDataObservable extends Observable<AdapterDataObserver> {
   9268         public boolean hasObservers() {
   9269             return !mObservers.isEmpty();
   9270         }
   9271 
   9272         public void notifyChanged() {
   9273             // since onChanged() is implemented by the app, it could do anything, including
   9274             // removing itself from {@link mObservers} - and that could cause problems if
   9275             // an iterator is used on the ArrayList {@link mObservers}.
   9276             // to avoid such problems, just march thru the list in the reverse order.
   9277             for (int i = mObservers.size() - 1; i >= 0; i--) {
   9278                 mObservers.get(i).onChanged();
   9279             }
   9280         }
   9281 
   9282         public void notifyItemRangeChanged(int positionStart, int itemCount) {
   9283             notifyItemRangeChanged(positionStart, itemCount, null);
   9284         }
   9285 
   9286         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
   9287             // since onItemRangeChanged() is implemented by the app, it could do anything, including
   9288             // removing itself from {@link mObservers} - and that could cause problems if
   9289             // an iterator is used on the ArrayList {@link mObservers}.
   9290             // to avoid such problems, just march thru the list in the reverse order.
   9291             for (int i = mObservers.size() - 1; i >= 0; i--) {
   9292                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
   9293             }
   9294         }
   9295 
   9296         public void notifyItemRangeInserted(int positionStart, int itemCount) {
   9297             // since onItemRangeInserted() is implemented by the app, it could do anything,
   9298             // including removing itself from {@link mObservers} - and that could cause problems if
   9299             // an iterator is used on the ArrayList {@link mObservers}.
   9300             // to avoid such problems, just march thru the list in the reverse order.
   9301             for (int i = mObservers.size() - 1; i >= 0; i--) {
   9302                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
   9303             }
   9304         }
   9305 
   9306         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
   9307             // since onItemRangeRemoved() is implemented by the app, it could do anything, including
   9308             // removing itself from {@link mObservers} - and that could cause problems if
   9309             // an iterator is used on the ArrayList {@link mObservers}.
   9310             // to avoid such problems, just march thru the list in the reverse order.
   9311             for (int i = mObservers.size() - 1; i >= 0; i--) {
   9312                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
   9313             }
   9314         }
   9315 
   9316         public void notifyItemMoved(int fromPosition, int toPosition) {
   9317             for (int i = mObservers.size() - 1; i >= 0; i--) {
   9318                 mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
   9319             }
   9320         }
   9321     }
   9322 
   9323     static class SavedState extends android.view.View.BaseSavedState {
   9324 
   9325         Parcelable mLayoutState;
   9326 
   9327         /**
   9328          * called by CREATOR
   9329          */
   9330         SavedState(Parcel in) {
   9331             super(in);
   9332             mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
   9333         }
   9334 
   9335         /**
   9336          * Called by onSaveInstanceState
   9337          */
   9338         SavedState(Parcelable superState) {
   9339             super(superState);
   9340         }
   9341 
   9342         @Override
   9343         public void writeToParcel(Parcel dest, int flags) {
   9344             super.writeToParcel(dest, flags);
   9345             dest.writeParcelable(mLayoutState, 0);
   9346         }
   9347 
   9348         private void copyFrom(SavedState other) {
   9349             mLayoutState = other.mLayoutState;
   9350         }
   9351 
   9352         public static final Parcelable.Creator<SavedState> CREATOR
   9353                 = new Parcelable.Creator<SavedState>() {
   9354             @Override
   9355             public SavedState createFromParcel(Parcel in) {
   9356                 return new SavedState(in);
   9357             }
   9358 
   9359             @Override
   9360             public SavedState[] newArray(int size) {
   9361                 return new SavedState[size];
   9362             }
   9363         };
   9364     }
   9365     /**
   9366      * <p>Contains useful information about the current RecyclerView state like target scroll
   9367      * position or view focus. State object can also keep arbitrary data, identified by resource
   9368      * ids.</p>
   9369      * <p>Often times, RecyclerView components will need to pass information between each other.
   9370      * To provide a well defined data bus between components, RecyclerView passes the same State
   9371      * object to component callbacks and these components can use it to exchange data.</p>
   9372      * <p>If you implement custom components, you can use State's put/get/remove methods to pass
   9373      * data between your components without needing to manage their lifecycles.</p>
   9374      */
   9375     public static class State {
   9376 
   9377         private int mTargetPosition = RecyclerView.NO_POSITION;
   9378         ArrayMap<ViewHolder, ItemHolderInfo> mPreLayoutHolderMap =
   9379                 new ArrayMap<ViewHolder, ItemHolderInfo>();
   9380         ArrayMap<ViewHolder, ItemHolderInfo> mPostLayoutHolderMap =
   9381                 new ArrayMap<ViewHolder, ItemHolderInfo>();
   9382         // nullable
   9383         ArrayMap<Long, ViewHolder> mOldChangedHolders = new ArrayMap<Long, ViewHolder>();
   9384 
   9385         // we use this like a set
   9386         final List<View> mDisappearingViewsInLayoutPass = new ArrayList<View>();
   9387 
   9388         private SparseArray<Object> mData;
   9389 
   9390         /**
   9391          * Number of items adapter has.
   9392          */
   9393         int mItemCount = 0;
   9394 
   9395         /**
   9396          * Number of items adapter had in the previous layout.
   9397          */
   9398         private int mPreviousLayoutItemCount = 0;
   9399 
   9400         /**
   9401          * Number of items that were NOT laid out but has been deleted from the adapter after the
   9402          * previous layout.
   9403          */
   9404         private int mDeletedInvisibleItemCountSincePreviousLayout = 0;
   9405 
   9406         private boolean mStructureChanged = false;
   9407 
   9408         private boolean mInPreLayout = false;
   9409 
   9410         private boolean mRunSimpleAnimations = false;
   9411 
   9412         private boolean mRunPredictiveAnimations = false;
   9413 
   9414         State reset() {
   9415             mTargetPosition = RecyclerView.NO_POSITION;
   9416             if (mData != null) {
   9417                 mData.clear();
   9418             }
   9419             mItemCount = 0;
   9420             mStructureChanged = false;
   9421             return this;
   9422         }
   9423 
   9424         public boolean isPreLayout() {
   9425             return mInPreLayout;
   9426         }
   9427 
   9428         /**
   9429          * Returns whether RecyclerView will run predictive animations in this layout pass
   9430          * or not.
   9431          *
   9432          * @return true if RecyclerView is calculating predictive animations to be run at the end
   9433          *         of the layout pass.
   9434          */
   9435         public boolean willRunPredictiveAnimations() {
   9436             return mRunPredictiveAnimations;
   9437         }
   9438 
   9439         /**
   9440          * Returns whether RecyclerView will run simple animations in this layout pass
   9441          * or not.
   9442          *
   9443          * @return true if RecyclerView is calculating simple animations to be run at the end of
   9444          *         the layout pass.
   9445          */
   9446         public boolean willRunSimpleAnimations() {
   9447             return mRunSimpleAnimations;
   9448         }
   9449 
   9450         /**
   9451          * Removes the mapping from the specified id, if there was any.
   9452          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to
   9453          *                   preserve cross functionality and avoid conflicts.
   9454          */
   9455         public void remove(int resourceId) {
   9456             if (mData == null) {
   9457                 return;
   9458             }
   9459             mData.remove(resourceId);
   9460         }
   9461 
   9462         /**
   9463          * Gets the Object mapped from the specified id, or <code>null</code>
   9464          * if no such data exists.
   9465          *
   9466          * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*
   9467          *                   to
   9468          *                   preserve cross functionality and avoid conflicts.
   9469          */
   9470         public <T> T get(int resourceId) {
   9471             if (mData == null) {
   9472                 return null;
   9473             }
   9474             return (T) mData.get(resourceId);
   9475         }
   9476 
   9477         /**
   9478          * Adds a mapping from the specified id to the specified value, replacing the previous
   9479          * mapping from the specified key if there was one.
   9480          *
   9481          * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to
   9482          *                   preserve cross functionality and avoid conflicts.
   9483          * @param data       The data you want to associate with the resourceId.
   9484          */
   9485         public void put(int resourceId, Object data) {
   9486             if (mData == null) {
   9487                 mData = new SparseArray<Object>();
   9488             }
   9489             mData.put(resourceId, data);
   9490         }
   9491 
   9492         /**
   9493          * If scroll is triggered to make a certain item visible, this value will return the
   9494          * adapter index of that item.
   9495          * @return Adapter index of the target item or
   9496          * {@link RecyclerView#NO_POSITION} if there is no target
   9497          * position.
   9498          */
   9499         public int getTargetScrollPosition() {
   9500             return mTargetPosition;
   9501         }
   9502 
   9503         /**
   9504          * Returns if current scroll has a target position.
   9505          * @return true if scroll is being triggered to make a certain position visible
   9506          * @see #getTargetScrollPosition()
   9507          */
   9508         public boolean hasTargetScrollPosition() {
   9509             return mTargetPosition != RecyclerView.NO_POSITION;
   9510         }
   9511 
   9512         /**
   9513          * @return true if the structure of the data set has changed since the last call to
   9514          *         onLayoutChildren, false otherwise
   9515          */
   9516         public boolean didStructureChange() {
   9517             return mStructureChanged;
   9518         }
   9519 
   9520         /**
   9521          * Returns the total number of items that can be laid out. Note that this number is not
   9522          * necessarily equal to the number of items in the adapter, so you should always use this
   9523          * number for your position calculations and never access the adapter directly.
   9524          * <p>
   9525          * RecyclerView listens for Adapter's notify events and calculates the effects of adapter
   9526          * data changes on existing Views. These calculations are used to decide which animations
   9527          * should be run.
   9528          * <p>
   9529          * To support predictive animations, RecyclerView may rewrite or reorder Adapter changes to
   9530          * present the correct state to LayoutManager in pre-layout pass.
   9531          * <p>
   9532          * For example, a newly added item is not included in pre-layout item count because
   9533          * pre-layout reflects the contents of the adapter before the item is added. Behind the
   9534          * scenes, RecyclerView offsets {@link Recycler#getViewForPosition(int)} calls such that
   9535          * LayoutManager does not know about the new item's existence in pre-layout. The item will
   9536          * be available in second layout pass and will be included in the item count. Similar
   9537          * adjustments are made for moved and removed items as well.
   9538          * <p>
   9539          * You can get the adapter's item count via {@link LayoutManager#getItemCount()} method.
   9540          *
   9541          * @return The number of items currently available
   9542          * @see LayoutManager#getItemCount()
   9543          */
   9544         public int getItemCount() {
   9545             return mInPreLayout ?
   9546                     (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) :
   9547                     mItemCount;
   9548         }
   9549 
   9550         void onViewRecycled(ViewHolder holder) {
   9551             mPreLayoutHolderMap.remove(holder);
   9552             mPostLayoutHolderMap.remove(holder);
   9553             if (mOldChangedHolders != null) {
   9554                 removeFrom(mOldChangedHolders, holder);
   9555             }
   9556             mDisappearingViewsInLayoutPass.remove(holder.itemView);
   9557             // holder cannot be in new list.
   9558         }
   9559 
   9560         public void onViewIgnored(ViewHolder holder) {
   9561             onViewRecycled(holder);
   9562         }
   9563 
   9564         private void removeFrom(ArrayMap<Long, ViewHolder> holderMap, ViewHolder holder) {
   9565             for (int i = holderMap.size() - 1; i >= 0; i --) {
   9566                 if (holder == holderMap.valueAt(i)) {
   9567                     holderMap.removeAt(i);
   9568                     return;
   9569                 }
   9570             }
   9571         }
   9572 
   9573         void removeFromDisappearingList(View child) {
   9574             mDisappearingViewsInLayoutPass.remove(child);
   9575         }
   9576 
   9577         void addToDisappearingList(View child) {
   9578             if (!mDisappearingViewsInLayoutPass.contains(child)) {
   9579                 mDisappearingViewsInLayoutPass.add(child);
   9580             }
   9581         }
   9582 
   9583         @Override
   9584         public String toString() {
   9585             return "State{" +
   9586                     "mTargetPosition=" + mTargetPosition +
   9587                     ", mPreLayoutHolderMap=" + mPreLayoutHolderMap +
   9588                     ", mPostLayoutHolderMap=" + mPostLayoutHolderMap +
   9589                     ", mData=" + mData +
   9590                     ", mItemCount=" + mItemCount +
   9591                     ", mPreviousLayoutItemCount=" + mPreviousLayoutItemCount +
   9592                     ", mDeletedInvisibleItemCountSincePreviousLayout="
   9593                     + mDeletedInvisibleItemCountSincePreviousLayout +
   9594                     ", mStructureChanged=" + mStructureChanged +
   9595                     ", mInPreLayout=" + mInPreLayout +
   9596                     ", mRunSimpleAnimations=" + mRunSimpleAnimations +
   9597                     ", mRunPredictiveAnimations=" + mRunPredictiveAnimations +
   9598                     '}';
   9599         }
   9600     }
   9601 
   9602     /**
   9603      * Internal listener that manages items after animations finish. This is how items are
   9604      * retained (not recycled) during animations, but allowed to be recycled afterwards.
   9605      * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()
   9606      * method on the animator's listener when it is done animating any item.
   9607      */
   9608     private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {
   9609 
   9610         @Override
   9611         public void onRemoveFinished(ViewHolder item) {
   9612             item.setIsRecyclable(true);
   9613             if (!removeAnimatingView(item.itemView) && item.isTmpDetached()) {
   9614                 removeDetachedView(item.itemView, false);
   9615             }
   9616         }
   9617 
   9618         @Override
   9619         public void onAddFinished(ViewHolder item) {
   9620             item.setIsRecyclable(true);
   9621             if (!item.shouldBeKeptAsChild()) {
   9622                 removeAnimatingView(item.itemView);
   9623             }
   9624         }
   9625 
   9626         @Override
   9627         public void onMoveFinished(ViewHolder item) {
   9628             item.setIsRecyclable(true);
   9629             if (!item.shouldBeKeptAsChild()) {
   9630                 removeAnimatingView(item.itemView);
   9631             }
   9632         }
   9633 
   9634         @Override
   9635         public void onChangeFinished(ViewHolder item) {
   9636             item.setIsRecyclable(true);
   9637             /**
   9638              * We check both shadowed and shadowing because a ViewHolder may get both roles at the
   9639              * same time.
   9640              *
   9641              * Assume this flow:
   9642              * item X is represented by VH_1. Then itemX changes, so we create VH_2 .
   9643              * RV sets the following and calls item animator:
   9644              * VH_1.shadowed = VH_2;
   9645              * VH_1.mChanged = true;
   9646              * VH_2.shadowing =VH_1;
   9647              *
   9648              * Then, before the first change finishes, item changes again so we create VH_3.
   9649              * RV sets the following and calls item animator:
   9650              * VH_2.shadowed = VH_3
   9651              * VH_2.mChanged = true
   9652              * VH_3.shadowing = VH_2
   9653              *
   9654              * Because VH_2 already has an animation, it will be cancelled. At this point VH_2 has
   9655              * both shadowing and shadowed fields set. Shadowing information is obsolete now
   9656              * because the first animation where VH_2 is newViewHolder is not valid anymore.
   9657              * We ended up in this case because VH_2 played both roles. On the other hand,
   9658              * we DO NOT want to clear its changed flag.
   9659              *
   9660              * If second change was simply reverting first change, we would find VH_1 in
   9661              * {@link Recycler#getScrapViewForPosition(int, int, boolean)} and recycle it before
   9662              * re-using
   9663              */
   9664             if (item.mShadowedHolder != null && item.mShadowingHolder == null) { // old vh
   9665                 item.mShadowedHolder = null;
   9666                 item.setFlags(~ViewHolder.FLAG_CHANGED, item.mFlags);
   9667             }
   9668             // always null this because an OldViewHolder can never become NewViewHolder w/o being
   9669             // recycled.
   9670             item.mShadowingHolder = null;
   9671             if (!item.shouldBeKeptAsChild()) {
   9672                 removeAnimatingView(item.itemView);
   9673             }
   9674         }
   9675     };
   9676 
   9677     /**
   9678      * This class defines the animations that take place on items as changes are made
   9679      * to the adapter.
   9680      *
   9681      * Subclasses of ItemAnimator can be used to implement custom animations for actions on
   9682      * ViewHolder items. The RecyclerView will manage retaining these items while they
   9683      * are being animated, but implementors must call the appropriate "Starting"
   9684      * ({@link #dispatchRemoveStarting(ViewHolder)}, {@link #dispatchMoveStarting(ViewHolder)},
   9685      * {@link #dispatchChangeStarting(ViewHolder, boolean)}, or
   9686      * {@link #dispatchAddStarting(ViewHolder)})
   9687      * and "Finished" ({@link #dispatchRemoveFinished(ViewHolder)},
   9688      * {@link #dispatchMoveFinished(ViewHolder)},
   9689      * {@link #dispatchChangeFinished(ViewHolder, boolean)},
   9690      * or {@link #dispatchAddFinished(ViewHolder)}) methods when each item animation is
   9691      * being started and ended.
   9692      *
   9693      * <p>By default, RecyclerView uses {@link DefaultItemAnimator}</p>
   9694      *
   9695      * @see #setItemAnimator(ItemAnimator)
   9696      */
   9697     public static abstract class ItemAnimator {
   9698 
   9699         private ItemAnimatorListener mListener = null;
   9700         private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =
   9701                 new ArrayList<ItemAnimatorFinishedListener>();
   9702 
   9703         private long mAddDuration = 120;
   9704         private long mRemoveDuration = 120;
   9705         private long mMoveDuration = 250;
   9706         private long mChangeDuration = 250;
   9707 
   9708         private boolean mSupportsChangeAnimations = true;
   9709 
   9710         /**
   9711          * Gets the current duration for which all move animations will run.
   9712          *
   9713          * @return The current move duration
   9714          */
   9715         public long getMoveDuration() {
   9716             return mMoveDuration;
   9717         }
   9718 
   9719         /**
   9720          * Sets the duration for which all move animations will run.
   9721          *
   9722          * @param moveDuration The move duration
   9723          */
   9724         public void setMoveDuration(long moveDuration) {
   9725             mMoveDuration = moveDuration;
   9726         }
   9727 
   9728         /**
   9729          * Gets the current duration for which all add animations will run.
   9730          *
   9731          * @return The current add duration
   9732          */
   9733         public long getAddDuration() {
   9734             return mAddDuration;
   9735         }
   9736 
   9737         /**
   9738          * Sets the duration for which all add animations will run.
   9739          *
   9740          * @param addDuration The add duration
   9741          */
   9742         public void setAddDuration(long addDuration) {
   9743             mAddDuration = addDuration;
   9744         }
   9745 
   9746         /**
   9747          * Gets the current duration for which all remove animations will run.
   9748          *
   9749          * @return The current remove duration
   9750          */
   9751         public long getRemoveDuration() {
   9752             return mRemoveDuration;
   9753         }
   9754 
   9755         /**
   9756          * Sets the duration for which all remove animations will run.
   9757          *
   9758          * @param removeDuration The remove duration
   9759          */
   9760         public void setRemoveDuration(long removeDuration) {
   9761             mRemoveDuration = removeDuration;
   9762         }
   9763 
   9764         /**
   9765          * Gets the current duration for which all change animations will run.
   9766          *
   9767          * @return The current change duration
   9768          */
   9769         public long getChangeDuration() {
   9770             return mChangeDuration;
   9771         }
   9772 
   9773         /**
   9774          * Sets the duration for which all change animations will run.
   9775          *
   9776          * @param changeDuration The change duration
   9777          */
   9778         public void setChangeDuration(long changeDuration) {
   9779             mChangeDuration = changeDuration;
   9780         }
   9781 
   9782         /**
   9783          * Returns whether this ItemAnimator supports animations of change events.
   9784          *
   9785          * @return true if change animations are supported, false otherwise
   9786          */
   9787         public boolean getSupportsChangeAnimations() {
   9788             return mSupportsChangeAnimations;
   9789         }
   9790 
   9791         /**
   9792          * Sets whether this ItemAnimator supports animations of item change events.
   9793          * If you set this property to false, actions on the data set which change the
   9794          * contents of items will not be animated. What those animations are is left
   9795          * up to the discretion of the ItemAnimator subclass, in its
   9796          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} implementation.
   9797          * The value of this property is true by default.
   9798          *
   9799          * @see Adapter#notifyItemChanged(int)
   9800          * @see Adapter#notifyItemRangeChanged(int, int)
   9801          *
   9802          * @param supportsChangeAnimations true if change animations are supported by
   9803          * this ItemAnimator, false otherwise. If the property is false, the ItemAnimator
   9804          * will not receive a call to
   9805          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} when changes occur.
   9806          */
   9807         public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
   9808             mSupportsChangeAnimations = supportsChangeAnimations;
   9809         }
   9810 
   9811         /**
   9812          * Internal only:
   9813          * Sets the listener that must be called when the animator is finished
   9814          * animating the item (or immediately if no animation happens). This is set
   9815          * internally and is not intended to be set by external code.
   9816          *
   9817          * @param listener The listener that must be called.
   9818          */
   9819         void setListener(ItemAnimatorListener listener) {
   9820             mListener = listener;
   9821         }
   9822 
   9823         /**
   9824          * Called when there are pending animations waiting to be started. This state
   9825          * is governed by the return values from {@link #animateAdd(ViewHolder) animateAdd()},
   9826          * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and
   9827          * {@link #animateRemove(ViewHolder) animateRemove()}, which inform the
   9828          * RecyclerView that the ItemAnimator wants to be called later to start the
   9829          * associated animations. runPendingAnimations() will be scheduled to be run
   9830          * on the next frame.
   9831          */
   9832         abstract public void runPendingAnimations();
   9833 
   9834         /**
   9835          * Called when an item is removed from the RecyclerView. Implementors can choose
   9836          * whether and how to animate that change, but must always call
   9837          * {@link #dispatchRemoveFinished(ViewHolder)} when done, either
   9838          * immediately (if no animation will occur) or after the animation actually finishes.
   9839          * The return value indicates whether an animation has been set up and whether the
   9840          * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
   9841          * next opportunity. This mechanism allows ItemAnimator to set up individual animations
   9842          * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
   9843          * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
   9844          * {@link #animateRemove(ViewHolder) animateRemove()}, and
   9845          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
   9846          * then start the animations together in the later call to {@link #runPendingAnimations()}.
   9847          *
   9848          * <p>This method may also be called for disappearing items which continue to exist in the
   9849          * RecyclerView, but for which the system does not have enough information to animate
   9850          * them out of view. In that case, the default animation for removing items is run
   9851          * on those items as well.</p>
   9852          *
   9853          * @param holder The item that is being removed.
   9854          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   9855          * false otherwise.
   9856          */
   9857         abstract public boolean animateRemove(ViewHolder holder);
   9858 
   9859         /**
   9860          * Called when an item is added to the RecyclerView. Implementors can choose
   9861          * whether and how to animate that change, but must always call
   9862          * {@link #dispatchAddFinished(ViewHolder)} when done, either
   9863          * immediately (if no animation will occur) or after the animation actually finishes.
   9864          * The return value indicates whether an animation has been set up and whether the
   9865          * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
   9866          * next opportunity. This mechanism allows ItemAnimator to set up individual animations
   9867          * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
   9868          * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
   9869          * {@link #animateRemove(ViewHolder) animateRemove()}, and
   9870          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
   9871          * then start the animations together in the later call to {@link #runPendingAnimations()}.
   9872          *
   9873          * <p>This method may also be called for appearing items which were already in the
   9874          * RecyclerView, but for which the system does not have enough information to animate
   9875          * them into view. In that case, the default animation for adding items is run
   9876          * on those items as well.</p>
   9877          *
   9878          * @param holder The item that is being added.
   9879          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   9880          * false otherwise.
   9881          */
   9882         abstract public boolean animateAdd(ViewHolder holder);
   9883 
   9884         /**
   9885          * Called when an item is moved in the RecyclerView. Implementors can choose
   9886          * whether and how to animate that change, but must always call
   9887          * {@link #dispatchMoveFinished(ViewHolder)} when done, either
   9888          * immediately (if no animation will occur) or after the animation actually finishes.
   9889          * The return value indicates whether an animation has been set up and whether the
   9890          * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
   9891          * next opportunity. This mechanism allows ItemAnimator to set up individual animations
   9892          * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
   9893          * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
   9894          * {@link #animateRemove(ViewHolder) animateRemove()}, and
   9895          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
   9896          * then start the animations together in the later call to {@link #runPendingAnimations()}.
   9897          *
   9898          * @param holder The item that is being moved.
   9899          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   9900          * false otherwise.
   9901          */
   9902         abstract public boolean animateMove(ViewHolder holder, int fromX, int fromY,
   9903                 int toX, int toY);
   9904 
   9905         /**
   9906          * Called when an item is changed in the RecyclerView, as indicated by a call to
   9907          * {@link Adapter#notifyItemChanged(int)} or
   9908          * {@link Adapter#notifyItemRangeChanged(int, int)}.
   9909          * <p>
   9910          * Implementers can choose whether and how to animate changes, but must always call
   9911          * {@link #dispatchChangeFinished(ViewHolder, boolean)} for each non-null ViewHolder,
   9912          * either immediately (if no animation will occur) or after the animation actually finishes.
   9913          * The return value indicates whether an animation has been set up and whether the
   9914          * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
   9915          * next opportunity. This mechanism allows ItemAnimator to set up individual animations
   9916          * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},
   9917          * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()},
   9918          * {@link #animateRemove(ViewHolder) animateRemove()}, and
   9919          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)} come in one by one,
   9920          * then start the animations together in the later call to {@link #runPendingAnimations()}.
   9921          *
   9922          * @param oldHolder The original item that changed.
   9923          * @param newHolder The new item that was created with the changed content. Might be null
   9924          * @param fromLeft  Left of the old view holder
   9925          * @param fromTop   Top of the old view holder
   9926          * @param toLeft    Left of the new view holder
   9927          * @param toTop     Top of the new view holder
   9928          * @return true if a later call to {@link #runPendingAnimations()} is requested,
   9929          * false otherwise.
   9930          */
   9931         abstract public boolean animateChange(ViewHolder oldHolder,
   9932                 ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop);
   9933 
   9934 
   9935         /**
   9936          * Method to be called by subclasses when a remove animation is done.
   9937          *
   9938          * @param item The item which has been removed
   9939          */
   9940         public final void dispatchRemoveFinished(ViewHolder item) {
   9941             onRemoveFinished(item);
   9942             if (mListener != null) {
   9943                 mListener.onRemoveFinished(item);
   9944             }
   9945         }
   9946 
   9947         /**
   9948          * Method to be called by subclasses when a move animation is done.
   9949          *
   9950          * @param item The item which has been moved
   9951          */
   9952         public final void dispatchMoveFinished(ViewHolder item) {
   9953             onMoveFinished(item);
   9954             if (mListener != null) {
   9955                 mListener.onMoveFinished(item);
   9956             }
   9957         }
   9958 
   9959         /**
   9960          * Method to be called by subclasses when an add animation is done.
   9961          *
   9962          * @param item The item which has been added
   9963          */
   9964         public final void dispatchAddFinished(ViewHolder item) {
   9965             onAddFinished(item);
   9966             if (mListener != null) {
   9967                 mListener.onAddFinished(item);
   9968             }
   9969         }
   9970 
   9971         /**
   9972          * Method to be called by subclasses when a change animation is done.
   9973          *
   9974          * @see #animateChange(ViewHolder, ViewHolder, int, int, int, int)
   9975          * @param item The item which has been changed (this method must be called for
   9976          * each non-null ViewHolder passed into
   9977          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
   9978          * @param oldItem true if this is the old item that was changed, false if
   9979          * it is the new item that replaced the old item.
   9980          */
   9981         public final void dispatchChangeFinished(ViewHolder item, boolean oldItem) {
   9982             onChangeFinished(item, oldItem);
   9983             if (mListener != null) {
   9984                 mListener.onChangeFinished(item);
   9985             }
   9986         }
   9987 
   9988         /**
   9989          * Method to be called by subclasses when a remove animation is being started.
   9990          *
   9991          * @param item The item being removed
   9992          */
   9993         public final void dispatchRemoveStarting(ViewHolder item) {
   9994             onRemoveStarting(item);
   9995         }
   9996 
   9997         /**
   9998          * Method to be called by subclasses when a move animation is being started.
   9999          *
   10000          * @param item The item being moved
   10001          */
   10002         public final void dispatchMoveStarting(ViewHolder item) {
   10003             onMoveStarting(item);
   10004         }
   10005 
   10006         /**
   10007          * Method to be called by subclasses when an add animation is being started.
   10008          *
   10009          * @param item The item being added
   10010          */
   10011         public final void dispatchAddStarting(ViewHolder item) {
   10012             onAddStarting(item);
   10013         }
   10014 
   10015         /**
   10016          * Method to be called by subclasses when a change animation is being started.
   10017          *
   10018          * @param item The item which has been changed (this method must be called for
   10019          * each non-null ViewHolder passed into
   10020          * {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}).
   10021          * @param oldItem true if this is the old item that was changed, false if
   10022          * it is the new item that replaced the old item.
   10023          */
   10024         public final void dispatchChangeStarting(ViewHolder item, boolean oldItem) {
   10025             onChangeStarting(item, oldItem);
   10026         }
   10027 
   10028         /**
   10029          * Method called when an animation on a view should be ended immediately.
   10030          * This could happen when other events, like scrolling, occur, so that
   10031          * animating views can be quickly put into their proper end locations.
   10032          * Implementations should ensure that any animations running on the item
   10033          * are canceled and affected properties are set to their end values.
   10034          * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)}
   10035          * should be called since the animations are effectively done when this
   10036          * method is called.
   10037          *
   10038          * @param item The item for which an animation should be stopped.
   10039          */
   10040         abstract public void endAnimation(ViewHolder item);
   10041 
   10042         /**
   10043          * Method called when all item animations should be ended immediately.
   10044          * This could happen when other events, like scrolling, occur, so that
   10045          * animating views can be quickly put into their proper end locations.
   10046          * Implementations should ensure that any animations running on any items
   10047          * are canceled and affected properties are set to their end values.
   10048          * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)}
   10049          * should be called since the animations are effectively done when this
   10050          * method is called.
   10051          */
   10052         abstract public void endAnimations();
   10053 
   10054         /**
   10055          * Method which returns whether there are any item animations currently running.
   10056          * This method can be used to determine whether to delay other actions until
   10057          * animations end.
   10058          *
   10059          * @return true if there are any item animations currently running, false otherwise.
   10060          */
   10061         abstract public boolean isRunning();
   10062 
   10063         /**
   10064          * Like {@link #isRunning()}, this method returns whether there are any item
   10065          * animations currently running. Addtionally, the listener passed in will be called
   10066          * when there are no item animations running, either immediately (before the method
   10067          * returns) if no animations are currently running, or when the currently running
   10068          * animations are {@link #dispatchAnimationsFinished() finished}.
   10069          *
   10070          * <p>Note that the listener is transient - it is either called immediately and not
   10071          * stored at all, or stored only until it is called when running animations
   10072          * are finished sometime later.</p>
   10073          *
   10074          * @param listener A listener to be called immediately if no animations are running
   10075          * or later when currently-running animations have finished. A null listener is
   10076          * equivalent to calling {@link #isRunning()}.
   10077          * @return true if there are any item animations currently running, false otherwise.
   10078          */
   10079         public final boolean isRunning(ItemAnimatorFinishedListener listener) {
   10080             boolean running = isRunning();
   10081             if (listener != null) {
   10082                 if (!running) {
   10083                     listener.onAnimationsFinished();
   10084                 } else {
   10085                     mFinishedListeners.add(listener);
   10086                 }
   10087             }
   10088             return running;
   10089         }
   10090 
   10091         /**
   10092          * The interface to be implemented by listeners to animation events from this
   10093          * ItemAnimator. This is used internally and is not intended for developers to
   10094          * create directly.
   10095          */
   10096         interface ItemAnimatorListener {
   10097             void onRemoveFinished(ViewHolder item);
   10098             void onAddFinished(ViewHolder item);
   10099             void onMoveFinished(ViewHolder item);
   10100             void onChangeFinished(ViewHolder item);
   10101         }
   10102 
   10103         /**
   10104          * This method should be called by ItemAnimator implementations to notify
   10105          * any listeners that all pending and active item animations are finished.
   10106          */
   10107         public final void dispatchAnimationsFinished() {
   10108             final int count = mFinishedListeners.size();
   10109             for (int i = 0; i < count; ++i) {
   10110                 mFinishedListeners.get(i).onAnimationsFinished();
   10111             }
   10112             mFinishedListeners.clear();
   10113         }
   10114 
   10115         /**
   10116          * This interface is used to inform listeners when all pending or running animations
   10117          * in an ItemAnimator are finished. This can be used, for example, to delay an action
   10118          * in a data set until currently-running animations are complete.
   10119          *
   10120          * @see #isRunning(ItemAnimatorFinishedListener)
   10121          */
   10122         public interface ItemAnimatorFinishedListener {
   10123             void onAnimationsFinished();
   10124         }
   10125 
   10126         /**
   10127          * Called when a remove animation is being started on the given ViewHolder.
   10128          * The default implementation does nothing. Subclasses may wish to override
   10129          * this method to handle any ViewHolder-specific operations linked to animation
   10130          * lifecycles.
   10131          *
   10132          * @param item The ViewHolder being animated.
   10133          */
   10134         public void onRemoveStarting(ViewHolder item) {}
   10135 
   10136         /**
   10137          * Called when a remove animation has ended on the given ViewHolder.
   10138          * The default implementation does nothing. Subclasses may wish to override
   10139          * this method to handle any ViewHolder-specific operations linked to animation
   10140          * lifecycles.
   10141          *
   10142          * @param item The ViewHolder being animated.
   10143          */
   10144         public void onRemoveFinished(ViewHolder item) {}
   10145 
   10146         /**
   10147          * Called when an add animation is being started on the given ViewHolder.
   10148          * The default implementation does nothing. Subclasses may wish to override
   10149          * this method to handle any ViewHolder-specific operations linked to animation
   10150          * lifecycles.
   10151          *
   10152          * @param item The ViewHolder being animated.
   10153          */
   10154         public void onAddStarting(ViewHolder item) {}
   10155 
   10156         /**
   10157          * Called when an add animation has ended on the given ViewHolder.
   10158          * The default implementation does nothing. Subclasses may wish to override
   10159          * this method to handle any ViewHolder-specific operations linked to animation
   10160          * lifecycles.
   10161          *
   10162          * @param item The ViewHolder being animated.
   10163          */
   10164         public void onAddFinished(ViewHolder item) {}
   10165 
   10166         /**
   10167          * Called when a move animation is being started on the given ViewHolder.
   10168          * The default implementation does nothing. Subclasses may wish to override
   10169          * this method to handle any ViewHolder-specific operations linked to animation
   10170          * lifecycles.
   10171          *
   10172          * @param item The ViewHolder being animated.
   10173          */
   10174         public void onMoveStarting(ViewHolder item) {}
   10175 
   10176         /**
   10177          * Called when a move animation has ended on the given ViewHolder.
   10178          * The default implementation does nothing. Subclasses may wish to override
   10179          * this method to handle any ViewHolder-specific operations linked to animation
   10180          * lifecycles.
   10181          *
   10182          * @param item The ViewHolder being animated.
   10183          */
   10184         public void onMoveFinished(ViewHolder item) {}
   10185 
   10186         /**
   10187          * Called when a change animation is being started on the given ViewHolder.
   10188          * The default implementation does nothing. Subclasses may wish to override
   10189          * this method to handle any ViewHolder-specific operations linked to animation
   10190          * lifecycles.
   10191          *
   10192          * @param item The ViewHolder being animated.
   10193          * @param oldItem true if this is the old item that was changed, false if
   10194          * it is the new item that replaced the old item.
   10195          */
   10196         public void onChangeStarting(ViewHolder item, boolean oldItem) {}
   10197 
   10198         /**
   10199          * Called when a change animation has ended on the given ViewHolder.
   10200          * The default implementation does nothing. Subclasses may wish to override
   10201          * this method to handle any ViewHolder-specific operations linked to animation
   10202          * lifecycles.
   10203          *
   10204          * @param item The ViewHolder being animated.
   10205          * @param oldItem true if this is the old item that was changed, false if
   10206          * it is the new item that replaced the old item.
   10207          */
   10208         public void onChangeFinished(ViewHolder item, boolean oldItem) {}
   10209 
   10210     }
   10211 
   10212     /**
   10213      * Internal data structure that holds information about an item's bounds.
   10214      * This information is used in calculating item animations.
   10215      */
   10216     private static class ItemHolderInfo {
   10217         ViewHolder holder;
   10218         int left, top, right, bottom;
   10219 
   10220         ItemHolderInfo(ViewHolder holder, int left, int top, int right, int bottom) {
   10221             this.holder = holder;
   10222             this.left = left;
   10223             this.top = top;
   10224             this.right = right;
   10225             this.bottom = bottom;
   10226         }
   10227     }
   10228 
   10229     @Override
   10230     protected int getChildDrawingOrder(int childCount, int i) {
   10231         if (mChildDrawingOrderCallback == null) {
   10232             return super.getChildDrawingOrder(childCount, i);
   10233         } else {
   10234             return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
   10235         }
   10236     }
   10237 
   10238     /**
   10239      * A callback interface that can be used to alter the drawing order of RecyclerView children.
   10240      * <p>
   10241      * It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
   10242      * that applies to that method also applies to this callback. For example, changing the drawing
   10243      * order of two views will not have any effect if their elevation values are different since
   10244      * elevation overrides the result of this callback.
   10245      */
   10246     public static interface ChildDrawingOrderCallback {
   10247         /**
   10248          * Returns the index of the child to draw for this iteration. Override this
   10249          * if you want to change the drawing order of children. By default, it
   10250          * returns i.
   10251          *
   10252          * @param i The current iteration.
   10253          * @return The index of the child to draw this iteration.
   10254          *
   10255          * @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
   10256          */
   10257         public int onGetChildDrawingOrder(int childCount, int i);
   10258     }
   10259 }
   10260