解决RecyclerView无法onItemClick问题

来源:互联网 发布:goovis下载什么软件 编辑:程序博客网 时间:2024/06/06 01:46

对于RecyclerView的使用,大家可以查看将替代ListView的RecyclerView 的使用(一),单单从代码结构来说RecyclerView确实比ListView优化了很多,也简化了我们编写代码量,但是有一个问题会导致开发者不会去用它,更比说替换ListView了,我不知道使用过RecyclerView的人有没有进一步查看,RecyclerView没有提供Item的点击事件,我们使用列表不仅仅为了显示数据,同时也可以能会交互,所以RecyclerView这个问题导致基本没有人用它,我清楚谷歌是怎么想的,不过RecyclerView也并没有把所有的路给堵死,需要我们写代码来实现Item的点击事件,我们都知道RecyclerView里面新加了ViewHolder这个静态抽象类,这个类里面有一个方法getPosition()可以返回当前ViewHolder实例的位置,实现onItemClick就是使用它来做的,下面有两种方法来实现:

第一种:不修改源码

这种方法不修改源码,问题是只能在RecyclerView.Adapter中实现ItemClick事件

public static class ViewHolder extends RecyclerView.ViewHolder {public ViewHolder(View itemView) {super(itemView);itemView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.e("jwzhangjie", "当前点击的位置:"+getPosition());}});}}

这种方式直观上看起来不太好,不过也可以实现ItemClick事件。

第二种方法:修改RecyclerView源码

1、把在RecyClerView类里面定义OnItemClickListener接口

/**     * Interface definition for a callback to be invoked when an item in this     * RecyclerView.Adapter has been clicked.     */    public interface OnItemClickListener {        /**         * Callback method to be invoked when an item in this RecyclerView.Adapter has         * been clicked.         * <p>         * Implementers can call getPosition(position) if they need         * to access the data associated with the selected item.         *         * @param view The view within the RecyclerView.Adapter that was clicked (this         *            will be a view provided by the adapter)         * @param position The position of the view in the adapter.         */        void onItemClick(View view, int position);    }        public static OnItemClickListener mOnItemClickListener = null;        /**     * Register a callback to be invoked when an item in this AdapterView has     * been clicked.     *     * @param listener The callback that will be invoked.     */    public void setOnItemClickListener(OnItemClickListener listener) {        mOnItemClickListener = listener;    }    /**     * @return The callback to be invoked with an item in this AdapterView has     *         been clicked, or null id no callback has been set.     */    public final OnItemClickListener getOnItemClickListener() {        return mOnItemClickListener;    }

2、在RecyclerView中的抽象类ViewHolder中添加View的点击事件

public static abstract class ViewHolder implements OnClickListener{        public final View itemView;        int mPosition = NO_POSITION;        int mOldPosition = NO_POSITION;        long mItemId = NO_ID;        int mItemViewType = INVALID_TYPE;        /**         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType         * are all valid.         */        static final int FLAG_BOUND = 1 << 0;        /**         * The data this ViewHolder's view reflects is stale and needs to be rebound         * by the adapter. mPosition and mItemId are consistent.         */        static final int FLAG_UPDATE = 1 << 1;        /**         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId         * are not to be trusted and may no longer match the item view type.         * This ViewHolder must be fully rebound to different data.         */        static final int FLAG_INVALID = 1 << 2;        /**         * This ViewHolder points at data that represents an item previously removed from the         * data set. Its view may still be used for things like outgoing animations.         */        static final int FLAG_REMOVED = 1 << 3;        /**         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()         * and is intended to keep views around during animations.         */        static final int FLAG_NOT_RECYCLABLE = 1 << 4;        private int mFlags;        private int mIsRecyclableCount = 0;        // If non-null, view is currently considered scrap and may be reused for other data by the        // scrap container.        private Recycler mScrapContainer = null;                @Overridepublic void onClick(View v) {        if (mOnItemClickListener != null) {        mOnItemClickListener.onItemClick(itemView, getPosition());}}public ViewHolder(View itemView) {            if (itemView == null) {                throw new IllegalArgumentException("itemView may not be null");            }            this.itemView = itemView;            this.itemView.setOnClickListener(this);        }        void offsetPosition(int offset) {            if (mOldPosition == NO_POSITION) {                mOldPosition = mPosition;            }            mPosition += offset;        }        void clearOldPosition() {            mOldPosition = NO_POSITION;        }        public final int getPosition() {            return mOldPosition == NO_POSITION ? mPosition : mOldPosition;        }        public final long getItemId() {            return mItemId;        }        public final int getItemViewType() {            return mItemViewType;        }        boolean isScrap() {            return mScrapContainer != null;        }        void unScrap() {            mScrapContainer.unscrapView(this);            mScrapContainer = null;        }        void setScrapContainer(Recycler recycler) {            mScrapContainer = recycler;        }        boolean isInvalid() {            return (mFlags & FLAG_INVALID) != 0;        }        boolean needsUpdate() {            return (mFlags & FLAG_UPDATE) != 0;        }        boolean isBound() {            return (mFlags & FLAG_BOUND) != 0;        }        boolean isRemoved() {            return (mFlags & FLAG_REMOVED) != 0;        }        void setFlags(int flags, int mask) {            mFlags = (mFlags & ~mask) | (flags & mask);        }        void addFlags(int flags) {            mFlags |= flags;        }        void clearFlagsForSharedPool() {            mFlags = 0;        }        @Override        public String toString() {            final StringBuilder sb = new StringBuilder("ViewHolder{" +                    Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId);            if (isScrap()) sb.append(" scrap");            if (isInvalid()) sb.append(" invalid");            if (!isBound()) sb.append(" unbound");            if (needsUpdate()) sb.append(" update");            if (isRemoved()) sb.append(" removed");            sb.append("}");            return sb.toString();        }

3、完成上面的步骤,就可以使用RecyclerView来完成itemClick事件了

cashAccountList.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(View view, int position) {AppLog.e("position: "+position);}});

下面是完整的RecyclerView源码:

/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package android.support.v7.widget;import android.content.Context;import android.database.Observable;import android.graphics.Canvas;import android.graphics.PointF;import android.graphics.Rect;import android.os.Build;import android.os.Parcel;import android.os.Parcelable;import android.support.annotation.Nullable;import android.support.v4.util.ArrayMap;import android.support.v4.util.Pools;import android.support.v4.view.MotionEventCompat;import android.support.v4.view.VelocityTrackerCompat;import android.support.v4.view.ViewCompat;import android.support.v4.widget.EdgeEffectCompat;import android.support.v4.widget.ScrollerCompat;import android.util.AttributeSet;import android.util.Log;import android.util.SparseArray;import android.util.SparseIntArray;import android.view.FocusFinder;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.ViewParent;import android.view.animation.Interpolator;import java.util.ArrayList;import java.util.Collections;import java.util.List;/** * A flexible view for providing a limited window into a large data set. * * <h3>Glossary of terms:</h3> * * <ul> *     <li><em>Adapter:</em> A subclass of {@link Adapter} responsible for providing views *     that represent items in a data set.</li> *     <li><em>Position:</em> The position of a data item within an <em>Adapter</em>.</li> *     <li><em>Index:</em> The index of an attached child view as used in a call to *     {@link ViewGroup#getChildAt}. Contrast with <em>Position.</em></li> *     <li><em>Binding:</em> The process of preparing a child view to display data corresponding *     to a <em>position</em> within the adapter.</li> *     <li><em>Recycle (view):</em> A view previously used to display data for a specific adapter *     position may be placed in a cache for later reuse to display the same type of data again *     later. This can drastically improve performance by skipping initial layout inflation *     or construction.</li> *     <li><em>Scrap (view):</em> A child view that has entered into a temporarily detached *     state during layout. Scrap views may be reused without becoming fully detached *     from the parent RecyclerView, either unmodified if no rebinding is required or modified *     by the adapter if the view was considered <em>dirty</em>.</li> *     <li><em>Dirty (view):</em> A child view that must be rebound by the adapter before *     being displayed.</li> * </ul> */public class RecyclerView extends ViewGroup {    private static final String TAG = "RecyclerView";    private static final boolean DEBUG = false;    private static final boolean ENABLE_PREDICTIVE_ANIMATIONS = false;    private static final boolean DISPATCH_TEMP_DETACH = false;    public static final int HORIZONTAL = 0;    public static final int VERTICAL = 1;    public static final int NO_POSITION = -1;    public static final long NO_ID = -1;    public static final int INVALID_TYPE = -1;    private static final int MAX_SCROLL_DURATION = 2000;    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();    private final Recycler mRecycler = new Recycler();    private SavedState mPendingSavedState;    /**     * Note: this Runnable is only ever posted if:     * 1) We've been through first layout     * 2) We know we have a fixed size (mHasFixedSize)     * 3) We're attached     */    private final Runnable mUpdateChildViewsRunnable = new Runnable() {        public void run() {            if (mPendingUpdates.isEmpty()) {                return;            }            eatRequestLayout();            updateChildViews();            resumeRequestLayout(true);        }    };    private final Rect mTempRect = new Rect();    private final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();    private final ArrayList<UpdateOp> mPendingLayoutUpdates = new ArrayList<UpdateOp>();    private Pools.Pool<UpdateOp> mUpdateOpPool = new Pools.SimplePool<UpdateOp>(UpdateOp.POOL_SIZE);    private Adapter mAdapter;    private LayoutManager mLayout;    private RecyclerListener mRecyclerListener;    private final ArrayList<ItemDecoration> mItemDecorations = new ArrayList<ItemDecoration>();    private final ArrayList<OnItemTouchListener> mOnItemTouchListeners =            new ArrayList<OnItemTouchListener>();    private OnItemTouchListener mActiveOnItemTouchListener;    private boolean mIsAttached;    private boolean mHasFixedSize;    private boolean mFirstLayoutComplete;    private boolean mEatRequestLayout;    private boolean mLayoutRequestEaten;    private boolean mAdapterUpdateDuringMeasure;    private final boolean mPostUpdatesOnAnimation;    private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;    ItemAnimator mItemAnimator = new DefaultItemAnimator();    private static final int INVALID_POINTER = -1;    /**     * The RecyclerView is not currently scrolling.     * @see #getScrollState()     */    public static final int SCROLL_STATE_IDLE = 0;    /**     * The RecyclerView is currently being dragged by outside input such as user touch input.     * @see #getScrollState()     */    public static final int SCROLL_STATE_DRAGGING = 1;    /**     * The RecyclerView is currently animating to a final position while not under     * outside control.     * @see #getScrollState()     */    public static final int SCROLL_STATE_SETTLING = 2;    // Touch/scrolling handling    private int mScrollState = SCROLL_STATE_IDLE;    private int mScrollPointerId = INVALID_POINTER;    private VelocityTracker mVelocityTracker;    private int mInitialTouchX;    private int mInitialTouchY;    private int mLastTouchX;    private int mLastTouchY;    private final int mTouchSlop;    private final int mMinFlingVelocity;    private final int mMaxFlingVelocity;    private final ViewFlinger mViewFlinger = new ViewFlinger();    private final State mState = new State();    private OnScrollListener mScrollListener;    // For use in item animations    boolean mItemsAddedOrRemoved = false;    boolean mItemsChanged = false;    int mAnimatingViewIndex = -1;    int mNumAnimatingViews = 0;    boolean mInPreLayout = false;    private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =            new ItemAnimatorRestoreListener();    private boolean mPostedAnimatorRunner = false;    private Runnable mItemAnimatorRunner = new Runnable() {        @Override        public void run() {            if (mItemAnimator != null) {                mItemAnimator.runPendingAnimations();            }            mPostedAnimatorRunner = false;        }    };    private static final Interpolator sQuinticInterpolator = new Interpolator() {        public float getInterpolation(float t) {            t -= 1.0f;            return t * t * t * t * t + 1.0f;        }    };    public RecyclerView(Context context) {        this(context, null);    }    public RecyclerView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public RecyclerView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        final int version = Build.VERSION.SDK_INT;        mPostUpdatesOnAnimation = version >= 16;        final ViewConfiguration vc = ViewConfiguration.get(context);        mTouchSlop = vc.getScaledTouchSlop();        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();        setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);        mItemAnimator.setListener(mItemAnimatorListener);    }    /**     * RecyclerView can perform several optimizations if it can know in advance that changes in     * adapter content cannot change the size of the RecyclerView itself.     * If your use of RecyclerView falls into this category, set this to true.     *     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.     */    public void setHasFixedSize(boolean hasFixedSize) {        mHasFixedSize = hasFixedSize;    }    /**     * @return true if the app has specified that changes in adapter content cannot change     * the size of the RecyclerView itself.     */    public boolean hasFixedSize() {        return mHasFixedSize;    }    /**     * Set a new adapter to provide child views on demand.     *     * @param adapter The new adapter to set, or null to set no adapter.     */    public void setAdapter(Adapter adapter) {        if (mAdapter != null) {            mAdapter.unregisterAdapterDataObserver(mObserver);        }        // end all running animations        if (mItemAnimator != null) {            mItemAnimator.endAnimations();        }        // Since animations are ended, mLayout.children should be equal to recyclerView.children.        // This may not be true if item animator's end does not work as expected. (e.g. not release        // children instantly). It is safer to use mLayout's child count.        if (mLayout != null) {            mLayout.removeAndRecycleAllViews(mRecycler);            mLayout.removeAndRecycleScrapInt(mRecycler, true);        }        final Adapter oldAdapter = mAdapter;        mAdapter = adapter;        if (adapter != null) {            adapter.registerAdapterDataObserver(mObserver);        }        if (mLayout != null) {            mLayout.onAdapterChanged(oldAdapter, mAdapter);        }        mRecycler.onAdapterChanged(oldAdapter, mAdapter);        mState.mStructureChanged = true;        markKnownViewsInvalid();        requestLayout();    }    /**     * Retrieves the previously set adapter or null if no adapter is set.     *     * @return The previously set adapter     * @see #setAdapter(Adapter)     */    public Adapter getAdapter() {        return mAdapter;    }    /**     * Register a listener that will be notified whenever a child view is recycled.     *     * <p>This listener will be called when a LayoutManager or the RecyclerView decides     * that a child view is no longer needed. If an application associates expensive     * or heavyweight data with item views, this may be a good place to release     * or free those resources.</p>     *     * @param listener Listener to register, or null to clear     */    public void setRecyclerListener(RecyclerListener listener) {        mRecyclerListener = listener;    }    /**     * Set the {@link LayoutManager} that this RecyclerView will use.     *     * <p>In contrast to other adapter-backed views such as {@link android.widget.ListView}     * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom     * layout arrangements for child views. These arrangements are controlled by the     * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.</p>     *     * <p>Several default strategies are provided for common uses such as lists and grids.</p>     *     * @param layout LayoutManager to use     */    public void setLayoutManager(LayoutManager layout) {        if (layout == mLayout) {            return;        }        mRecycler.clear();        removeAllViews();        if (mLayout != null) {            if (mIsAttached) {                mLayout.onDetachedFromWindow(this);            }            mLayout.mRecyclerView = null;        }        mLayout = layout;        if (layout != null) {            if (layout.mRecyclerView != null) {                throw new IllegalArgumentException("LayoutManager " + layout +                        " is already attached to a RecyclerView: " + layout.mRecyclerView);            }            layout.mRecyclerView = this;            if (mIsAttached) {                mLayout.onAttachedToWindow(this);            }        }        requestLayout();    }    @Override    protected Parcelable onSaveInstanceState() {        SavedState state = new SavedState(super.onSaveInstanceState());        if (mPendingSavedState != null) {            state.copyFrom(mPendingSavedState);        } else if (mLayout != null) {            state.mLayoutState = mLayout.onSaveInstanceState();        } else {            state.mLayoutState = null;        }        return state;    }    @Override    protected void onRestoreInstanceState(Parcelable state) {        mPendingSavedState = (SavedState) state;        super.onRestoreInstanceState(mPendingSavedState.getSuperState());        if (mLayout != null && mPendingSavedState.mLayoutState != null) {            mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);        }    }    /**     * Adds a view to the animatingViews list.     * mAnimatingViews holds the child views that are currently being kept around     * purely for the purpose of being animated out of view. They are drawn as a regular     * part of the child list of the RecyclerView, but they are invisible to the LayoutManager     * as they are managed separately from the regular child views.     * @param view The view to be removed     */    private void addAnimatingView(View view) {        boolean alreadyAdded = false;        if (mNumAnimatingViews > 0) {            for (int i = mAnimatingViewIndex; i < getChildCount(); ++i) {                if (getChildAt(i) == view) {                    alreadyAdded = true;                    break;                }            }        }        if (!alreadyAdded) {            if (mNumAnimatingViews == 0) {                mAnimatingViewIndex = getChildCount();            }            ++mNumAnimatingViews;            addView(view);        }        mRecycler.unscrapView(getChildViewHolder(view));    }    /**     * Removes a view from the animatingViews list.     * @param view The view to be removed     * @see #addAnimatingView(View)     */    private void removeAnimatingView(View view) {        if (mNumAnimatingViews > 0) {            for (int i = mAnimatingViewIndex; i < getChildCount(); ++i) {                if (getChildAt(i) == view) {                    removeViewAt(i);                    --mNumAnimatingViews;                    if (mNumAnimatingViews == 0) {                        mAnimatingViewIndex = -1;                    }                    mRecycler.recycleView(view);                    return;                }            }        }    }    private View getAnimatingView(int position, int type) {        if (mNumAnimatingViews > 0) {            for (int i = mAnimatingViewIndex; i < getChildCount(); ++i) {                final View view = getChildAt(i);                ViewHolder holder = getChildViewHolder(view);                if (holder.getPosition() == position &&                        ( type == INVALID_TYPE || holder.getItemViewType() == type)) {                    return view;                }            }        }        return null;    }    /**     * Return the {@link LayoutManager} currently responsible for     * layout policy for this RecyclerView.     *     * @return The currently bound LayoutManager     */    public LayoutManager getLayoutManager() {        return mLayout;    }    /**     * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;     * if no pool is set for this view a new one will be created. See     * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.     *     * @return The pool used to store recycled item views for reuse.     * @see #setRecycledViewPool(RecycledViewPool)     */    public RecycledViewPool getRecycledViewPool() {        return mRecycler.getRecycledViewPool();    }    /**     * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.     * This can be useful if you have multiple RecyclerViews with adapters that use the same     * view types, for example if you have several data sets with the same kinds of item views     * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.     *     * @param pool Pool to set. If this parameter is null a new pool will be created and used.     */    public void setRecycledViewPool(RecycledViewPool pool) {        mRecycler.setRecycledViewPool(pool);    }    /**     * Set the number of offscreen views to retain before adding them to the potentially shared     * {@link #getRecycledViewPool() recycled view pool}.     *     * <p>The offscreen view cache stays aware of changes in the attached adapter, allowing     * a LayoutManager to reuse those views unmodified without needing to return to the adapter     * to rebind them.</p>     *     * @param size Number of views to cache offscreen before returning them to the general     *             recycled view pool     */    public void setItemViewCacheSize(int size) {        mRecycler.setViewCacheSize(size);    }    /**     * Return the current scrolling state of the RecyclerView.     *     * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or     * {@link #SCROLL_STATE_SETTLING}     */    public int getScrollState() {        return mScrollState;    }    private void setScrollState(int state) {        if (state == mScrollState) {            return;        }        mScrollState = state;        if (state != SCROLL_STATE_SETTLING) {            stopScroll();        }        if (mScrollListener != null) {            mScrollListener.onScrollStateChanged(state);        }    }    /**     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can     * affect both measurement and drawing of individual item views.     *     * <p>Item decorations are ordered. Decorations placed earlier in the list will     * be run/queried/drawn first for their effects on item views. Padding added to views     * will be nested; a padding added by an earlier decoration will mean further     * item decorations in the list will be asked to draw/pad within the previous decoration's     * given area.</p>     *     * @param decor Decoration to add     * @param index Position in the decoration chain to insert this decoration at. If this value     *              is negative the decoration will be added at the end.     */    public void addItemDecoration(ItemDecoration decor, int index) {        if (mItemDecorations.isEmpty()) {            setWillNotDraw(false);        }        if (index < 0) {            mItemDecorations.add(decor);        } else {            mItemDecorations.add(index, decor);        }        markItemDecorInsetsDirty();        requestLayout();    }    /**     * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can     * affect both measurement and drawing of individual item views.     *     * <p>Item decorations are ordered. Decorations placed earlier in the list will     * be run/queried/drawn first for their effects on item views. Padding added to views     * will be nested; a padding added by an earlier decoration will mean further     * item decorations in the list will be asked to draw/pad within the previous decoration's     * given area.</p>     *     * @param decor Decoration to add     */    public void addItemDecoration(ItemDecoration decor) {        addItemDecoration(decor, -1);    }    /**     * Remove an {@link ItemDecoration} from this RecyclerView.     *     * <p>The given decoration will no longer impact the measurement and drawing of     * item views.</p>     *     * @param decor Decoration to remove     * @see #addItemDecoration(ItemDecoration)     */    public void removeItemDecoration(ItemDecoration decor) {        mItemDecorations.remove(decor);        if (mItemDecorations.isEmpty()) {            setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);        }        markItemDecorInsetsDirty();        requestLayout();    }    /**     * Set a listener that will be notified of any changes in scroll state or position.     *     * @param listener Listener to set or null to clear     */    public void setOnScrollListener(OnScrollListener listener) {        mScrollListener = listener;    }    /**     * Convenience method to scroll to a certain position.     *     * RecyclerView does not implement scrolling logic, rather forwards the call to     * {@link android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)}     * @param position Scroll to this adapter position     * @see android.support.v7.widget.RecyclerView.LayoutManager#scrollToPosition(int)     */    public void scrollToPosition(int position) {        stopScroll();        mLayout.scrollToPosition(position);        awakenScrollBars();    }    /**     * Starts a smooth scroll to an adapter position.     * <p>     * To support smooth scrolling, you must override     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a     * {@link SmoothScroller}.     * <p>     * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to     * provide a custom smooth scroll logic, override     * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your     * LayoutManager.     *     * @param position The adapter position to scroll to     * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)     */    public void smoothScrollToPosition(int position) {        mLayout.smoothScrollToPosition(this, mState, position);    }    @Override    public void scrollTo(int x, int y) {        throw new UnsupportedOperationException(                "RecyclerView does not support scrolling to an absolute position.");    }    @Override    public void scrollBy(int x, int y) {        if (mLayout == null) {            throw new IllegalStateException("Cannot scroll without a LayoutManager set. " +                    "Call setLayoutManager with a non-null argument.");        }        final boolean canScrollHorizontal = mLayout.canScrollHorizontally();        final boolean canScrollVertical = mLayout.canScrollVertically();        if (canScrollHorizontal || canScrollVertical) {            scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0);        }    }    /**     * Helper method reflect data changes to the state.     * <p>     * Adapter changes during a scroll may trigger a crash because scroll assumes no data change     * but data actually changed.     * <p>     * This method consumes all deferred changes to avoid that case.     * <p>     * This also ends all pending animations. It will be changed once we can support     * animations during scroll.     */    private void consumePendingUpdateOperations() {        if (mItemAnimator != null) {            mItemAnimator.endAnimations();        }        if (mPendingUpdates.size() > 0) {            mUpdateChildViewsRunnable.run();        }    }    /**     * Does not perform bounds checking. Used by internal methods that have already validated input.     */    void scrollByInternal(int x, int y) {        int overscrollX = 0, overscrollY = 0;        consumePendingUpdateOperations();        if (mAdapter != null) {            eatRequestLayout();            if (x != 0) {                final int hresult = mLayout.scrollHorizontallyBy(x, mRecycler, mState);                overscrollX = x - hresult;            }            if (y != 0) {                final int vresult = mLayout.scrollVerticallyBy(y, mRecycler, mState);                overscrollY = y - vresult;            }            resumeRequestLayout(false);        }        if (!mItemDecorations.isEmpty()) {            invalidate();        }        if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {            pullGlows(overscrollX, overscrollY);        }        if (mScrollListener != null && (x != 0 || y != 0)) {            mScrollListener.onScrolled(x, y);        }        if (!awakenScrollBars()) {            invalidate();        }    }    /**     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal     * range. This value is used to compute the length of the thumb within the scrollbar's track.     * </p>     *     * <p>The range is expressed in arbitrary units that must be the same as the units used by     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.</p>     *     * <p>Default implementation returns 0.</p>     *     * <p>If you want to support scroll bars, override     * {@link RecyclerView.LayoutManager#computeHorizontalScrollOffset(RecyclerView.State)} in your     * LayoutManager. </p>     *     * @return The horizontal offset of the scrollbar's thumb     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeHorizontalScrollOffset     * (RecyclerView.Adapter)     */    @Override    protected int computeHorizontalScrollOffset() {        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState)                : 0;    }    /**     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb within the     * horizontal range. This value is used to compute the length of the thumb within the     * scrollbar's track.</p>     *     * <p>The range is expressed in arbitrary units that must be the same as the units used by     * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.</p>     *     * <p>Default implementation returns 0.</p>     *     * <p>If you want to support scroll bars, override     * {@link RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)} in your     * LayoutManager.</p>     *     * @return The horizontal extent of the scrollbar's thumb     * @see RecyclerView.LayoutManager#computeHorizontalScrollExtent(RecyclerView.State)     */    @Override    protected int computeHorizontalScrollExtent() {        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;    }    /**     * <p>Compute the horizontal range that the horizontal scrollbar represents.</p>     *     * <p>The range is expressed in arbitrary units that must be the same as the units used by     * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.</p>     *     * <p>Default implementation returns 0.</p>     *     * <p>If you want to support scroll bars, override     * {@link RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)} in your     * LayoutManager.</p>     *     * @return The total horizontal range represented by the vertical scrollbar     * @see RecyclerView.LayoutManager#computeHorizontalScrollRange(RecyclerView.State)     */    @Override    protected int computeHorizontalScrollRange() {        return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;    }    /**     * <p>Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.     * This value is used to compute the length of the thumb within the scrollbar's track. </p>     *     * <p>The range is expressed in arbitrary units that must be the same as the units used by     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.</p>     *     * <p>Default implementation returns 0.</p>     *     * <p>If you want to support scroll bars, override     * {@link RecyclerView.LayoutManager#computeVerticalScrollOffset(RecyclerView.State)} in your     * LayoutManager.</p>     *     * @return The vertical offset of the scrollbar's thumb     * @see android.support.v7.widget.RecyclerView.LayoutManager#computeVerticalScrollOffset     * (RecyclerView.Adapter)     */    @Override    protected int computeVerticalScrollOffset() {        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;    }    /**     * <p>Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.     * This value is used to compute the length of the thumb within the scrollbar's track.</p>     *     * <p>The range is expressed in arbitrary units that must be the same as the units used by     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.</p>     *     * <p>Default implementation returns 0.</p>     *     * <p>If you want to support scroll bars, override     * {@link RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)} in your     * LayoutManager.</p>     *     * @return The vertical extent of the scrollbar's thumb     * @see RecyclerView.LayoutManager#computeVerticalScrollExtent(RecyclerView.State)     */    @Override    protected int computeVerticalScrollExtent() {        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;    }    /**     * <p>Compute the vertical range that the vertical scrollbar represents.</p>     *     * <p>The range is expressed in arbitrary units that must be the same as the units used by     * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.</p>     *     * <p>Default implementation returns 0.</p>     *     * <p>If you want to support scroll bars, override     * {@link RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)} in your     * LayoutManager.</p>     *     * @return The total vertical range represented by the vertical scrollbar     * @see RecyclerView.LayoutManager#computeVerticalScrollRange(RecyclerView.State)     */    @Override    protected int computeVerticalScrollRange() {        return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;    }    void eatRequestLayout() {        if (!mEatRequestLayout) {            mEatRequestLayout = true;            mLayoutRequestEaten = false;        }    }    void resumeRequestLayout(boolean performLayoutChildren) {        if (mEatRequestLayout) {            if (performLayoutChildren && mLayoutRequestEaten &&                    mLayout != null && mAdapter != null) {                dispatchLayout();            }            mEatRequestLayout = false;            mLayoutRequestEaten = false;        }    }    /**     * Animate a scroll by the given amount of pixels along either axis.     *     * @param dx Pixels to scroll horizontally     * @param dy Pixels to scroll vertically     */    public void smoothScrollBy(int dx, int dy) {        if (dx != 0 || dy != 0) {            mViewFlinger.smoothScrollBy(dx, dy);        }    }    /**     * Begin a standard fling with an initial velocity along each axis in pixels per second.     * If the velocity given is below the system-defined minimum this method will return false     * and no fling will occur.     *     * @param velocityX Initial horizontal velocity in pixels per second     * @param velocityY Initial vertical velocity in pixels per second     * @return true if the fling was started, false if the velocity was too low to fling     */    public boolean fling(int velocityX, int velocityY) {        if (Math.abs(velocityX) < mMinFlingVelocity) {            velocityX = 0;        }        if (Math.abs(velocityY) < mMinFlingVelocity) {            velocityY = 0;        }        velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));        velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));        if (velocityX != 0 || velocityY != 0) {            mViewFlinger.fling(velocityX, velocityY);            return true;        }        return false;    }    /**     * Stop any current scroll in progress, such as one started by     * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.     */    public void stopScroll() {        mViewFlinger.stop();        mLayout.stopSmoothScroller();    }    /**     * Apply a pull to relevant overscroll glow effects     */    private void pullGlows(int overscrollX, int overscrollY) {        if (overscrollX < 0) {            if (mLeftGlow == null) {                mLeftGlow = new EdgeEffectCompat(getContext());                mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),                        getMeasuredWidth() - getPaddingLeft() - getPaddingRight());            }            mLeftGlow.onPull(-overscrollX / (float) getWidth());        } else if (overscrollX > 0) {            if (mRightGlow == null) {                mRightGlow = new EdgeEffectCompat(getContext());                mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),                        getMeasuredWidth() - getPaddingLeft() - getPaddingRight());            }            mRightGlow.onPull(overscrollX / (float) getWidth());        }        if (overscrollY < 0) {            if (mTopGlow == null) {                mTopGlow = new EdgeEffectCompat(getContext());                mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),                        getMeasuredHeight() - getPaddingTop() - getPaddingBottom());            }            mTopGlow.onPull(-overscrollY / (float) getHeight());        } else if (overscrollY > 0) {            if (mBottomGlow == null) {                mBottomGlow = new EdgeEffectCompat(getContext());                mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),                        getMeasuredHeight() - getPaddingTop() - getPaddingBottom());            }            mBottomGlow.onPull(overscrollY / (float) getHeight());        }        if (overscrollX != 0 || overscrollY != 0) {            ViewCompat.postInvalidateOnAnimation(this);        }    }    private void releaseGlows() {        boolean needsInvalidate = false;        if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();        if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();        if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();        if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();        if (needsInvalidate) {            ViewCompat.postInvalidateOnAnimation(this);        }    }    void absorbGlows(int velocityX, int velocityY) {        if (velocityX < 0) {            if (mLeftGlow == null) {                mLeftGlow = new EdgeEffectCompat(getContext());                mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),                        getMeasuredWidth() - getPaddingLeft() - getPaddingRight());            }            mLeftGlow.onAbsorb(-velocityX);        } else if (velocityX > 0) {            if (mRightGlow == null) {                mRightGlow = new EdgeEffectCompat(getContext());                mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),                        getMeasuredWidth() - getPaddingLeft() - getPaddingRight());            }            mRightGlow.onAbsorb(velocityX);        }        if (velocityY < 0) {            if (mTopGlow == null) {                mTopGlow = new EdgeEffectCompat(getContext());                mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),                        getMeasuredHeight() - getPaddingTop() - getPaddingBottom());            }            mTopGlow.onAbsorb(-velocityY);        } else if (velocityY > 0) {            if (mBottomGlow == null) {                mBottomGlow = new EdgeEffectCompat(getContext());                mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),                        getMeasuredHeight() - getPaddingTop() - getPaddingBottom());            }            mBottomGlow.onAbsorb(velocityY);        }        if (velocityX != 0 || velocityY != 0) {            ViewCompat.postInvalidateOnAnimation(this);        }    }    // Focus handling    @Override    public View focusSearch(View focused, int direction) {        View result = mLayout.onInterceptFocusSearch(focused, direction);        if (result != null) {            return result;        }        final FocusFinder ff = FocusFinder.getInstance();        result = ff.findNextFocus(this, focused, direction);        if (result == null && mAdapter != null) {            eatRequestLayout();            result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);            resumeRequestLayout(false);        }        return result != null ? result : super.focusSearch(focused, direction);    }    @Override    public void requestChildFocus(View child, View focused) {        if (!mLayout.onRequestChildFocus(this, child, focused)) {            mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());            offsetDescendantRectToMyCoords(focused, mTempRect);            offsetRectIntoDescendantCoords(child, mTempRect);            requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);        }        super.requestChildFocus(child, focused);    }    @Override    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {        return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);    }    @Override    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {        if (!mLayout.onAddFocusables(this, views, direction, focusableMode)) {            super.addFocusables(views, direction, focusableMode);        }    }    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        mIsAttached = true;        mFirstLayoutComplete = false;        if (mLayout != null) {            mLayout.onAttachedToWindow(this);        }        mPostedAnimatorRunner = false;    }    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        mFirstLayoutComplete = false;        stopScroll();        // TODO Mark what our target position was if relevant, then we can jump there        // on reattach.        mIsAttached = false;        if (mLayout != null) {            mLayout.onDetachedFromWindow(this);        }        removeCallbacks(mItemAnimatorRunner);    }    /**     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched     * to child views or this view's standard scrolling behavior.     *     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener     * returns true from     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called     * for each incoming MotionEvent until the end of the gesture.</p>     *     * @param listener Listener to add     */    public void addOnItemTouchListener(OnItemTouchListener listener) {        mOnItemTouchListeners.add(listener);    }    /**     * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.     *     * @param listener Listener to remove     */    public void removeOnItemTouchListener(OnItemTouchListener listener) {        mOnItemTouchListeners.remove(listener);        if (mActiveOnItemTouchListener == listener) {            mActiveOnItemTouchListener = null;        }    }    private boolean dispatchOnItemTouchIntercept(MotionEvent e) {        final int action = e.getAction();        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {            mActiveOnItemTouchListener = null;        }        final int listenerCount = mOnItemTouchListeners.size();        for (int i = 0; i < listenerCount; i++) {            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {                mActiveOnItemTouchListener = listener;                return true;            }        }        return false;    }    private boolean dispatchOnItemTouch(MotionEvent e) {        final int action = e.getAction();        if (mActiveOnItemTouchListener != null) {            if (action == MotionEvent.ACTION_DOWN) {                // Stale state from a previous gesture, we're starting a new one. Clear it.                mActiveOnItemTouchListener = null;            } else {                mActiveOnItemTouchListener.onTouchEvent(this, e);                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {                    // Clean up for the next gesture.                    mActiveOnItemTouchListener = null;                }                return true;            }        }        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept        // as called from onInterceptTouchEvent; skip it.        if (action != MotionEvent.ACTION_DOWN) {            final int listenerCount = mOnItemTouchListeners.size();            for (int i = 0; i < listenerCount; i++) {                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);                if (listener.onInterceptTouchEvent(this, e)) {                    mActiveOnItemTouchListener = listener;                    return true;                }            }        }        return false;    }    @Override    public boolean onInterceptTouchEvent(MotionEvent e) {        if (dispatchOnItemTouchIntercept(e)) {            cancelTouch();            return true;        }        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();        final boolean canScrollVertically = mLayout.canScrollVertically();        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(e);        final int action = MotionEventCompat.getActionMasked(e);        final int actionIndex = MotionEventCompat.getActionIndex(e);        switch (action) {            case MotionEvent.ACTION_DOWN:                mScrollPointerId = MotionEventCompat.getPointerId(e, 0);                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);                if (mScrollState == SCROLL_STATE_SETTLING) {                    getParent().requestDisallowInterceptTouchEvent(true);                    setScrollState(SCROLL_STATE_DRAGGING);                }                break;            case MotionEventCompat.ACTION_POINTER_DOWN:                mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);                mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);                mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);                break;            case MotionEvent.ACTION_MOVE: {                final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);                if (index < 0) {                    Log.e(TAG, "Error processing scroll; pointer index for id " +                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");                    return false;                }                final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);                final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);                if (mScrollState != SCROLL_STATE_DRAGGING) {                    final int dx = x - mInitialTouchX;                    final int dy = y - mInitialTouchY;                    boolean startScroll = false;                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);                        startScroll = true;                    }                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);                        startScroll = true;                    }                    if (startScroll) {                        getParent().requestDisallowInterceptTouchEvent(true);                        setScrollState(SCROLL_STATE_DRAGGING);                    }                }            } break;            case MotionEventCompat.ACTION_POINTER_UP: {                onPointerUp(e);            } break;            case MotionEvent.ACTION_UP: {                mVelocityTracker.clear();            } break;            case MotionEvent.ACTION_CANCEL: {                cancelTouch();            }        }        return mScrollState == SCROLL_STATE_DRAGGING;    }    @Override    public boolean onTouchEvent(MotionEvent e) {        if (dispatchOnItemTouch(e)) {            cancelTouch();            return true;        }        final boolean canScrollHorizontally = mLayout.canScrollHorizontally();        final boolean canScrollVertically = mLayout.canScrollVertically();        if (mVelocityTracker == null) {            mVelocityTracker = VelocityTracker.obtain();        }        mVelocityTracker.addMovement(e);        final int action = MotionEventCompat.getActionMasked(e);        final int actionIndex = MotionEventCompat.getActionIndex(e);        switch (action) {            case MotionEvent.ACTION_DOWN: {                mScrollPointerId = MotionEventCompat.getPointerId(e, 0);                mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);                mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);            } break;            case MotionEventCompat.ACTION_POINTER_DOWN: {                mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);                mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);                mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);            } break;            case MotionEvent.ACTION_MOVE: {                final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);                if (index < 0) {                    Log.e(TAG, "Error processing scroll; pointer index for id " +                            mScrollPointerId + " not found. Did any MotionEvents get skipped?");                    return false;                }                final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);                final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);                if (mScrollState != SCROLL_STATE_DRAGGING) {                    final int dx = x - mInitialTouchX;                    final int dy = y - mInitialTouchY;                    boolean startScroll = false;                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {                        mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);                        startScroll = true;                    }                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {                        mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);                        startScroll = true;                    }                    if (startScroll) {                        getParent().requestDisallowInterceptTouchEvent(true);                        setScrollState(SCROLL_STATE_DRAGGING);                    }                }                if (mScrollState == SCROLL_STATE_DRAGGING) {                    final int dx = x - mLastTouchX;                    final int dy = y - mLastTouchY;                    scrollByInternal(canScrollHorizontally ? -dx : 0,                            canScrollVertically ? -dy : 0);                }                mLastTouchX = x;                mLastTouchY = y;            } break;            case MotionEventCompat.ACTION_POINTER_UP: {                onPointerUp(e);            } break;            case MotionEvent.ACTION_UP: {                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);                final float xvel = canScrollHorizontally ?                        -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;                final float yvel = canScrollVertically ?                        -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {                    setScrollState(SCROLL_STATE_IDLE);                }                mVelocityTracker.clear();                releaseGlows();            } break;            case MotionEvent.ACTION_CANCEL: {                cancelTouch();            } break;        }        return true;    }    private void cancelTouch() {        mVelocityTracker.clear();        releaseGlows();        setScrollState(SCROLL_STATE_IDLE);    }    private void onPointerUp(MotionEvent e) {        final int actionIndex = MotionEventCompat.getActionIndex(e);        if (MotionEventCompat.getPointerId(e, actionIndex) == mScrollPointerId) {            // Pick a new pointer to pick up the slack.            final int newIndex = actionIndex == 0 ? 1 : 0;            mScrollPointerId = MotionEventCompat.getPointerId(e, newIndex);            mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, newIndex) + 0.5f);            mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, newIndex) + 0.5f);        }    }    @Override    protected void onMeasure(int widthSpec, int heightSpec) {        if (mAdapterUpdateDuringMeasure) {            eatRequestLayout();            updateChildViews();            mAdapterUpdateDuringMeasure = false;            resumeRequestLayout(false);        }        if (mAdapter != null) {            mState.mItemCount = mAdapter.getItemCount();        }        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);        final int widthSize = getMeasuredWidth();        final int heightSize = getMeasuredHeight();        if (mLeftGlow != null) mLeftGlow.setSize(heightSize, widthSize);        if (mTopGlow != null) mTopGlow.setSize(widthSize, heightSize);        if (mRightGlow != null) mRightGlow.setSize(heightSize, widthSize);        if (mBottomGlow != null) mBottomGlow.setSize(widthSize, heightSize);    }    /**     * Sets the {@link ItemAnimator} that will handle animations involving changes     * to the items in this RecyclerView. By default, RecyclerView instantiates and     * uses an instance of {@link DefaultItemAnimator}. Whether item animations are     * enabled for the RecyclerView depends on the ItemAnimator and whether     * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()     * supports item animations}.     *     * @param animator The ItemAnimator being set. If null, no animations will occur     * when changes occur to the items in this RecyclerView.     */    public void setItemAnimator(ItemAnimator animator) {        if (mItemAnimator != null) {            mItemAnimator.setListener(null);        }        mItemAnimator = animator;        if (mItemAnimator != null) {            mItemAnimator.setListener(mItemAnimatorListener);        }    }    /**     * Gets the current ItemAnimator for this RecyclerView. A null return value     * indicates that there is no animator and that item changes will happen without     * any animations. By default, RecyclerView instantiates and     * uses an instance of {@link DefaultItemAnimator}.     *     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur     * when changes occur to the items in this RecyclerView.     */    public ItemAnimator getItemAnimator() {        return mItemAnimator;    }    /**     * Post a runnable to the next frame to run pending item animations. Only the first such     * request will be posted, governed by the mPostedAnimatorRunner flag.     */    private void postAnimationRunner() {        if (!mPostedAnimatorRunner && mIsAttached) {            ViewCompat.postOnAnimation(this, mItemAnimatorRunner);            mPostedAnimatorRunner = true;        }    }    private boolean predictiveItemAnimationsEnabled() {        return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());    }    /**     * Wrapper around layoutChildren() that handles animating changes caused by layout.     * Animations work on the assumption that there are five different kinds of items     * in play:     * PERSISTENT: items are visible before and after layout     * REMOVED: items were visible before layout and were removed by the app     * ADDED: items did not exist before layout and were added by the app     * DISAPPEARING: items exist in the data set before/after, but changed from     * visible to non-visible in the process of layout (they were moved off     * screen as a side-effect of other changes)     * APPEARING: items exist in the data set before/after, but changed from     * non-visible to visible in the process of layout (they were moved on     * screen as a side-effect of other changes)     * The overall approach figures out what items exist before/after layout and     * infers one of the five above states for each of the items. Then the animations     * are set up accordingly:     * PERSISTENT views are moved ({@link ItemAnimator#animateMove(ViewHolder, int, int, int, int)})     * REMOVED views are removed ({@link ItemAnimator#animateRemove(ViewHolder)})     * ADDED views are added ({@link ItemAnimator#animateAdd(ViewHolder)})     * DISAPPEARING views are moved off screen     * APPEARING views are moved on screen     */    void dispatchLayout() {        if (mAdapter == null) {            Log.e(TAG, "No adapter attached; skipping layout");            return;        }        eatRequestLayout();        // simple animations are a subset of advanced animations (which will cause a        // prelayout step)        boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved                && !mItemsChanged;        final boolean animateChangesAdvanced = ENABLE_PREDICTIVE_ANIMATIONS &&                animateChangesSimple && predictiveItemAnimationsEnabled();        mItemsAddedOrRemoved = mItemsChanged = false;        ArrayMap<View, Rect> appearingViewInitialBounds = null;        mState.mInPreLayout = animateChangesAdvanced;        mState.mItemCount = mAdapter.getItemCount();        if (animateChangesSimple) {            // Step 0: Find out where all non-removed items are, pre-layout            mState.mPreLayoutHolderMap.clear();            mState.mPostLayoutHolderMap.clear();            int count = getChildCount();            for (int i = 0; i < count; ++i) {                final ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder,                        view.getLeft(), view.getTop(), view.getRight(), view.getBottom(),                        holder.mPosition));            }        }        if (animateChangesAdvanced) {            // Step 1: run prelayout: This will use the old positions of items. The layout manager            // is expected to layout everything, even removed items (though not to add removed            // items back to the container). This gives the pre-layout position of APPEARING views            // which come into existence as part of the real layout.            mInPreLayout = true;            final boolean didStructureChange = mState.mStructureChanged;            mState.mStructureChanged = false;            // temporarily disable flag because we are asking for previous layout            mLayout.onLayoutChildren(mRecycler, mState);            mState.mStructureChanged = didStructureChange;            mInPreLayout = false;            appearingViewInitialBounds = new ArrayMap<View, Rect>();            for (int i = 0; i < getChildCount(); ++i) {                boolean found = false;                View child = getChildAt(i);                for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {                    ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);                    if (holder.itemView == child) {                        found = true;                        continue;                    }                }                if (!found) {                    appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(),                            child.getRight(), child.getBottom()));                }            }        }        clearOldPositions();        dispatchLayoutUpdates();        mState.mItemCount = mAdapter.getItemCount();        // Step 2: Run layout        mState.mInPreLayout = false;        mLayout.onLayoutChildren(mRecycler, mState);        mState.mStructureChanged = false;        mPendingSavedState = null;        // onLayoutChildren may have caused client code to disable item animations; re-check        animateChangesSimple = animateChangesSimple && mItemAnimator != null;        if (animateChangesSimple) {            // Step 3: Find out where things are now, post-layout            int count = getChildCount();            for (int i = 0; i < count; ++i) {                ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder,                        view.getLeft(), view.getTop(), view.getRight(), view.getBottom(),                        holder.mPosition));            }            // Step 4: Animate DISAPPEARING and REMOVED items            int preLayoutCount = mState.mPreLayoutHolderMap.size();            for (int i = preLayoutCount - 1; i >= 0; i--) {                ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);                if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {                    ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i);                    mState.mPreLayoutHolderMap.removeAt(i);                    View disappearingItemView = disappearingItem.holder.itemView;                    removeDetachedView(disappearingItemView, false);                    mRecycler.unscrapView(disappearingItem.holder);                    animateDisappearance(disappearingItem);                }            }            // Step 5: Animate APPEARING and ADDED items            int postLayoutCount = mState.mPostLayoutHolderMap.size();            if (postLayoutCount > 0) {                for (int i = postLayoutCount - 1; i >= 0; i--) {                    ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i);                    ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i);                    if ((mState.mPreLayoutHolderMap.isEmpty() ||                            !mState.mPreLayoutHolderMap.containsKey(itemHolder))) {                        mState.mPostLayoutHolderMap.removeAt(i);                        Rect initialBounds = (appearingViewInitialBounds != null) ?                                appearingViewInitialBounds.get(itemHolder.itemView) : null;                        animateAppearance(itemHolder, initialBounds,                                info.left, info.top);                    }                }            }            // Step 6: Animate PERSISTENT items            count = mState.mPostLayoutHolderMap.size();            for (int i = 0; i < count; ++i) {                ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);                ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i);                ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder);                if (preInfo != null && postInfo != null) {                    if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {                        postHolder.setIsRecyclable(false);                        if (DEBUG) {                            Log.d(TAG, "PERSISTENT: " + postHolder +                                    " with view " + postHolder.itemView);                        }                        if (mItemAnimator.animateMove(postHolder,                                preInfo.left, preInfo.top, postInfo.left, postInfo.top)) {                            postAnimationRunner();                        }                    }                }            }        }        resumeRequestLayout(false);        mLayout.removeAndRecycleScrapInt(mRecycler, !animateChangesAdvanced);        mState.mPreviousLayoutItemCount = mState.mItemCount;        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;    }    private void animateAppearance(ViewHolder itemHolder, Rect beforeBounds, int afterLeft,            int afterTop) {        View newItemView = itemHolder.itemView;        if (beforeBounds != null &&                (beforeBounds.left != afterLeft || beforeBounds.top != afterTop)) {            // slide items in if before/after locations differ            itemHolder.setIsRecyclable(false);            if (DEBUG) {                Log.d(TAG, "APPEARING: " + itemHolder + " with view " + newItemView);            }            if (mItemAnimator.animateMove(itemHolder,                    beforeBounds.left, beforeBounds.top,                    afterLeft, afterTop)) {                postAnimationRunner();            }        } else {            if (DEBUG) {                Log.d(TAG, "ADDED: " + itemHolder + " with view " + newItemView);            }            itemHolder.setIsRecyclable(false);            if (mItemAnimator.animateAdd(itemHolder)) {                postAnimationRunner();            }        }    }    private void animateDisappearance(ItemHolderInfo disappearingItem) {        View disappearingItemView = disappearingItem.holder.itemView;        addAnimatingView(disappearingItemView);        int oldLeft = disappearingItem.left;        int oldTop = disappearingItem.top;        int newLeft = disappearingItemView.getLeft();        int newTop = disappearingItemView.getTop();        if (oldLeft != newLeft || oldTop != newTop) {            disappearingItem.holder.setIsRecyclable(false);            disappearingItemView.layout(newLeft, newTop,                    newLeft + disappearingItemView.getWidth(),                    newTop + disappearingItemView.getHeight());            if (DEBUG) {                Log.d(TAG, "DISAPPEARING: " + disappearingItem.holder +                        " with view " + disappearingItemView);            }            if (mItemAnimator.animateMove(disappearingItem.holder, oldLeft, oldTop,                    newLeft, newTop)) {                postAnimationRunner();            }        } else {            if (DEBUG) {                Log.d(TAG, "REMOVED: " + disappearingItem.holder +                        " with view " + disappearingItemView);            }            disappearingItem.holder.setIsRecyclable(false);            if (mItemAnimator.animateRemove(disappearingItem.holder)) {                postAnimationRunner();            }        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        eatRequestLayout();        dispatchLayout();        resumeRequestLayout(false);        mFirstLayoutComplete = true;    }    @Override    public void requestLayout() {        if (!mEatRequestLayout) {            super.requestLayout();        } else {            mLayoutRequestEaten = true;        }    }    void markItemDecorInsetsDirty() {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = getChildAt(i);            ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;        }    }    @Override    public void draw(Canvas c) {        super.draw(c);        final int count = mItemDecorations.size();        for (int i = 0; i < count; i++) {            mItemDecorations.get(i).onDrawOver(c, this);        }        boolean needsInvalidate = false;        if (mLeftGlow != null && !mLeftGlow.isFinished()) {            final int restore = c.save();            c.rotate(270);            c.translate(-getHeight() + getPaddingTop(), 0);            needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);            c.restoreToCount(restore);        }        if (mTopGlow != null && !mTopGlow.isFinished()) {            c.translate(getPaddingLeft(), getPaddingTop());            needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);        }        if (mRightGlow != null && !mRightGlow.isFinished()) {            final int restore = c.save();            final int width = getWidth();            c.rotate(90);            c.translate(-getPaddingTop(), -width);            needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);            c.restoreToCount(restore);        }        if (mBottomGlow != null && !mBottomGlow.isFinished()) {            final int restore = c.save();            c.rotate(180);            c.translate(-getWidth() + getPaddingLeft(), -getHeight() + getPaddingTop());            needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);            c.restoreToCount(restore);        }        if (needsInvalidate) {            ViewCompat.postInvalidateOnAnimation(this);        }    }    @Override    public void onDraw(Canvas c) {        super.onDraw(c);        final int count = mItemDecorations.size();        for (int i = 0; i < count; i++) {            mItemDecorations.get(i).onDraw(c, this);        }    }    @Override    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {        return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);    }    @Override    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {        if (mLayout == null) {            throw new IllegalStateException("RecyclerView has no LayoutManager");        }        return mLayout.generateDefaultLayoutParams();    }    @Override    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {        if (mLayout == null) {            throw new IllegalStateException("RecyclerView has no LayoutManager");        }        return mLayout.generateLayoutParams(getContext(), attrs);    }    @Override    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {        if (mLayout == null) {            throw new IllegalStateException("RecyclerView has no LayoutManager");        }        return mLayout.generateLayoutParams(p);    }    private int findPositionOffset(int position) {        int offset = 0;        int count = mPendingLayoutUpdates.size();        for (int i = 0; i < count; ++i) {            UpdateOp op = mPendingLayoutUpdates.get(i);            if (op.positionStart <= position) {                if (op.cmd == UpdateOp.REMOVE) {                    offset -= op.itemCount;                } else if (op.cmd == UpdateOp.ADD) {                    offset += op.itemCount;                }            }        }        return position + offset;    }    void dispatchLayoutUpdates() {        final int opCount = mPendingLayoutUpdates.size();        for (int i = 0; i < opCount; i++) {            final UpdateOp op = mPendingLayoutUpdates.get(i);            switch (op.cmd) {                case UpdateOp.ADD:                    mLayout.onItemsAdded(this, op.positionStart, op.itemCount);                    break;                case UpdateOp.REMOVE:                    mLayout.onItemsRemoved(this, op.positionStart, op.itemCount);                    break;                case UpdateOp.UPDATE:                    // TODO: tell the layout manager                    break;            }            recycleUpdateOp(op);        }        mPendingLayoutUpdates.clear();    }    void updateChildViews() {        final int opCount = mPendingUpdates.size();        for (int i = 0; i < opCount; i++) {            final UpdateOp op = mPendingUpdates.get(i);            switch (op.cmd) {                case UpdateOp.ADD:                    if (DEBUG) {                        Log.d(TAG, "UpdateOp.ADD start=" + op.positionStart + " count=" +                                op.itemCount);                    }                    offsetPositionRecordsForInsert(op.positionStart, op.itemCount);                    mItemsAddedOrRemoved = true;                    break;                case UpdateOp.REMOVE:                    if (DEBUG) {                        Log.d(TAG, "UpdateOp.REMOVE start=" + op.positionStart + " count=" +                                op.itemCount);                    }                    for (int j = 0; j < op.itemCount; ++j) {                        ViewHolder holder = findViewHolderForPosition(op.positionStart + j, true);                        if (holder != null) {                            holder.setIsRecyclable(false);                        } else {                            mState.mDeletedInvisibleItemCountSincePreviousLayout ++;                        }                    }                    offsetPositionRecordsForRemove(op.positionStart, op.itemCount);                    mItemsAddedOrRemoved = true;                    break;                case UpdateOp.UPDATE:                    if (DEBUG) {                        Log.d(TAG, "UpdateOp.UPDATE start=" + op.positionStart + " count=" +                                op.itemCount);                    }                    viewRangeUpdate(op.positionStart, op.itemCount);                    mItemsChanged = true;                    break;            }            mPendingLayoutUpdates.add(op);            // TODO: recycle the op if no animator (also don't bother stashing in pending layout updates?)        }        mPendingUpdates.clear();    }    void clearOldPositions() {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final ViewHolder holder = getChildViewHolderInt(getChildAt(i));            holder.clearOldPosition();        }        mRecycler.clearOldPositions();    }    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final ViewHolder holder = getChildViewHolderInt(getChildAt(i));            if (holder != null && holder.mPosition >= positionStart) {                if (DEBUG) {                    Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +                            holder + " now at position " + (holder.mPosition + itemCount));                }                holder.offsetPosition(itemCount);                mState.mStructureChanged = true;            }        }        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);        requestLayout();    }    void offsetPositionRecordsForRemove(int positionStart, int itemCount) {        final int positionEnd = positionStart + itemCount;        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final ViewHolder holder = getChildViewHolderInt(getChildAt(i));            if (holder != null) {                if (holder.mPosition >= positionEnd) {                    if (DEBUG) {                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +                                " holder " + holder + " now at position " +                                (holder.mPosition - itemCount));                    }                    holder.offsetPosition(-itemCount);                    mState.mStructureChanged = true;                } else if (holder.mPosition >= positionStart) {                    if (DEBUG) {                        Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +                                " holder " + holder + " now REMOVED");                    }                    holder.addFlags(ViewHolder.FLAG_REMOVED);                    mState.mStructureChanged = true;                }            }        }        mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount);        requestLayout();    }    /**     * Rebind existing views for the given range, or create as needed.     *     * @param positionStart Adapter position to start at     * @param itemCount Number of views that must explicitly be rebound     */    void viewRangeUpdate(int positionStart, int itemCount) {        final int childCount = getChildCount();        final int positionEnd = positionStart + itemCount;        for (int i = 0; i < childCount; i++) {            final ViewHolder holder = getChildViewHolderInt(getChildAt(i));            if (holder == null) {                continue;            }            final int position = holder.getPosition();            if (position >= positionStart && position < positionEnd) {                holder.addFlags(ViewHolder.FLAG_UPDATE);                // Binding an attached view will request a layout if needed.                mAdapter.bindViewHolder(holder, holder.getPosition());            }        }        mRecycler.viewRangeUpdate(positionStart, itemCount);    }    /**     * Mark all known views as invalid. Used in response to a, "the whole world might have changed"     * data change event.     */    void markKnownViewsInvalid() {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final ViewHolder holder = getChildViewHolderInt(getChildAt(i));            if (holder != null) {                holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);            }        }        mRecycler.markKnownViewsInvalid();    }    /**     * Schedule an update of data from the adapter to occur on the next frame.     * On newer platform versions this happens via the postOnAnimation mechanism and RecyclerView     * attempts to avoid relayouts if possible.     * On older platform versions the RecyclerView requests a layout the same way ListView does.     */    void postAdapterUpdate(UpdateOp op) {        mPendingUpdates.add(op);        if (mPendingUpdates.size() == 1) {            if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {                ViewCompat.postOnAnimation(this, mUpdateChildViewsRunnable);            } else {                mAdapterUpdateDuringMeasure = true;                requestLayout();            }        }    }    /**     * Retrieve the {@link ViewHolder} for the given child view.     *     * @param child Child of this RecyclerView to query for its ViewHolder     * @return The child view's ViewHolder     */    public ViewHolder getChildViewHolder(View child) {        final ViewParent parent = child.getParent();        if (parent != null && parent != this) {            throw new IllegalArgumentException("View " + child + " is not a direct child of " +                    this);        }        return getChildViewHolderInt(child);    }    static ViewHolder getChildViewHolderInt(View child) {        if (child == null) {            return null;        }        return ((LayoutParams) child.getLayoutParams()).mViewHolder;    }    /**     * Return the adapter position that the given child view corresponds to.     *     * @param child Child View to query     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}     */    public int getChildPosition(View child) {        final ViewHolder holder = getChildViewHolderInt(child);        return holder != null ? holder.getPosition() : NO_POSITION;    }    /**     * Return the stable item id that the given child view corresponds to.     *     * @param child Child View to query     * @return Item id corresponding to the given view or {@link #NO_ID}     */    public long getChildItemId(View child) {        if (mAdapter == null || !mAdapter.hasStableIds()) {            return NO_ID;        }        final ViewHolder holder = getChildViewHolderInt(child);        return holder != null ? holder.getItemId() : NO_ID;    }    /**     * Return the ViewHolder for the item in the given position of the data set.     *     * @param position The position of the item in the data set of the adapter     * @return The ViewHolder at <code>position</code>     */    public ViewHolder findViewHolderForPosition(int position) {        return findViewHolderForPosition(position, false);    }    ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final ViewHolder holder = getChildViewHolderInt(getChildAt(i));            if (holder != null) {                if (checkNewPosition) {                    if (holder.mPosition == position) {                        return holder;                    }                } else if(holder.getPosition() == position) {                    return holder;                }            }        }        return mRecycler.findViewHolderForPosition(position);    }    /**     * Return the ViewHolder for the item with the given id. The RecyclerView must     * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to     * return a non-null value.     *     * @param id The id for the requested item     * @return The ViewHolder with the given <code>id</code>, of null if there     * is no such item.     */    public ViewHolder findViewHolderForItemId(long id) {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            final ViewHolder holder = getChildViewHolderInt(getChildAt(i));            if (holder != null && holder.getItemId() == id) {                return holder;            }        }        return mRecycler.findViewHolderForItemId(id);    }    /**     * Find the topmost view under the given point.     *     * @param x Horizontal position in pixels to search     * @param y Vertical position in pixels to search     * @return The child view under (x, y) or null if no matching child is found     */    public View findChildViewUnder(float x, float y) {        final int count = getChildCount();        for (int i = count - 1; i >= 0; i--) {            final View child = getChildAt(i);            final float translationX = ViewCompat.getTranslationX(child);            final float translationY = ViewCompat.getTranslationY(child);            if (x >= child.getLeft() + translationX &&                    x <= child.getRight() + translationX &&                    y >= child.getTop() + translationY &&                    y <= child.getBottom() + translationY) {                return child;            }        }        return null;    }    /**     * Offset the bounds of all child views by <code>dy</code> pixels.     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.     *     * @param dy Vertical pixel offset to apply to the bounds of all child views     */    public void offsetChildrenVertical(int dy) {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            getChildAt(i).offsetTopAndBottom(dy);        }    }    /**     * Called when an item view is attached to this RecyclerView.     *     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications     * of child views as they become attached. This will be called before a     * {@link LayoutManager} measures or lays out the view and is a good time to perform these     * changes.</p>     *     * @param child Child view that is now attached to this RecyclerView and its associated window     */    public void onChildAttachedToWindow(View child) {    }    /**     * Called when an item view is detached from this RecyclerView.     *     * <p>Subclasses of RecyclerView may want to perform extra bookkeeping or modifications     * of child views as they become detached. This will be called as a     * {@link LayoutManager} fully detaches the child view from the parent and its window.</p>     *     * @param child Child view that is now detached from this RecyclerView and its associated window     */    public void onChildDetachedFromWindow(View child) {    }    /**     * Offset the bounds of all child views by <code>dx</code> pixels.     * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.     *     * @param dx Horizontal pixel offset to apply to the bounds of all child views     */    public void offsetChildrenHorizontal(int dx) {        final int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            getChildAt(i).offsetLeftAndRight(dx);        }    }    Rect getItemDecorInsetsForChild(View child) {        final LayoutParams lp = (LayoutParams) child.getLayoutParams();        if (!lp.mInsetsDirty) {            return lp.mDecorInsets;        }        final Rect insets = lp.mDecorInsets;        insets.set(0, 0, 0, 0);        final int decorCount = mItemDecorations.size();        for (int i = 0; i < decorCount; i++) {            mTempRect.set(0, 0, 0, 0);            mItemDecorations.get(i).getItemOffsets(mTempRect, lp.getViewPosition(), this);            insets.left += mTempRect.left;            insets.top += mTempRect.top;            insets.right += mTempRect.right;            insets.bottom += mTempRect.bottom;        }        lp.mInsetsDirty = false;        return insets;    }    private class ViewFlinger implements Runnable {        private int mLastFlingX;        private int mLastFlingY;        private ScrollerCompat mScroller;        private Interpolator mInterpolator = sQuinticInterpolator;        // When set to true, postOnAnimation callbacks are delayed until the run method completes        private boolean mEatRunOnAnimationRequest = false;        // Tracks if postAnimationCallback should be re-attached when it is done        private boolean mReSchedulePostAnimationCallback = false;        public ViewFlinger() {            mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);        }        @Override        public void run() {            disableRunOnAnimationRequests();            consumePendingUpdateOperations();            // keep a local reference so that if it is changed during onAnimation method, it wont cause            // unexpected behaviors            final ScrollerCompat scroller = mScroller;            final SmoothScroller smoothScroller = mLayout.mSmoothScroller;            if (scroller.computeScrollOffset()) {                final int x = scroller.getCurrX();                final int y = scroller.getCurrY();                final int dx = x - mLastFlingX;                final int dy = y - mLastFlingY;                mLastFlingX = x;                mLastFlingY = y;                int overscrollX = 0, overscrollY = 0;                if (mAdapter != null) {                    eatRequestLayout();                    if (dx != 0) {                        final int hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);                        overscrollX = dx - hresult;                    }                    if (dy != 0) {                        final int vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);                        overscrollY = dy - vresult;                    }                    if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&                            smoothScroller.isRunning()) {                        smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);                    }                    resumeRequestLayout(false);                }                if (!mItemDecorations.isEmpty()) {                    invalidate();                }                if (overscrollX != 0 || overscrollY != 0) {                    final int vel = (int) scroller.getCurrVelocity();                    int velX = 0;                    if (overscrollX != x) {                        velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;                    }                    int velY = 0;                    if (overscrollY != y) {                        velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;                    }                    if (ViewCompat.getOverScrollMode(RecyclerView.this) !=                            ViewCompat.OVER_SCROLL_NEVER) {                        absorbGlows(velX, velY);                    }                    if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&                            (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {                        scroller.abortAnimation();                    }                }                if (mScrollListener != null && (x != 0 || y != 0)) {                    mScrollListener.onScrolled(dx, dy);                }                if (!awakenScrollBars()) {                    invalidate();                }                if (scroller.isFinished()) {                    setScrollState(SCROLL_STATE_IDLE);                } else {                    postOnAnimation();                }            }            // call this after the onAnimation is complete not to have inconsistent callbacks etc.            if (smoothScroller != null && smoothScroller.isPendingInitialRun()) {                smoothScroller.onAnimation(0, 0);            }            enableRunOnAnimationRequests();        }        private void disableRunOnAnimationRequests() {            mReSchedulePostAnimationCallback = false;            mEatRunOnAnimationRequest = true;        }        private void enableRunOnAnimationRequests() {            mEatRunOnAnimationRequest = false;            if (mReSchedulePostAnimationCallback) {                postOnAnimation();            }        }        void postOnAnimation() {            if (mEatRunOnAnimationRequest) {                mReSchedulePostAnimationCallback = true;            } else {                ViewCompat.postOnAnimation(RecyclerView.this, this);            }        }        public void fling(int velocityX, int velocityY) {            setScrollState(SCROLL_STATE_SETTLING);            mLastFlingX = mLastFlingY = 0;            mScroller.fling(0, 0, velocityX, velocityY,                    Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);            postOnAnimation();        }        public void smoothScrollBy(int dx, int dy) {            smoothScrollBy(dx, dy, 0, 0);        }        public void smoothScrollBy(int dx, int dy, int vx, int vy) {            smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));        }        private float distanceInfluenceForSnapDuration(float f) {            f -= 0.5f; // center the values about 0.            f *= 0.3f * Math.PI / 2.0f;            return (float) Math.sin(f);        }        private int computeScrollDuration(int dx, int dy, int vx, int vy) {            final int absDx = Math.abs(dx);            final int absDy = Math.abs(dy);            final boolean horizontal = absDx > absDy;            final int velocity = (int) Math.sqrt(vx * vx + vy * vy);            final int delta = (int) Math.sqrt(dx * dx + dy * dy);            final int containerSize = horizontal ? getWidth() : getHeight();            final int halfContainerSize = containerSize / 2;            final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);            final float distance = halfContainerSize + halfContainerSize *                    distanceInfluenceForSnapDuration(distanceRatio);            final int duration;            if (velocity > 0) {                duration = 4 * Math.round(1000 * Math.abs(distance / velocity));            } else {                float absDelta = (float) (horizontal ? absDx : absDy);                duration = (int) (((absDelta / containerSize) + 1) * 300);            }            return Math.min(duration, MAX_SCROLL_DURATION);        }        public void smoothScrollBy(int dx, int dy, int duration) {            smoothScrollBy(dx, dy, duration, sQuinticInterpolator);        }        public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {            if (mInterpolator != interpolator) {                mInterpolator = interpolator;                mScroller = ScrollerCompat.create(getContext(), interpolator);            }            setScrollState(SCROLL_STATE_SETTLING);            mLastFlingX = mLastFlingY = 0;            mScroller.startScroll(0, 0, dx, dy, duration);            postOnAnimation();        }        public void stop() {            removeCallbacks(this);            mScroller.abortAnimation();        }    }    private class RecyclerViewDataObserver extends AdapterDataObserver {        @Override        public void onChanged() {            if (mAdapter.hasStableIds()) {                // TODO Determine what actually changed                markKnownViewsInvalid();                mState.mStructureChanged = true;                requestLayout();            } else {                markKnownViewsInvalid();                mState.mStructureChanged = true;                requestLayout();            }        }        @Override        public void onItemRangeChanged(int positionStart, int itemCount) {            postAdapterUpdate(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount));        }        @Override        public void onItemRangeInserted(int positionStart, int itemCount) {            postAdapterUpdate(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount));        }        @Override        public void onItemRangeRemoved(int positionStart, int itemCount) {            postAdapterUpdate(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount));        }    }    public static class RecycledViewPool {        private SparseArray<ArrayList<ViewHolder>> mScrap =                new SparseArray<ArrayList<ViewHolder>>();        private SparseIntArray mMaxScrap = new SparseIntArray();        private int mAttachCount = 0;        private static final int DEFAULT_MAX_SCRAP = 5;        public void clear() {            mScrap.clear();        }        public void setMaxRecycledViews(int viewType, int max) {            mMaxScrap.put(viewType, max);            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);            if (scrapHeap != null) {                while (scrapHeap.size() > max) {                    scrapHeap.remove(scrapHeap.size() - 1);                }            }        }        public ViewHolder getRecycledView(int viewType) {            final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);            if (scrapHeap != null && !scrapHeap.isEmpty()) {                final int index = scrapHeap.size() - 1;                final ViewHolder scrap = scrapHeap.get(index);                scrapHeap.remove(index);                return scrap;            }            return null;        }        public void putRecycledView(ViewHolder scrap) {            final int viewType = scrap.getItemViewType();            final ArrayList scrapHeap = getScrapHeapForType(viewType);            if (mMaxScrap.get(viewType) <= scrapHeap.size()) {                return;            }            scrap.mPosition = NO_POSITION;            scrap.mOldPosition = NO_POSITION;            scrap.mItemId = NO_ID;            scrap.clearFlagsForSharedPool();            scrapHeap.add(scrap);        }        void attach(Adapter adapter) {            mAttachCount++;        }        void detach() {            mAttachCount--;        }        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {            if (mAttachCount == 1) {                clear();            }        }        private ArrayList<ViewHolder> getScrapHeapForType(int viewType) {            ArrayList<ViewHolder> scrap = mScrap.get(viewType);            if (scrap == null) {                scrap = new ArrayList<ViewHolder>();                mScrap.put(viewType, scrap);                if (mMaxScrap.indexOfKey(viewType) < 0) {                    mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);                }            }            return scrap;        }    }    /**     * A Recycler is responsible for managing scrapped or detached item views for reuse.     *     * <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but     * that has been marked for removal or reuse.</p>     *     * <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for     * an adapter's data set representing the data at a given position or item ID.     * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.     * If not, the view can be quickly reused by the LayoutManager with no further work.     * Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}     * may be repositioned by a LayoutManager without remeasurement.</p>     */    public final class Recycler {        private final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<ViewHolder>();        private final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();        private final List<ViewHolder>                mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);        private int mViewCacheMax = DEFAULT_CACHE_SIZE;        private RecycledViewPool mRecyclerPool;        private static final int DEFAULT_CACHE_SIZE = 2;        /**         * Clear scrap views out of this recycler. Detached views contained within a         * recycled view pool will remain.         */        public void clear() {            mAttachedScrap.clear();            recycleCachedViews();        }        /**         * Set the maximum number of detached, valid views we should retain for later use.         *         * @param viewCount Number of views to keep before sending views to the shared pool         */        public void setViewCacheSize(int viewCount) {            mViewCacheMax = viewCount;            while (mCachedViews.size() > viewCount) {                mCachedViews.remove(mCachedViews.size() - 1);            }        }        /**         * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.         *         * @return List of ViewHolders in the scrap list.         */        public List<ViewHolder> getScrapList() {            return mUnmodifiableAttachedScrap;        }        /**         * Helper method for getViewForPosition.         * <p>         * Checks whether a given view holder can be used for the provided position.         *         * @param holder         ViewHolder         * @param offsetPosition The position which is updated by UPDATE_OP changes on the adapter         * @return true if ViewHolder matches the provided position, false otherwise         */        boolean validateViewHolderForOffsetPosition(ViewHolder holder, int offsetPosition) {            // if it is a removed holder, nothing to verify since we cannot ask adapter anymore            // if it is not removed, verify the type and id.            if (holder.isRemoved()) {                return true;            }            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {                if (DEBUG) {                    Log.d(TAG, "validateViewHolderForOffsetPosition: invalid position, returning "                            + "false");                }                return false;            }            final int type = mAdapter.getItemViewType(offsetPosition);            if (type != holder.getItemViewType()) {                return false;            }            if (mAdapter.hasStableIds()) {                return holder.getItemId() == mAdapter.getItemId(offsetPosition);            }            return true;        }        /**         * Obtain a view initialized for the given position.         *         * <p>This method should be used by {@link LayoutManager} implementations to obtain         * views to represent data from an {@link Adapter}.</p>         *         * <p>The Recycler may reuse a scrap or detached view from a shared pool if one is         * available for the correct view type. If the adapter has not indicated that the         * data at the given position has changed, the Recycler will attempt to hand back         * a scrap view that was previously initialized for that data without rebinding.</p>         *         * @param position Position to obtain a view for         * @return A view representing the data at <code>position</code> from <code>adapter</code>         */        public View getViewForPosition(int position) {            ViewHolder holder;            holder = getScrapViewForPosition(position, INVALID_TYPE);            final int offsetPosition = findPositionOffset(position);            if (holder != null) {                if (!validateViewHolderForOffsetPosition(holder, offsetPosition)) {                    // recycle this scrap                    removeDetachedView(holder.itemView, false);                    quickRecycleScrapView(holder.itemView);                    // if validate fails, we can query scrap again w/ type. that may return a                    // different view holder from cache.                    final int type = mAdapter.getItemViewType(offsetPosition);                    if (mAdapter.hasStableIds()) {                        final long id = mAdapter.getItemId(offsetPosition);                        holder = getScrapViewForId(id, type);                    } else {                        holder = getScrapViewForPosition(offsetPosition, type);                    }                }            } else {                // try recycler.                holder = getRecycledViewPool()                        .getRecycledView(mAdapter.getItemViewType(offsetPosition));            }            if (holder == null) {                if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {                    throw new IndexOutOfBoundsException("Invalid item position " + position                            + "(" + offsetPosition + ")");                } else {                    holder = mAdapter.createViewHolder(RecyclerView.this,                            mAdapter.getItemViewType(offsetPosition));                    if (DEBUG) {                        Log.d(TAG, "getViewForPosition created new ViewHolder");                    }                }            }            if (!holder.isRemoved() && (!holder.isBound() || holder.needsUpdate())) {                if (DEBUG) {                    Log.d(TAG, "getViewForPosition unbound holder or needs update; updating...");                }                // TODO: think through when getOffsetPosition() is called. I use it here because                // existing views have already been offset appropriately through the mOldOffset                // mechanism, but new views do not have this mechanism.                mAdapter.bindViewHolder(holder, offsetPosition);            }            ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();            if (lp == null) {                lp = generateDefaultLayoutParams();                holder.itemView.setLayoutParams(lp);            } else if (!checkLayoutParams(lp)) {                lp = generateLayoutParams(lp);                holder.itemView.setLayoutParams(lp);            }            ((LayoutParams) lp).mViewHolder = holder;            return holder.itemView;        }        /**         * Recycle a detached view. The specified view will be added to a pool of views         * for later rebinding and reuse.         *         * <p>A view must be fully detached before it may be recycled.</p>         *         * @param view Removed view for recycling         */        public void recycleView(View view) {            recycleViewHolder(getChildViewHolderInt(view));        }        void recycleCachedViews() {            final int count = mCachedViews.size();            for (int i = count - 1; i >= 0; i--) {                final ViewHolder cachedView = mCachedViews.get(i);                if (cachedView.isRecyclable()) {                    getRecycledViewPool().putRecycledView(cachedView);                    dispatchViewRecycled(cachedView);                }                mCachedViews.remove(i);            }        }        void recycleViewHolder(ViewHolder holder) {            if (holder.isScrap() || holder.itemView.getParent() != null) {                throw new IllegalArgumentException(                        "Scrapped or attached views may not be recycled.");            }            boolean cached = false;            if (!holder.isInvalid() && (mInPreLayout || !holder.isRemoved())) {                // Retire oldest cached views first                if (mCachedViews.size() == mViewCacheMax && !mCachedViews.isEmpty()) {                    for (int i = 0; i < mCachedViews.size(); i++) {                        final ViewHolder cachedView = mCachedViews.get(i);                        if (cachedView.isRecyclable()) {                            mCachedViews.remove(i);                            getRecycledViewPool().putRecycledView(cachedView);                            dispatchViewRecycled(cachedView);                            break;                        }                    }                }                if (mCachedViews.size() < mViewCacheMax) {                    mCachedViews.add(holder);                    cached = true;                }            }            if (!cached && holder.isRecyclable()) {                getRecycledViewPool().putRecycledView(holder);                dispatchViewRecycled(holder);            }            // Remove from pre/post maps that are used to animate items; a recycled holder            // should not be animated            mState.mPreLayoutHolderMap.remove(holder);            mState.mPostLayoutHolderMap.remove(holder);        }        /**         * Used as a fast path for unscrapping and recycling a view during a bulk operation.         * The caller must call {@link #clearScrap()} when it's done to update the recycler's         * internal bookkeeping.         */        void quickRecycleScrapView(View view) {            final ViewHolder holder = getChildViewHolderInt(view);            holder.mScrapContainer = null;            recycleViewHolder(holder);        }        /**         * Mark an attached view as scrap.         *         * <p>"Scrap" views are still attached to their parent RecyclerView but are eligible         * for rebinding and reuse. Requests for a view for a given position may return a         * reused or rebound scrap view instance.</p>         *         * @param view View to scrap         */        void scrapView(View view) {            final ViewHolder holder = getChildViewHolderInt(view);            holder.setScrapContainer(this);            mAttachedScrap.add(holder);        }        /**         * Remove a previously scrapped view from the pool of eligible scrap.         *         * <p>This view will no longer be eligible for reuse until re-scrapped or         * until it is explicitly removed and recycled.</p>         */        void unscrapView(ViewHolder holder) {            mAttachedScrap.remove(holder);            holder.mScrapContainer = null;        }        int getScrapCount() {            return mAttachedScrap.size();        }        View getScrapViewAt(int index) {            return mAttachedScrap.get(index).itemView;        }        void clearScrap() {            mAttachedScrap.clear();        }        /**         * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if         * ViewHolder's type matches the provided type.         *         * @param position Item position         * @param type View type         * @return a ViewHolder that can be re-used for this position.         */        ViewHolder getScrapViewForPosition(int position, int type) {            final int scrapCount = mAttachedScrap.size();            // Try first for an exact, non-invalid match from scrap.            for (int i = 0; i < scrapCount; i++) {                final ViewHolder holder = mAttachedScrap.get(i);                if (holder.getPosition() == position && !holder.isInvalid() &&                        (mInPreLayout || !holder.isRemoved())) {                    if (type != INVALID_TYPE && holder.getItemViewType() != type) {                        Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +                                " wrong view type! (found " + holder.getItemViewType() +                                " but expected " + type + ")");                        break;                    }                    mAttachedScrap.remove(i);                    holder.setScrapContainer(null);                    if (DEBUG) {                        Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +                            ") found exact match in scrap: " + holder);                    }                    return holder;                }            }            if (mNumAnimatingViews != 0) {                View view = getAnimatingView(position, type);                if (view != null) {                    // ending the animation should cause it to get recycled before we reuse it                    mItemAnimator.endAnimation(getChildViewHolder(view));                }            }            // Search in our first-level recycled view cache.            final int cacheSize = mCachedViews.size();            for (int i = 0; i < cacheSize; i++) {                final ViewHolder holder = mCachedViews.get(i);                if (holder.getPosition() == position) {                    mCachedViews.remove(i);                    if (holder.isInvalid() &&                            (type != INVALID_TYPE && holder.getItemViewType() != type)) {                        // Can't use it. We don't know where it's been.                        if (DEBUG) {                            Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +                                    ") found position match, but holder is invalid with type " +                                    holder.getItemViewType());                        }                        if (holder.isRecyclable()) {                            getRecycledViewPool().putRecycledView(holder);                        }                        // Even if the holder wasn't officially recycleable, dispatch that it                        // was recycled anyway in case there are resources to unbind.                        dispatchViewRecycled(holder);                        // Drop out of the cache search and try something else instead,                        // we won't find another match here.                        break;                    }                    if (DEBUG) {                        Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +                                ") found match in cache: " + holder);                    }                    return holder;                }            }            // Give up. Head to the shared pool.            if (DEBUG) {                Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +                    ") fetching from shared pool");            }            return type == INVALID_TYPE ? null : getRecycledViewPool().getRecycledView(type);        }        ViewHolder getScrapViewForId(long id, int type) {            // Look in our attached views first            final int count = mAttachedScrap.size();            for (int i = 0; i < count; i++) {                final ViewHolder holder = mAttachedScrap.get(i);                if (holder.getItemId() == id) {                    if (type == holder.getItemViewType()) {                        mAttachedScrap.remove(i);                        holder.setScrapContainer(null);                        return holder;                    } else {                        break;                    }                }            }            // Search the first-level cache            final int cacheSize = mCachedViews.size();            for (int i = 0; i < cacheSize; i++) {                final ViewHolder holder = mCachedViews.get(i);                if (holder.getItemId() == id) {                    mCachedViews.remove(i);                    return holder;                }            }            // That didn't work, look for an unordered view of the right type instead.            // The holder's position won't match so the calling code will need to have            // the adapter rebind it.            return getRecycledViewPool().getRecycledView(type);        }        void dispatchViewRecycled(ViewHolder holder) {            if (mRecyclerListener != null) {                mRecyclerListener.onViewRecycled(holder);            }            if (mAdapter != null) {                mAdapter.onViewRecycled(holder);            }            if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);        }        void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {            clear();            getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter);        }        void offsetPositionRecordsForInsert(int insertedAt, int count) {            final int cachedCount = mCachedViews.size();            for (int i = 0; i < cachedCount; i++) {                final ViewHolder holder = mCachedViews.get(i);                if (holder != null && holder.getPosition() >= insertedAt) {                    if (DEBUG) {                        Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +                                holder + " now at position " + (holder.mPosition + count));                    }                    holder.offsetPosition(count);                }            }        }        void offsetPositionRecordsForRemove(int removedFrom, int count) {            final int removedEnd = removedFrom + count;            final int cachedCount = mCachedViews.size();            for (int i = cachedCount - 1; i >= 0; i--) {                final ViewHolder holder = mCachedViews.get(i);                if (holder != null) {                    if (holder.getPosition() >= removedEnd) {                        if (DEBUG) {                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +                                    " holder " + holder + " now at position " +                                    (holder.mPosition - count));                        }                        holder.offsetPosition(-count);                    } else if (holder.getPosition() >= removedFrom) {                        // Item for this view was removed. Dump it from the cache.                        if (DEBUG) {                            Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +                                    " holder " + holder + " now placed in pool");                        }                        mCachedViews.remove(i);                        getRecycledViewPool().putRecycledView(holder);                        dispatchViewRecycled(holder);                    }                }            }        }        void setRecycledViewPool(RecycledViewPool pool) {            if (mRecyclerPool != null) {                mRecyclerPool.detach();            }            mRecyclerPool = pool;            if (pool != null) {                mRecyclerPool.attach(getAdapter());            }        }        RecycledViewPool getRecycledViewPool() {            if (mRecyclerPool == null) {                mRecyclerPool = new RecycledViewPool();            }            return mRecyclerPool;        }        ViewHolder findViewHolderForPosition(int position) {            final int cachedCount = mCachedViews.size();            for (int i = 0; i < cachedCount; i++) {                final ViewHolder holder = mCachedViews.get(i);                if (holder != null && holder.getPosition() == position) {                    mCachedViews.remove(i);                    return holder;                }            }            return null;        }        ViewHolder findViewHolderForItemId(long id) {            if (!mAdapter.hasStableIds()) {                return null;            }            final int cachedCount = mCachedViews.size();            for (int i = 0; i < cachedCount; i++) {                final ViewHolder holder = mCachedViews.get(i);                if (holder != null && holder.getItemId() == id) {                    mCachedViews.remove(i);                    return holder;                }            }            return null;        }        void viewRangeUpdate(int positionStart, int itemCount) {            final int positionEnd = positionStart + itemCount;            final int cachedCount = mCachedViews.size();            for (int i = 0; i < cachedCount; i++) {                final ViewHolder holder = mCachedViews.get(i);                if (holder == null) {                    continue;                }                final int pos = holder.getPosition();                if (pos >= positionStart && pos < positionEnd) {                    holder.addFlags(ViewHolder.FLAG_UPDATE);                }            }        }        void markKnownViewsInvalid() {            final int cachedCount = mCachedViews.size();            for (int i = 0; i < cachedCount; i++) {                final ViewHolder holder = mCachedViews.get(i);                if (holder != null) {                    holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);                }            }        }        void clearOldPositions() {            final int cachedCount = mCachedViews.size();            for (int i = 0; i < cachedCount; i++) {                final ViewHolder holder = mCachedViews.get(i);                holder.clearOldPosition();            }        }    }    /**     * Base class for an Adapter     *     * <p>Adapters provide a binding from an app-specific data set to views that are displayed     * within a {@link RecyclerView}.</p>     */    public static abstract class Adapter<VH extends ViewHolder> {        private final AdapterDataObservable mObservable = new AdapterDataObservable();        private boolean mHasStableIds = false;        public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);        public abstract void onBindViewHolder(VH holder, int position);        public final VH createViewHolder(ViewGroup parent, int viewType) {            final VH holder = onCreateViewHolder(parent, viewType);            holder.mItemViewType = viewType;            return holder;        }        public final void bindViewHolder(VH holder, int position) {            holder.mPosition = position;            if (hasStableIds()) {                holder.mItemId = getItemId(position);            }            onBindViewHolder(holder, position);            holder.setFlags(ViewHolder.FLAG_BOUND,                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);        }        /**         * Return the view type of the item at <code>position</code> for the purposes         * of view recycling.         *         * <p>The default implementation of this method returns 0, making the assumption of         * a single view type for the adapter. Unlike ListView adapters, types need not         * be contiguous. Consider using id resources to uniquely identify item view types.         *         * @param position position to query         * @return integer value identifying the type of the view needed to represent the item at         *                 <code>position</code>. Type codes need not be contiguous.         */        public int getItemViewType(int position) {            return 0;        }        public void setHasStableIds(boolean hasStableIds) {            if (hasObservers()) {                throw new IllegalStateException("Cannot change whether this adapter has " +                        "stable IDs while the adapter has registered observers.");            }            mHasStableIds = hasStableIds;        }        /**         * Return the stable ID for the item at <code>position</code>. If {@link #hasStableIds()}         * would return false this method should return {@link #NO_ID}. The default implementation         * of this method returns {@link #NO_ID}.         *         * @param position Adapter position to query         * @return the stable ID of the item at position         */        public long getItemId(int position) {            return NO_ID;        }        public abstract int getItemCount();        /**         * Returns true if this adapter publishes a unique <code>long</code> value that can         * act as a key for the item at a given position in the data set. If that item is relocated         * in the data set, the ID returned for that item should be the same.         *         * @return true if this adapter's items have stable IDs         */        public final boolean hasStableIds() {            return mHasStableIds;        }        /**         * Called when a view created by this adapter has been recycled.         *         * <p>A view is recycled when a {@link LayoutManager} decides that it no longer         * needs to be attached to its parent {@link RecyclerView}. This can be because it has         * fallen out of visibility or a set of cached views represented by views still         * attached to the parent RecyclerView. If an item view has large or expensive data         * bound to it such as large bitmaps, this may be a good place to release those         * resources.</p>         *         * @param holder The ViewHolder for the view being recycled         */        public void onViewRecycled(VH holder) {        }        /**         * Called when a view created by this adapter has been attached to a window.         *         * <p>This can be used as a reasonable signal that the view is about to be seen         * by the user. If the adapter previously freed any resources in         * {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}         * those resources should be restored here.</p>         *         * @param holder Holder of the view being attached         */        public void onViewAttachedToWindow(VH holder) {        }        /**         * Called when a view created by this adapter has been detached from its window.         *         * <p>Becoming detached from the window is not necessarily a permanent condition;         * the consumer of an Adapter's views may choose to cache views offscreen while they         * are not visible, attaching an detaching them as appropriate.</p>         *         * @param holder Holder of the view being detached         */        public void onViewDetachedFromWindow(VH holder) {        }        /**         * Returns true if one or more observers are attached to this adapter.         * @return true if this adapter has observers         */        public final boolean hasObservers() {            return mObservable.hasObservers();        }        /**         * Register a new observer to listen for data changes.         *         * <p>The adapter may publish a variety of events describing specific changes.         * Not all adapters may support all change types and some may fall back to a generic         * {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()         * "something changed"} event if more specific data is not available.</p>         *         * <p>Components registering observers with an adapter are responsible for         * {@link #unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver)         * unregistering} those observers when finished.</p>         *         * @param observer Observer to register         *         * @see #unregisterAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver)         */        public void registerAdapterDataObserver(AdapterDataObserver observer) {            mObservable.registerObserver(observer);        }        /**         * Unregister an observer currently listening for data changes.         *         * <p>The unregistered observer will no longer receive events about changes         * to the adapter.</p>         *         * @param observer Observer to unregister         *         * @see #registerAdapterDataObserver(android.support.v7.widget.RecyclerView.AdapterDataObserver)         */        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {            mObservable.unregisterObserver(observer);        }        /**         * Notify any registered observers that the data set has changed.         *         * <p>There are two different classes of data change events, item changes and structural         * changes. Item changes are when a single item has its data updated but no positional         * changes have occurred. Structural changes are when items are inserted, removed or moved         * within the data set.</p>         *         * <p>This event does not specify what about the data set has changed, forcing         * any observers to assume that all existing items and structure may no longer be valid.         * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>         *         * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events         * for adapters that report that they have {@link #hasStableIds() stable IDs} when         * this method is used. This can help for the purposes of animation and visual         * object persistence but individual item views will still need to be rebound         * and relaid out.</p>         *         * <p>If you are writing an adapter it will always be more efficient to use the more         * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>         * as a last resort.</p>         *         * @see #notifyItemChanged(int)         * @see #notifyItemInserted(int)         * @see #notifyItemRemoved(int)         * @see #notifyItemRangeChanged(int, int)         * @see #notifyItemRangeInserted(int, int)         * @see #notifyItemRangeRemoved(int, int)         */        public final void notifyDataSetChanged() {            mObservable.notifyChanged();        }        /**         * Notify any registered observers that the item at <code>position</code> has changed.         *         * <p>This is an item change event, not a structural change event. It indicates that any         * reflection of the data at <code>position</code> is out of date and should be updated.         * The item at <code>position</code> retains the same identity.</p>         *         * @param position Position of the item that has changed         *         * @see #notifyItemRangeChanged(int, int)         */        public final void notifyItemChanged(int position) {            mObservable.notifyItemRangeChanged(position, 1);        }        /**         * Notify any registered observers that the <code>itemCount</code> items starting at         * position <code>positionStart</code> have changed.         *         * <p>This is an item change event, not a structural change event. It indicates that         * any reflection of the data in the given position range is out of date and should         * be updated. The items in the given range retain the same identity.</p>         *         * @param positionStart Position of the first item that has changed         * @param itemCount Number of items that have changed         *         * @see #notifyItemChanged(int)         */        public final void notifyItemRangeChanged(int positionStart, int itemCount) {            mObservable.notifyItemRangeChanged(positionStart, itemCount);        }        /**         * Notify any registered observers that the item reflected at <code>position</code>         * has been newly inserted. The item previously at <code>position</code> is now at         * position <code>position + 1</code>.         *         * <p>This is a structural change event. Representations of other existing items in the         * data set are still considered up to date and will not be rebound, though their         * positions may be altered.</p>         *         * @param position Position of the newly inserted item in the data set         *         * @see #notifyItemRangeInserted(int, int)         */        public final void notifyItemInserted(int position) {            mObservable.notifyItemRangeInserted(position, 1);        }        /**         * Notify any registered observers that the currently reflected <code>itemCount</code>         * items starting at <code>positionStart</code> have been newly inserted. The items         * previously located at <code>positionStart</code> and beyond can now be found starting         * at position <code>positionStart + itemCount</code>.         *         * <p>This is a structural change event. Representations of other existing items in the         * data set are still considered up to date and will not be rebound, though their positions         * may be altered.</p>         *         * @param positionStart Position of the first item that was inserted         * @param itemCount Number of items inserted         *         * @see #notifyItemInserted(int)         */        public final void notifyItemRangeInserted(int positionStart, int itemCount) {            mObservable.notifyItemRangeInserted(positionStart, itemCount);        }        /**         * Notify any registered observers that the item previously located at <code>position</code>         * has been removed from the data set. The items previously located at and after         * <code>position</code> may now be found at <code>oldPosition - 1</code>.         *         * <p>This is a structural change event. Representations of other existing items in the         * data set are still considered up to date and will not be rebound, though their positions         * may be altered.</p>         *         * @param position Position of the item that has now been removed         *         * @see #notifyItemRangeRemoved(int, int)         */        public final void notifyItemRemoved(int position) {            mObservable.notifyItemRangeRemoved(position, 1);        }        /**         * Notify any registered observers that the <code>itemCount</code> items previously         * located at <code>positionStart</code> have been removed from the data set. The items         * previously located at and after <code>positionStart + itemCount</code> may now be found         * at <code>oldPosition - itemCount</code>.         *         * <p>This is a structural change event. Representations of other existing items in the data         * set are still considered up to date and will not be rebound, though their positions         * may be altered.</p>         *         * @param positionStart Previous position of the first item that was removed         * @param itemCount Number of items removed from the data set         */        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {            mObservable.notifyItemRangeRemoved(positionStart, itemCount);        }    }    /**     * A <code>LayoutManager</code> is responsible for measuring and positioning item views     * within a <code>RecyclerView</code> as well as determining the policy for when to recycle     * item views that are no longer visible to the user. By changing the <code>LayoutManager</code>     * a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,     * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock     * layout managers are provided for general use.     */    public static abstract class LayoutManager {        RecyclerView mRecyclerView;        @Nullable        SmoothScroller mSmoothScroller;        /**         * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView         */        public void requestLayout() {            if(mRecyclerView != null) {                mRecyclerView.requestLayout();            }        }        /**         * Returns whether this LayoutManager supports automatic item animations.         * A LayoutManager wishing to support item animations should obey certain         * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.         * The default return value is <code>false</code>, so subclasses of LayoutManager         * will not get predictive item animations by default.         *         * <p>Whether item animations are enabled in a RecyclerView is determined both         * by the return value from this method and the         * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the         * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this         * method returns false, then simple item animations will be enabled, in which         * views that are moving onto or off of the screen are simply faded in/out. If         * the RecyclerView has a non-null ItemAnimator and this method returns true,         * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to         * setup up the information needed to more intelligently predict where appearing         * and disappearing views should be animated from/to.</p>         *         * @return true if predictive item animations should be enabled, false otherwise         */        public boolean supportsPredictiveItemAnimations() {            return false;        }        /**         * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView         * is attached to a window.         *         * <p>Subclass implementations should always call through to the superclass implementation.         * </p>         *         * @param view The RecyclerView this LayoutManager is bound to         */        public void onAttachedToWindow(RecyclerView view) {        }        /**         * Called when this LayoutManager is detached from its parent RecyclerView or when         * its parent RecyclerView is detached from its window.         *         * <p>Subclass implementations should always call through to the superclass implementation.         * </p>         *         * @param view The RecyclerView this LayoutManager is bound to         */        public void onDetachedFromWindow(RecyclerView view) {        }        /**         * Lay out all relevant child views from the given adapter.         *         * The LayoutManager is in charge of the behavior of item animations. By default,         * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple         * item animations are enabled. This means that add/remove operations on the         * adapter will result in animations to add new or appearing items, removed or         * disappearing items, and moved items. If a LayoutManager returns false from         * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a         * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the         * RecyclerView will have enough information to run those animations in a simple         * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will         * simple fade views in and out, whether they are actuall added/removed or whether         * they are moved on or off the screen due to other add/remove operations.         *         * <p>A LayoutManager wanting a better item animation experience, where items can be         * animated onto and off of the screen according to where the items exist when they         * are not on screen, then the LayoutManager should return true from         * {@link #supportsPredictiveItemAnimations()} and add additional logic to         * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations         * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;         * once as a "pre" layout step to determine where items would have been prior to         * a real layout, and again to do the "real" layout. In the pre-layout phase,         * items will remember their pre-layout positions to allow them to be laid out         * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will         * be returned from the scrap to help determine correct placement of other items.         * These removed items should not be added to the child list, but should be used         * to help calculate correct positioning of other views, including views that         * were not previously onscreen (referred to as APPEARING views), but whose         * pre-layout offscreen position can be determined given the extra         * information about the pre-layout removed views.</p>         *         * <p>The second layout pass is the real layout in which only non-removed views         * will be used. The only additional requirement during this pass is, if         * {@link #supportsPredictiveItemAnimations()} returns true, to note which         * views exist in the child list prior to layout and which are not there after         * layout (referred to as DISAPPEARING views), and to position/layout those views         * appropriately, without regard to the actual bounds of the RecyclerView. This allows         * the animation system to know the location to which to animate these disappearing         * views.</p>         *         * <p>The default LayoutManager implementations for RecyclerView handle all of these         * requirements for animations already. Clients of RecyclerView can either use one         * of these layout managers directly or look at their implementations of         * onLayoutChildren() to see how they account for the APPEARING and         * DISAPPEARING views.</p>         *         * @param recycler         Recycler to use for fetching potentially cached views for a         *                         position         * @param state            Transient state of RecyclerView         */        public void onLayoutChildren(Recycler recycler, State state) {            Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");        }        /**         * Create a default <code>LayoutParams</code> object for a child of the RecyclerView.         *         * <p>LayoutManagers will often want to use a custom <code>LayoutParams</code> type         * to store extra information specific to the layout. Client code should subclass         * {@link RecyclerView.LayoutParams} for this purpose.</p>         *         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type         * you must also override         * {@link #checkLayoutParams(LayoutParams)},         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>         *         * @return A new LayoutParams for a child view         */        public abstract LayoutParams generateDefaultLayoutParams();        /**         * Determines the validity of the supplied LayoutParams object.         *         * <p>This should check to make sure that the object is of the correct type         * and all values are within acceptable ranges. The default implementation         * returns <code>true</code> for non-null params.</p>         *         * @param lp LayoutParams object to check         * @return true if this LayoutParams object is valid, false otherwise         */        public boolean checkLayoutParams(LayoutParams lp) {            return lp != null;        }        /**         * Create a LayoutParams object suitable for this LayoutManager, copying relevant         * values from the supplied LayoutParams object if possible.         *         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type         * you must also override         * {@link #checkLayoutParams(LayoutParams)},         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>         *         * @param lp Source LayoutParams object to copy values from         * @return a new LayoutParams object         */        public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {            if (lp instanceof LayoutParams) {                return new LayoutParams((LayoutParams) lp);            } else if (lp instanceof MarginLayoutParams) {                return new LayoutParams((MarginLayoutParams) lp);            } else {                return new LayoutParams(lp);            }        }        /**         * Create a LayoutParams object suitable for this LayoutManager from         * an inflated layout resource.         *         * <p><em>Important:</em> if you use your own custom <code>LayoutParams</code> type         * you must also override         * {@link #checkLayoutParams(LayoutParams)},         * {@link #generateLayoutParams(android.view.ViewGroup.LayoutParams)} and         * {@link #generateLayoutParams(android.content.Context, android.util.AttributeSet)}.</p>         *         * @param c Context for obtaining styled attributes         * @param attrs AttributeSet describing the supplied arguments         * @return a new LayoutParams object         */        public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {            return new LayoutParams(c, attrs);        }        /**         * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.         * The default implementation does nothing and returns 0.         *         * @param dx            distance to scroll by in pixels. X increases as scroll position         *                      approaches the right.         * @param recycler      Recycler to use for fetching potentially cached views for a         *                      position         * @param state         Transient state of RecyclerView         * @return The actual distance scrolled. The return value will be negative if dx was         * negative and scrolling proceeeded in that direction.         * <code>Math.abs(result)</code> may be less than dx if a boundary was reached.         */        public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {            return 0;        }        /**         * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.         * The default implementation does nothing and returns 0.         *         * @param dy            distance to scroll in pixels. Y increases as scroll position         *                      approaches the bottom.         * @param recycler      Recycler to use for fetching potentially cached views for a         *                      position         * @param state         Transient state of RecyclerView         * @return The actual distance scrolled. The return value will be negative if dy was         * negative and scrolling proceeeded in that direction.         * <code>Math.abs(result)</code> may be less than dy if a boundary was reached.         */        public int scrollVerticallyBy(int dy, Recycler recycler, State state) {            return 0;        }        /**         * Query if horizontal scrolling is currently supported. The default implementation         * returns false.         *         * @return True if this LayoutManager can scroll the current contents horizontally         */        public boolean canScrollHorizontally() {            return false;        }        /**         * Query if vertical scrolling is currently supported. The default implementation         * returns false.         *         * @return True if this LayoutManager can scroll the current contents vertically         */        public boolean canScrollVertically() {            return false;        }        /**         * Scroll to the specified adapter position.         *         * Actual position of the item on the screen depends on the LayoutManager implementation.         * @param position Scroll to this adapter position.         */        public void scrollToPosition(int position) {            if (DEBUG) {                Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");            }        }        /**         * <p>Smooth scroll to the specified adapter position.</p>         * <p>To support smooth scrolling, override this method, create your {@link SmoothScroller}         * instance and call {@link #startSmoothScroll(SmoothScroller)}.         * </p>         * @param recyclerView The RecyclerView to which this layout manager is attached         * @param state    Current State of RecyclerView         * @param position Scroll to this adapter position.         */        public void smoothScrollToPosition(RecyclerView recyclerView, State state,                int position) {            Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");        }        /**         * <p>Starts a smooth scroll using the provided SmoothScroller.</p>         * <p>Calling this method will cancel any previous smooth scroll request.</p>         * @param smoothScroller Unstance which defines how smooth scroll should be animated         */        public void startSmoothScroll(SmoothScroller smoothScroller) {            if (mSmoothScroller != null && smoothScroller != mSmoothScroller                    && mSmoothScroller.isRunning()) {                mSmoothScroller.stop();            }            mSmoothScroller = smoothScroller;            mSmoothScroller.start(mRecyclerView, this);        }        /**         * @return true if RecycylerView is currently in the state of smooth scrolling.         */        public boolean isSmoothScrolling() {            return mSmoothScroller != null && mSmoothScroller.isRunning();        }        /**         * Returns the resolved layout direction for this RecyclerView.         *         * @return {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL} if the layout         * direction is RTL or returns         * {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction         * is not RTL.         */        public int getLayoutDirection() {            return ViewCompat.getLayoutDirection(mRecyclerView);        }        /**         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should         * use this method to add views obtained from a {@link Recycler} using         * {@link Recycler#getViewForPosition(int)}.         *         * @param child View to add         * @param index Index to add child at         */        public void addView(View child, int index) {            if (mRecyclerView.mAnimatingViewIndex >= 0) {                if (index > mRecyclerView.mAnimatingViewIndex) {                    throw new IndexOutOfBoundsException("index=" + index + " count="                            + mRecyclerView.mAnimatingViewIndex);                }                mRecyclerView.mAnimatingViewIndex++;            }            final ViewHolder holder = getChildViewHolderInt(child);            if (holder.isScrap()) {                holder.unScrap();                mRecyclerView.attachViewToParent(child, index, child.getLayoutParams());                if (DISPATCH_TEMP_DETACH) {                    ViewCompat.dispatchFinishTemporaryDetach(child);                }            } else {                mRecyclerView.addView(child, index);                final LayoutParams lp = (LayoutParams) child.getLayoutParams();                lp.mInsetsDirty = true;                final Adapter adapter = mRecyclerView.getAdapter();                if (adapter != null) {                    adapter.onViewAttachedToWindow(getChildViewHolderInt(child));                }                mRecyclerView.onChildAttachedToWindow(child);                if (mSmoothScroller != null && mSmoothScroller.isRunning()) {                    mSmoothScroller.onChildAttachedToWindow(child);                }            }        }        /**         * Add a view to the currently attached RecyclerView if needed. LayoutManagers should         * use this method to add views obtained from a {@link Recycler} using         * {@link Recycler#getViewForPosition(int)}.         *         * @param child View to add         */        public void addView(View child) {            if (mRecyclerView.mAnimatingViewIndex >= 0) {                addView(child, mRecyclerView.mAnimatingViewIndex);            } else {                addView(child, -1);            }        }        /**         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should         * use this method to completely remove a child view that is no longer needed.         * LayoutManagers should strongly consider recycling removed views using         * {@link Recycler#recycleView(android.view.View)}.         *         * @param child View to remove         */        public void removeView(View child) {            final Adapter adapter = mRecyclerView.getAdapter();            if (adapter != null) {                adapter.onViewDetachedFromWindow(getChildViewHolderInt(child));            }            mRecyclerView.onChildDetachedFromWindow(child);            mRecyclerView.removeView(child);            if (mRecyclerView.mAnimatingViewIndex >= 0) {                mRecyclerView.mAnimatingViewIndex--;            }        }        /**         * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should         * use this method to completely remove a child view that is no longer needed.         * LayoutManagers should strongly consider recycling removed views using         * {@link Recycler#recycleView(android.view.View)}.         *         * @param index Index of the child view to remove         */        public void removeViewAt(int index) {            final View child = mRecyclerView.getChildAt(index);            if (child != null) {                final Adapter adapter = mRecyclerView.getAdapter();                if (adapter != null) {                    adapter.onViewDetachedFromWindow(getChildViewHolderInt(child));                }                mRecyclerView.onChildDetachedFromWindow(child);                mRecyclerView.removeViewAt(index);                if (mRecyclerView.mAnimatingViewIndex >= 0) {                    mRecyclerView.mAnimatingViewIndex--;                }            }        }        /**         * Remove all views from the currently attached RecyclerView. This will not recycle         * any of the affected views; the LayoutManager is responsible for doing so if desired.         */        public void removeAllViews() {            final Adapter adapter = mRecyclerView.getAdapter();            // Only remove non-animating views            final int childCount = mRecyclerView.getChildCount() - mRecyclerView.mNumAnimatingViews;            for (int i = 0; i < childCount; i++) {                final View child = mRecyclerView.getChildAt(i);                if (adapter != null) {                    adapter.onViewDetachedFromWindow(getChildViewHolderInt(child));                }                mRecyclerView.onChildDetachedFromWindow(child);            }            for (int i = childCount - 1; i >= 0; i--) {                mRecyclerView.removeViewAt(i);                if (mRecyclerView.mAnimatingViewIndex >= 0) {                    mRecyclerView.mAnimatingViewIndex--;                }            }        }        /**         * Returns the adapter position of the item represented by the given View.         *         * @param view The view to query         * @return The adapter position of the item which is rendered by this View.         */        public int getPosition(View view) {            return ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewPosition();        }        /**         * <p>Finds the view which represents the given adapter position.</p>         * <p>This method traverses each child since it has no information about child order.         * Override this method to improve performance if your LayoutManager keeps data about         * child views.</p>         *         * @param position Position of the item in adapter         * @return The child view that represents the given position or null if the position is not         * visible         */        public View findViewByPosition(int position) {            final int childCount = getChildCount();            for (int i = 0; i < childCount; i++) {                View child = getChildAt(i);                if (getPosition(child) == position) {                    return child;                }            }            return null;        }        /**         * Temporarily detach a child view.         *         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange         * views currently attached to the RecyclerView. Generally LayoutManager implementations         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}         * so that the detached view may be rebound and reused.</p>         *         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view         * before the LayoutManager entry point method called by RecyclerView returns.</p>         *         * @param child Child to detach         */        public void detachView(View child) {            if (DISPATCH_TEMP_DETACH) {                ViewCompat.dispatchStartTemporaryDetach(child);            }            mRecyclerView.detachViewFromParent(child);        }        /**         * Temporarily detach a child view.         *         * <p>LayoutManagers may want to perform a lightweight detach operation to rearrange         * views currently attached to the RecyclerView. Generally LayoutManager implementations         * will want to use {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}         * so that the detached view may be rebound and reused.</p>         *         * <p>If a LayoutManager uses this method to detach a view, it <em>must</em>         * {@link #attachView(android.view.View, int, RecyclerView.LayoutParams) reattach}         * or {@link #removeDetachedView(android.view.View) fully remove} the detached view         * before the LayoutManager entry point method called by RecyclerView returns.</p>         *         * @param index Index of the child to detach         */        public void detachViewAt(int index) {            if (DISPATCH_TEMP_DETACH) {                ViewCompat.dispatchStartTemporaryDetach(mRecyclerView.getChildAt(index));            }            mRecyclerView.detachViewFromParent(index);            if (mRecyclerView.mAnimatingViewIndex >= 0) {                --mRecyclerView.mAnimatingViewIndex;            }        }        /**         * Reattach a previously {@link #detachView(android.view.View) detached} view.         * This method should not be used to reattach views that were previously         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.         *         * @param child Child to reattach         * @param index Intended child index for child         * @param lp LayoutParams for child         */        public void attachView(View child, int index, LayoutParams lp) {            mRecyclerView.attachViewToParent(child, index, lp);            if (mRecyclerView.mAnimatingViewIndex >= 0) {                ++mRecyclerView.mAnimatingViewIndex;            }            if (DISPATCH_TEMP_DETACH)  {                ViewCompat.dispatchFinishTemporaryDetach(child);            }        }        /**         * Reattach a previously {@link #detachView(android.view.View) detached} view.         * This method should not be used to reattach views that were previously         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.         *         * @param child Child to reattach         * @param index Intended child index for child         */        public void attachView(View child, int index) {            attachView(child, index, (LayoutParams) child.getLayoutParams());        }        /**         * Reattach a previously {@link #detachView(android.view.View) detached} view.         * This method should not be used to reattach views that were previously         * {@link #detachAndScrapView(android.view.View, RecyclerView.Recycler)}  scrapped}.         *         * @param child Child to reattach         */        public void attachView(View child) {            attachView(child, -1);        }        /**         * Finish removing a view that was previously temporarily         * {@link #detachView(android.view.View) detached}.         *         * @param child Detached child to remove         */        public void removeDetachedView(View child) {            mRecyclerView.removeDetachedView(child, false);        }        /**         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.         *         * <p>Scrapping a view allows it to be rebound and reused to show updated or         * different data.</p>         *         * @param child Child to detach and scrap         * @param recycler Recycler to deposit the new scrap view into         */        public void detachAndScrapView(View child, Recycler recycler) {            detachView(child);            recycler.scrapView(child);        }        /**         * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.         *         * <p>Scrapping a view allows it to be rebound and reused to show updated or         * different data.</p>         *         * @param index Index of child to detach and scrap         * @param recycler Recycler to deposit the new scrap view into         */        public void detachAndScrapViewAt(int index, Recycler recycler) {            final View child = getChildAt(index);            detachViewAt(index);            recycler.scrapView(child);        }        /**         * Remove a child view and recycle it using the given Recycler.         *         * @param child Child to remove and recycle         * @param recycler Recycler to use to recycle child         */        public void removeAndRecycleView(View child, Recycler recycler) {            removeView(child);            recycler.recycleView(child);        }        /**         * Remove a child view and recycle it using the given Recycler.         *         * @param index Index of child to remove and recycle         * @param recycler Recycler to use to recycle child         */        public void removeAndRecycleViewAt(int index, Recycler recycler) {            final View view = getChildAt(index);            removeViewAt(index);            recycler.recycleView(view);        }        /**         * Return the current number of child views attached to the parent RecyclerView.         * This does not include child views that were temporarily detached and/or scrapped.         *         * @return Number of attached children         */        public int getChildCount() {            return mRecyclerView != null ?                    mRecyclerView.getChildCount() - mRecyclerView.mNumAnimatingViews : 0;        }        /**         * Return the child view at the given index         * @param index Index of child to return         * @return Child view at index         */        public View getChildAt(int index) {            return mRecyclerView != null ? mRecyclerView.getChildAt(index) : null;        }        /**         * Return the width of the parent RecyclerView         *         * @return Width in pixels         */        public int getWidth() {            return mRecyclerView != null ? mRecyclerView.getWidth() : 0;        }        /**         * Return the height of the parent RecyclerView         *         * @return Height in pixels         */        public int getHeight() {            return mRecyclerView != null ? mRecyclerView.getHeight() : 0;        }        /**         * Return the left padding of the parent RecyclerView         *         * @return Padding in pixels         */        public int getPaddingLeft() {            return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;        }        /**         * Return the top padding of the parent RecyclerView         *         * @return Padding in pixels         */        public int getPaddingTop() {            return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;        }        /**         * Return the right padding of the parent RecyclerView         *         * @return Padding in pixels         */        public int getPaddingRight() {            return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;        }        /**         * Return the bottom padding of the parent RecyclerView         *         * @return Padding in pixels         */        public int getPaddingBottom() {            return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;        }        /**         * Return the start padding of the parent RecyclerView         *         * @return Padding in pixels         */        public int getPaddingStart() {            return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;        }        /**         * Return the end padding of the parent RecyclerView         *         * @return Padding in pixels         */        public int getPaddingEnd() {            return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;        }        /**         * Returns true if the RecyclerView this LayoutManager is bound to has focus.         *         * @return True if the RecyclerView has focus, false otherwise.         * @see View#isFocused()         */        public boolean isFocused() {            return mRecyclerView != null && mRecyclerView.isFocused();        }        /**         * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.         *         * @return true if the RecyclerView has or contains focus         * @see View#hasFocus()         */        public boolean hasFocus() {            return mRecyclerView != null && mRecyclerView.hasFocus();        }        /**         * Return the number of items in the adapter bound to the parent RecyclerView         *         * @return Items in the bound adapter         */        public int getItemCount() {            final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;            return a != null ? a.getItemCount() : 0;        }        /**         * Offset all child views attached to the parent RecyclerView by dx pixels along         * the horizontal axis.         *         * @param dx Pixels to offset by         */        public void offsetChildrenHorizontal(int dx) {            if (mRecyclerView != null) {                mRecyclerView.offsetChildrenHorizontal(dx);            }        }        /**         * Offset all child views attached to the parent RecyclerView by dy pixels along         * the vertical axis.         *         * @param dy Pixels to offset by         */        public void offsetChildrenVertical(int dy) {            if (mRecyclerView != null) {                mRecyclerView.offsetChildrenVertical(dy);            }        }        /**         * Temporarily detach and scrap all currently attached child views. Views will be scrapped         * into the given Recycler. The Recycler may prefer to reuse scrap views before         * other views that were previously recycled.         *         * @param recycler Recycler to scrap views into         */        public void detachAndScrapAttachedViews(Recycler recycler) {            final int childCount = getChildCount();            for (int i = childCount - 1; i >= 0; i--) {                final View v = getChildAt(i);                detachViewAt(i);                recycler.scrapView(v);            }        }        /**         * Recycles the scrapped views.         * <p>         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is         * the expected behavior if scrapped views are used for animations. Otherwise, we need to         * call remove and invalidate RecyclerView to ensure UI update.         *         * @param recycler Recycler         * @param remove   Whether scrapped views should be removed from ViewGroup or not. This         *                 method will invalidate RecyclerView if it removes any scrapped child.         */        void removeAndRecycleScrapInt(Recycler recycler, boolean remove) {            final int scrapCount = recycler.getScrapCount();            for (int i = 0; i < scrapCount; i++) {                final View scrap = recycler.getScrapViewAt(i);                if (remove) {                    mRecyclerView.removeDetachedView(scrap, false);                }                recycler.quickRecycleScrapView(scrap);            }            recycler.clearScrap();            if (remove && scrapCount > 0) {                mRecyclerView.invalidate();            }        }        /**         * Measure a child view using standard measurement policy, taking the padding         * of the parent RecyclerView and any added item decorations into account.         *         * <p>If the RecyclerView can be scrolled in either dimension the caller may         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>         *         * @param child Child view to measure         * @param widthUsed Width in pixels currently consumed by other views, if relevant         * @param heightUsed Height in pixels currently consumed by other views, if relevant         */        public void measureChild(View child, int widthUsed, int heightUsed) {            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);            widthUsed += insets.left + insets.right;            heightUsed += insets.top + insets.bottom;            final int widthSpec = getChildMeasureSpec(getWidth(),                    getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,                    canScrollHorizontally());            final int heightSpec = getChildMeasureSpec(getHeight(),                    getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,                    canScrollVertically());            child.measure(widthSpec, heightSpec);        }        /**         * Measure a child view using standard measurement policy, taking the padding         * of the parent RecyclerView, any added item decorations and the child margins         * into account.         *         * <p>If the RecyclerView can be scrolled in either dimension the caller may         * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.</p>         *         * @param child Child view to measure         * @param widthUsed Width in pixels currently consumed by other views, if relevant         * @param heightUsed Height in pixels currently consumed by other views, if relevant         */        public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {            final LayoutParams lp = (LayoutParams) child.getLayoutParams();            final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);            widthUsed += insets.left + insets.right;            heightUsed += insets.top + insets.bottom;            final int widthSpec = getChildMeasureSpec(getWidth(),                    getPaddingLeft() + getPaddingRight() +                            lp.leftMargin + lp.rightMargin + widthUsed, lp.width,                    canScrollHorizontally());            final int heightSpec = getChildMeasureSpec(getHeight(),                    getPaddingTop() + getPaddingBottom() +                            lp.topMargin + lp.bottomMargin + heightUsed, lp.height,                    canScrollVertically());            child.measure(widthSpec, heightSpec);        }        /**         * Calculate a MeasureSpec value for measuring a child view in one dimension.         *         * @param parentSize Size of the parent view where the child will be placed         * @param padding Total space currently consumed by other elements of parent         * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.         *                       Generally obtained from the child view's LayoutParams         * @param canScroll true if the parent RecyclerView can scroll in this dimension         *         * @return a MeasureSpec value for the child view         */        public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,                boolean canScroll) {            int size = Math.max(0, parentSize - padding);            int resultSize = 0;            int resultMode = 0;            if (canScroll) {                if (childDimension >= 0) {                    resultSize = childDimension;                    resultMode = MeasureSpec.EXACTLY;                } else {                    // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap                    // instead using UNSPECIFIED.                    resultSize = 0;                    resultMode = MeasureSpec.UNSPECIFIED;                }            } else {                if (childDimension >= 0) {                    resultSize = childDimension;                    resultMode = MeasureSpec.EXACTLY;                } else if (childDimension == LayoutParams.FILL_PARENT) {                    resultSize = size;                    resultMode = MeasureSpec.EXACTLY;                } else if (childDimension == LayoutParams.WRAP_CONTENT) {                    resultSize = size;                    resultMode = MeasureSpec.AT_MOST;                }            }            return MeasureSpec.makeMeasureSpec(resultSize, resultMode);        }        /**         * Returns the measured width of the given child, plus the additional size of         * any insets applied by {@link ItemDecoration ItemDecorations}.         *         * @param child Child view to query         * @return child's measured width plus <code>ItemDecoration</code> insets         *         * @see View#getMeasuredWidth()         */        public int getDecoratedMeasuredWidth(View child) {            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;            return child.getMeasuredWidth() + insets.left + insets.right;        }        /**         * Returns the measured height of the given child, plus the additional size of         * any insets applied by {@link ItemDecoration ItemDecorations}.         *         * @param child Child view to query         * @return child's measured height plus <code>ItemDecoration</code> insets         *         * @see View#getMeasuredHeight()         */        public int getDecoratedMeasuredHeight(View child) {            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;            return child.getMeasuredHeight() + insets.top + insets.bottom;        }        /**         * Lay out the given child view within the RecyclerView using coordinates that         * include any current {@link ItemDecoration ItemDecorations}.         *         * <p>LayoutManagers should prefer working in sizes and coordinates that include         * item decoration insets whenever possible. This allows the LayoutManager to effectively         * ignore decoration insets within measurement and layout code. See the following         * methods:</p>         * <ul>         *     <li>{@link #measureChild(View, int, int)}</li>         *     <li>{@link #measureChildWithMargins(View, int, int)}</li>         *     <li>{@link #getDecoratedLeft(View)}</li>         *     <li>{@link #getDecoratedTop(View)}</li>         *     <li>{@link #getDecoratedRight(View)}</li>         *     <li>{@link #getDecoratedBottom(View)}</li>         *     <li>{@link #getDecoratedMeasuredWidth(View)}</li>         *     <li>{@link #getDecoratedMeasuredHeight(View)}</li>         * </ul>         *         * @param child Child to lay out         * @param left Left edge, with item decoration insets included         * @param top Top edge, with item decoration insets included         * @param right Right edge, with item decoration insets included         * @param bottom Bottom edge, with item decoration insets included         *         * @see View#layout(int, int, int, int)         */        public void layoutDecorated(View child, int left, int top, int right, int bottom) {            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;            child.layout(left + insets.left, top + insets.top, right - insets.right,                    bottom - insets.bottom);        }        /**         * Returns the left edge of the given child view within its parent, offset by any applied         * {@link ItemDecoration ItemDecorations}.         *         * @param child Child to query         * @return Child left edge with offsets applied         */        public int getDecoratedLeft(View child) {            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;            return child.getLeft() - insets.left;        }        /**         * Returns the top edge of the given child view within its parent, offset by any applied         * {@link ItemDecoration ItemDecorations}.         *         * @param child Child to query         * @return Child top edge with offsets applied         */        public int getDecoratedTop(View child) {            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;            return child.getTop() - insets.top;        }        /**         * Returns the right edge of the given child view within its parent, offset by any applied         * {@link ItemDecoration ItemDecorations}.         *         * @param child Child to query         * @return Child right edge with offsets applied         */        public int getDecoratedRight(View child) {            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;            return child.getRight() + insets.right;        }        /**         * Returns the bottom edge of the given child view within its parent, offset by any applied         * {@link ItemDecoration ItemDecorations}.         *         * @param child Child to query         * @return Child bottom edge with offsets applied         */        public int getDecoratedBottom(View child) {            final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;            return child.getBottom() + insets.bottom;        }        /**         * Called when searching for a focusable view in the given direction has failed         * for the current content of the RecyclerView.         *         * <p>This is the LayoutManager's opportunity to populate views in the given direction         * to fulfill the request if it can. The LayoutManager should attach and return         * the view to be focused. The default implementation returns null.</p>         *         * @param focused   The currently focused view         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}         *                  or 0 for not applicable         * @param recycler  The recycler to use for obtaining views for currently offscreen items         * @param state     Transient state of RecyclerView         * @return The chosen view to be focused         */        public View onFocusSearchFailed(View focused, int direction, Recycler recycler,                State state) {            return null;        }        /**         * This method gives a LayoutManager an opportunity to intercept the initial focus search         * before the default behavior of {@link FocusFinder} is used. If this method returns         * null FocusFinder will attempt to find a focusable child view. If it fails         * then {@link #onFocusSearchFailed(View, int, RecyclerView.Recycler, RecyclerView.State)}         * will be called to give the LayoutManager an opportunity to add new views for items         * that did not have attached views representing them. The LayoutManager should not add         * or remove views from this method.         *         * @param focused The currently focused view         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}         * @return A descendant view to focus or null to fall back to default behavior.         *         The default implementation returns null.         */        public View onInterceptFocusSearch(View focused, int direction) {            return null;        }        /**         * Called when a child of the RecyclerView wants a particular rectangle to be positioned         * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(android.view.View,         * android.graphics.Rect, boolean)} for more details.         *         * <p>The base implementation will attempt to perform a standard programmatic scroll         * to bring the given rect into view, within the padded area of the RecyclerView.</p>         *         * @param child The direct child making the request.         * @param rect  The rectangle in the child's coordinates the child         *              wishes to be on the screen.         * @param immediate True to forbid animated or delayed scrolling,         *                  false otherwise         * @return Whether the group scrolled to handle the operation         */        public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,                boolean immediate) {            final int parentLeft = getPaddingLeft();            final int parentTop = getPaddingTop();            final int parentRight = getWidth() - getPaddingRight();            final int parentBottom = getHeight() - getPaddingBottom();            final int childLeft = child.getLeft() + rect.left;            final int childTop = child.getTop() + rect.top;            final int childRight = childLeft + rect.right;            final int childBottom = childTop + rect.bottom;            final int offScreenLeft = Math.min(0, childLeft - parentLeft);            final int offScreenTop = Math.min(0, childTop - parentTop);            final int offScreenRight = Math.max(0, childRight - parentRight);            final int offScreenBottom = Math.max(0, childBottom - parentBottom);            // Favor the "start" layout direction over the end when bringing one side or the other            // of a large rect into view.            final int dx;            if (ViewCompat.getLayoutDirection(parent) == ViewCompat.LAYOUT_DIRECTION_RTL) {                dx = offScreenRight != 0 ? offScreenRight : offScreenLeft;            } else {                dx = offScreenLeft != 0 ? offScreenLeft : offScreenRight;            }            // Favor bringing the top into view over the bottom            final int dy = offScreenTop != 0 ? offScreenTop : offScreenBottom;            if (dx != 0 || dy != 0) {                if (immediate) {                    parent.scrollBy(dx, dy);                } else {                    parent.smoothScrollBy(dx, dy);                }                return true;            }            return false;        }        /**         * Called when a descendant view of the RecyclerView requests focus.         *         * <p>A LayoutManager wishing to keep focused views aligned in a specific         * portion of the view may implement that behavior in an override of this method.</p>         *         * <p>If the LayoutManager executes different behavior that should override the default         * behavior of scrolling the focused child on screen instead of running alongside it,         * this method should return true.</p>         *         * @param parent The RecyclerView hosting this LayoutManager         * @param child Direct child of the RecyclerView containing the newly focused view         * @param focused The newly focused view. This may be the same view as child         * @return true if the default scroll behavior should be suppressed         */        public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {            return false;        }        /**         * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.         * The LayoutManager may use this opportunity to clear caches and configure state such         * that it can relayout appropriately with the new data and potentially new view types.         *         * <p>The default implementation removes all currently attached views.</p>         *         * @param oldAdapter The previous adapter instance. Will be null if there was previously no         *                   adapter.         * @param newAdapter The new adapter instance. Might be null if         *                   {@link #setAdapter(RecyclerView.Adapter)} is called with {@code null}.         */        public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {        }        /**         * Called to populate focusable views within the RecyclerView.         *         * <p>The LayoutManager implementation should return <code>true</code> if the default         * behavior of {@link ViewGroup#addFocusables(java.util.ArrayList, int)} should be         * suppressed.</p>         *         * <p>The default implementation returns <code>false</code> to trigger RecyclerView         * to fall back to the default ViewGroup behavior.</p>         *         * @param recyclerView The RecyclerView hosting this LayoutManager         * @param views List of output views. This method should add valid focusable views         *              to this list.         * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},         *                  {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},         *                  {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}         * @param focusableMode The type of focusables to be added.         *         * @return true to suppress the default behavior, false to add default focusables after         *         this method returns.         *         * @see #FOCUSABLES_ALL         * @see #FOCUSABLES_TOUCH_MODE         */        public boolean onAddFocusables(RecyclerView recyclerView, ArrayList<View> views,                int direction, int focusableMode) {            return false;        }        /**         * Called when items have been added to the adapter. The LayoutManager may choose to         * requestLayout if the inserted items would require refreshing the currently visible set         * of child views. (e.g. currently empty space would be filled by appended items, etc.)         *         * @param recyclerView         * @param positionStart         * @param itemCount         */        public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {        }        /**         * Called when items have been removed from the adapter.         *         * @param recyclerView         * @param positionStart         * @param itemCount         */        public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {        }        /**         * <p>Override this method if you want to support scroll bars.</p>         *         * <p>Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.</p>         *         * <p>Default implementation returns 0.</p>         *         * @param state Current state of RecyclerView         * @return The horizontal extent of the scrollbar's thumb         * @see RecyclerView#computeHorizontalScrollExtent()         */        public int computeHorizontalScrollExtent(State state) {            return 0;        }        /**         * <p>Override this method if you want to support scroll bars.</p>         *         * <p>Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.</p>         *         * <p>Default implementation returns 0.</p>         *         * @param state Current State of RecyclerView where you can find total item count         * @return The horizontal offset of the scrollbar's thumb         * @see RecyclerView#computeHorizontalScrollOffset()         */        public int computeHorizontalScrollOffset(State state) {            return 0;        }        /**         * <p>Override this method if you want to support scroll bars.</p>         *         * <p>Read {@link RecyclerView#computeHorizontalScrollRange()} for details.</p>         *         * <p>Default implementation returns 0.</p>         *         * @param state Current State of RecyclerView where you can find total item count         * @return The total horizontal range represented by the vertical scrollbar         * @see RecyclerView#computeHorizontalScrollRange()         */        public int computeHorizontalScrollRange(State state) {            return 0;        }        /**         * <p>Override this method if you want to support scroll bars.</p>         *         * <p>Read {@link RecyclerView#computeVerticalScrollExtent()} for details.</p>         *         * <p>Default implementation returns 0.</p>         *         * @param state Current state of RecyclerView         * @return The vertical extent of the scrollbar's thumb         * @see RecyclerView#computeVerticalScrollExtent()         */        public int computeVerticalScrollExtent(State state) {            return 0;        }        /**         * <p>Override this method if you want to support scroll bars.</p>         *         * <p>Read {@link RecyclerView#computeVerticalScrollOffset()} for details.</p>         *         * <p>Default implementation returns 0.</p>         *         * @param state Current State of RecyclerView where you can find total item count         * @return The vertical offset of the scrollbar's thumb         * @see RecyclerView#computeVerticalScrollOffset()         */        public int computeVerticalScrollOffset(State state) {            return 0;        }        /**         * <p>Override this method if you want to support scroll bars.</p>         *         * <p>Read {@link RecyclerView#computeVerticalScrollRange()} for details.</p>         *         * <p>Default implementation returns 0.</p>         *         * @param state Current State of RecyclerView where you can find total item count         * @return The total vertical range represented by the vertical scrollbar         * @see RecyclerView#computeVerticalScrollRange()         */        public int computeVerticalScrollRange(State state) {            return 0;        }        /**         * Measure the attached RecyclerView. Implementations must call         * {@link #setMeasuredDimension(int, int)} before returning.         *         * <p>The default implementation will handle EXACTLY measurements and respect         * the minimum width and height properties of the host RecyclerView if measured         * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView         * will consume all available space.</p>         *         * @param recycler Recycler         * @param state Transient state of RecyclerView         * @param widthSpec Width {@link android.view.View.MeasureSpec}         * @param heightSpec Height {@link android.view.View.MeasureSpec}         */        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {            final int widthMode = MeasureSpec.getMode(widthSpec);            final int heightMode = MeasureSpec.getMode(heightSpec);            final int widthSize = MeasureSpec.getSize(widthSpec);            final int heightSize = MeasureSpec.getSize(heightSpec);            int width = 0;            int height = 0;            switch (widthMode) {                case MeasureSpec.EXACTLY:                case MeasureSpec.AT_MOST:                    width = widthSize;                    break;                case MeasureSpec.UNSPECIFIED:                default:                    width = getMinimumWidth();                    break;            }            switch (heightMode) {                case MeasureSpec.EXACTLY:                case MeasureSpec.AT_MOST:                    height = heightSize;                    break;                case MeasureSpec.UNSPECIFIED:                default:                    height = getMinimumHeight();                    break;            }            setMeasuredDimension(width, height);        }        /**         * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the         * host RecyclerView.         *         * @param widthSize Measured width         * @param heightSize Measured height         */        public void setMeasuredDimension(int widthSize, int heightSize) {            mRecyclerView.setMeasuredDimension(widthSize, heightSize);        }        /**         * @return The host RecyclerView's {@link View#getMinimumWidth()}         */        public int getMinimumWidth() {            return ViewCompat.getMinimumWidth(mRecyclerView);        }        /**         * @return The host RecyclerView's {@link View#getMinimumHeight()}         */        public int getMinimumHeight() {            return ViewCompat.getMinimumHeight(mRecyclerView);        }        /**         * <p>Called when the LayoutManager should save its state. This is a good time to save your         * scroll position, configuration and anything else that may be required to restore the same         * layout state if the LayoutManager is recreated.</p>         * <p>RecyclerView does NOT verify if the LayoutManager has changed between state save and         * restore. This will let you share information between your LayoutManagers but it is also         * your responsibility to make sure they use the same parcelable class.</p>         *         * @return Necessary information for LayoutManager to be able to restore its state         */        public Parcelable onSaveInstanceState() {            return null;        }        public void onRestoreInstanceState(Parcelable state) {        }        void stopSmoothScroller() {            if (mSmoothScroller != null) {                mSmoothScroller.stop();            }        }        private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {            if (mSmoothScroller == smoothScroller) {                mSmoothScroller = null;            }        }        void removeAndRecycleAllViews(Recycler recycler) {            for (int i = getChildCount() - 1; i >= 0; i--) {                removeAndRecycleViewAt(i, recycler);            }        }    }    /**     * An ItemDecoration allows the application to add a special drawing and layout offset     * to specific item views from the adapter's data set. This can be useful for drawing dividers     * between items, highlights, visual grouping boundaries and more.     *     * <p>All ItemDecorations are drawn in the order they were added, before the item     * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView) onDraw()} and after the items     * (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView)}.</p>     */    public static abstract class ItemDecoration {        /**         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.         * Any content drawn by this method will be drawn before the item views are drawn,         * and will thus appear underneath the views.         *         * @param c Canvas to draw into         * @param parent RecyclerView this ItemDecoration is drawing into         */        public void onDraw(Canvas c, RecyclerView parent) {        }        /**         * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.         * Any content drawn by this method will be drawn after the item views are drawn         * and will thus appear over the views.         *         * @param c Canvas to draw into         * @param parent RecyclerView this ItemDecoration is drawing into         */        public void onDrawOver(Canvas c, RecyclerView parent) {        }        /**         * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies         * the number of pixels that the item view should be inset by, similar to padding or margin.         * The default implementation sets the bounds of outRect to 0 and returns.         *         * <p>If this ItemDecoration does not affect the positioning of item views it should set         * all four fields of <code>outRect</code> (left, top, right, bottom) to zero         * before returning.</p>         *         * @param outRect Rect to receive the output.         * @param itemPosition Adapter position of the item to offset         * @param parent RecyclerView this ItemDecoration is decorating         */        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {            outRect.set(0, 0, 0, 0);        }    }    /**     * An OnItemTouchListener allows the application to intercept touch events in progress at the     * view hierarchy level of the RecyclerView before those touch events are considered for     * RecyclerView's own scrolling behavior.     *     * <p>This can be useful for applications that wish to implement various forms of gestural     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept     * a touch interaction already in progress even if the RecyclerView is already handling that     * gesture stream itself for the purposes of scrolling.</p>     */    public interface OnItemTouchListener {        /**         * Silently observe and/or take over touch events sent to the RecyclerView         * before they are handled by either the RecyclerView itself or its child views.         *         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run         * in the order in which each listener was added, before any other touch processing         * by the RecyclerView itself or child views occurs.</p>         *         * @param e MotionEvent describing the touch event. All coordinates are in         *          the RecyclerView's coordinate system.         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false         *         to continue with the current behavior and continue observing future events in         *         the gesture.         */        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);        /**         * Process a touch event as part of a gesture that was claimed by returning true from         * a previous call to {@link #onInterceptTouchEvent}.         *         * @param e MotionEvent describing the touch event. All coordinates are in         *          the RecyclerView's coordinate system.         */        public void onTouchEvent(RecyclerView rv, MotionEvent e);    }    /**     * An OnScrollListener can be set on a RecyclerView to receive messages     * when a scrolling event has occurred on that RecyclerView.     *     * @see RecyclerView#setOnScrollListener(OnScrollListener)     */    public interface OnScrollListener {        public void onScrollStateChanged(int newState);        public void onScrolled(int dx, int dy);    }    /**     * A RecyclerListener can be set on a RecyclerView to receive messages whenever     * a view is recycled.     *     * @see RecyclerView#setRecyclerListener(RecyclerListener)     */    public interface RecyclerListener {        /**         * This method is called whenever the view in the ViewHolder is recycled.         *         * @param holder The ViewHolder containing the view that was recycled         */        public void onViewRecycled(ViewHolder holder);    }        /**     * Interface definition for a callback to be invoked when an item in this     * RecyclerView.Adapter has been clicked.     */    public interface OnItemClickListener {        /**         * Callback method to be invoked when an item in this RecyclerView.Adapter has         * been clicked.         * <p>         * Implementers can call getPosition(position) if they need         * to access the data associated with the selected item.         *         * @param view The view within the RecyclerView.Adapter that was clicked (this         *            will be a view provided by the adapter)         * @param position The position of the view in the adapter.         */        void onItemClick(View view, int position);    }        public static OnItemClickListener mOnItemClickListener = null;        /**     * Register a callback to be invoked when an item in this AdapterView has     * been clicked.     *     * @param listener The callback that will be invoked.     */    public void setOnItemClickListener(OnItemClickListener listener) {        mOnItemClickListener = listener;    }    /**     * @return The callback to be invoked with an item in this AdapterView has     *         been clicked, or null id no callback has been set.     */    public final OnItemClickListener getOnItemClickListener() {        return mOnItemClickListener;    }    /**     * A ViewHolder describes an item view and metadata about its place within the RecyclerView.     *     * <p>{@link Adapter} implementations should subclass ViewHolder and add fields for caching     * potentially expensive {@link View#findViewById(int)} results.</p>     *     * <p>While {@link LayoutParams} belong to the {@link LayoutManager},     * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use     * their own custom ViewHolder implementations to store data that makes binding view contents     * easier. Implementations should assume that individual item views will hold strong references     * to <code>ViewHolder</code> objects and that <code>RecyclerView</code> instances may hold     * strong references to extra off-screen item views for caching purposes</p>     */    public static abstract class ViewHolder implements OnClickListener{        public final View itemView;        int mPosition = NO_POSITION;        int mOldPosition = NO_POSITION;        long mItemId = NO_ID;        int mItemViewType = INVALID_TYPE;        /**         * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType         * are all valid.         */        static final int FLAG_BOUND = 1 << 0;        /**         * The data this ViewHolder's view reflects is stale and needs to be rebound         * by the adapter. mPosition and mItemId are consistent.         */        static final int FLAG_UPDATE = 1 << 1;        /**         * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId         * are not to be trusted and may no longer match the item view type.         * This ViewHolder must be fully rebound to different data.         */        static final int FLAG_INVALID = 1 << 2;        /**         * This ViewHolder points at data that represents an item previously removed from the         * data set. Its view may still be used for things like outgoing animations.         */        static final int FLAG_REMOVED = 1 << 3;        /**         * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()         * and is intended to keep views around during animations.         */        static final int FLAG_NOT_RECYCLABLE = 1 << 4;        private int mFlags;        private int mIsRecyclableCount = 0;        // If non-null, view is currently considered scrap and may be reused for other data by the        // scrap container.        private Recycler mScrapContainer = null;                @Overridepublic void onClick(View v) {        if (mOnItemClickListener != null) {        mOnItemClickListener.onItemClick(itemView, getPosition());}}public ViewHolder(View itemView) {            if (itemView == null) {                throw new IllegalArgumentException("itemView may not be null");            }            this.itemView = itemView;            this.itemView.setOnClickListener(this);        }        void offsetPosition(int offset) {            if (mOldPosition == NO_POSITION) {                mOldPosition = mPosition;            }            mPosition += offset;        }        void clearOldPosition() {            mOldPosition = NO_POSITION;        }        public final int getPosition() {            return mOldPosition == NO_POSITION ? mPosition : mOldPosition;        }        public final long getItemId() {            return mItemId;        }        public final int getItemViewType() {            return mItemViewType;        }        boolean isScrap() {            return mScrapContainer != null;        }        void unScrap() {            mScrapContainer.unscrapView(this);            mScrapContainer = null;        }        void setScrapContainer(Recycler recycler) {            mScrapContainer = recycler;        }        boolean isInvalid() {            return (mFlags & FLAG_INVALID) != 0;        }        boolean needsUpdate() {            return (mFlags & FLAG_UPDATE) != 0;        }        boolean isBound() {            return (mFlags & FLAG_BOUND) != 0;        }        boolean isRemoved() {            return (mFlags & FLAG_REMOVED) != 0;        }        void setFlags(int flags, int mask) {            mFlags = (mFlags & ~mask) | (flags & mask);        }        void addFlags(int flags) {            mFlags |= flags;        }        void clearFlagsForSharedPool() {            mFlags = 0;        }        @Override        public String toString() {            final StringBuilder sb = new StringBuilder("ViewHolder{" +                    Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId);            if (isScrap()) sb.append(" scrap");            if (isInvalid()) sb.append(" invalid");            if (!isBound()) sb.append(" unbound");            if (needsUpdate()) sb.append(" update");            if (isRemoved()) sb.append(" removed");            sb.append("}");            return sb.toString();        }        /**         * Informs the recycler whether this item can be recycled. Views which are not         * recyclable will not be reused for other items until setIsRecyclable() is         * later set to true. Calls to setIsRecyclable() should always be paired (one         * call to setIsRecyclabe(false) should always be matched with a later call to         * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally         * reference-counted.         *         * @param recyclable Whether this item is available to be recycled. Default value         * is true.         */        public final void setIsRecyclable(boolean recyclable) {            mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;            if (mIsRecyclableCount < 0) {                mIsRecyclableCount = 0;                Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +                        "unmatched pair of setIsRecyable() calls");            } else if (!recyclable && mIsRecyclableCount == 1) {                mFlags |= FLAG_NOT_RECYCLABLE;            } else if (recyclable && mIsRecyclableCount == 0) {                mFlags &= ~FLAG_NOT_RECYCLABLE;            }        }        /**         * @see {@link #setIsRecyclable(boolean)}         *         * @return true if this item is available to be recycled, false otherwise.         */        public final boolean isRecyclable() {            return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&                    !ViewCompat.hasTransientState(itemView);        }    }    /**     * Queued operation to happen when child views are updated.     */    private static class UpdateOp {        public static final int ADD = 0;        public static final int REMOVE = 1;        public static final int UPDATE = 2;        static final int POOL_SIZE = 30;        public int cmd;        public int positionStart;        public int itemCount;        public UpdateOp(int cmd, int positionStart, int itemCount) {            this.cmd = cmd;            this.positionStart = positionStart;            this.itemCount = itemCount;        }    }    UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount) {        UpdateOp op = mUpdateOpPool.acquire();        if (op == null) {            op = new UpdateOp(cmd, positionStart, itemCount);        } else {            op.cmd = cmd;            op.positionStart = positionStart;            op.itemCount = itemCount;        }        return op;    }    void recycleUpdateOp(UpdateOp op) {        mUpdateOpPool.release(op);    }    /**     * {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of     * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged     * to create their own subclass of this <code>LayoutParams</code> class     * to store any additional required per-child view metadata about the layout.     */    public static class LayoutParams extends MarginLayoutParams {        ViewHolder mViewHolder;        final Rect mDecorInsets = new Rect();        boolean mInsetsDirty = true;        public LayoutParams(Context c, AttributeSet attrs) {            super(c, attrs);        }        public LayoutParams(int width, int height) {            super(width, height);        }        public LayoutParams(MarginLayoutParams source) {            super(source);        }        public LayoutParams(ViewGroup.LayoutParams source) {            super(source);        }        public LayoutParams(LayoutParams source) {            super((ViewGroup.LayoutParams) source);        }        /**         * Returns true if the view this LayoutParams is attached to needs to have its content         * updated from the corresponding adapter.         *         * @return true if the view should have its content updated         */        public boolean viewNeedsUpdate() {            return mViewHolder.needsUpdate();        }        /**         * Returns true if the view this LayoutParams is attached to is now representing         * potentially invalid data. A LayoutManager should scrap/recycle it.         *         * @return true if the view is invalid         */        public boolean isViewInvalid() {            return mViewHolder.isInvalid();        }        /**         * Returns true if the adapter data item corresponding to the view this LayoutParams         * is attached to has been removed from the data set. A LayoutManager may choose to         * treat it differently in order to animate its outgoing or disappearing state.         *         * @return true if the item the view corresponds to was removed from the data set         */        public boolean isItemRemoved() {            return mViewHolder.isRemoved();        }        /**         * Returns the position that the view this LayoutParams is attached to corresponds to.         *         * @return the adapter position this view was bound from         */        public int getViewPosition() {            return mViewHolder.getPosition();        }    }    /**     * Observer base class for watching changes to an {@link Adapter}.     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.     */    public static abstract class AdapterDataObserver {        public void onChanged() {            // Do nothing        }        public void onItemRangeChanged(int positionStart, int itemCount) {            // do nothing        }        public void onItemRangeInserted(int positionStart, int itemCount) {            // do nothing        }        public void onItemRangeRemoved(int positionStart, int itemCount) {            // do nothing        }    }    /**     * <p>Base class for smooth scrolling. Handles basic tracking of the target view position and     * provides methods to trigger a programmatic scroll.</p>     *     * @see LinearSmoothScroller     */    public static abstract class SmoothScroller {        private int mTargetPosition = RecyclerView.NO_POSITION;        private RecyclerView mRecyclerView;        private LayoutManager mLayoutManager;        private boolean mPendingInitialRun;        private boolean mRunning;        private View mTargetView;        private final Action mRecyclingAction;        public SmoothScroller() {            mRecyclingAction = new Action(0, 0);        }        /**         * Starts a smooth scroll for the given target position.         * <p>In each animation step, {@link RecyclerView} will check         * for the target view and call either         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)} until         * SmoothScroller is stopped.</p>         *         * <p>Note that if RecyclerView finds the target view, it will automatically stop the         * SmoothScroller. This <b>does not</b> mean that scroll will stop, it only means it will         * stop calling SmoothScroller in each animation step.</p>         */        void start(RecyclerView recyclerView, LayoutManager layoutManager) {            mRecyclerView = recyclerView;            mLayoutManager = layoutManager;            if (mTargetPosition == RecyclerView.NO_POSITION) {                throw new IllegalArgumentException("Invalid target position");            }            mRecyclerView.mState.mTargetPosition = mTargetPosition;            mRunning = true;            mPendingInitialRun = true;            mTargetView = findViewByPosition(getTargetPosition());            onStart();            mRecyclerView.mViewFlinger.postOnAnimation();        }        public void setTargetPosition(int targetPosition) {            mTargetPosition = targetPosition;        }        /**         * @return The LayoutManager to which this SmoothScroller is attached         */        public LayoutManager getLayoutManager() {            return mLayoutManager;        }        /**         * Stops running the SmoothScroller in each animation callback. Note that this does not         * cancel any existing {@link Action} updated by         * {@link #onTargetFound(android.view.View, RecyclerView.State, SmoothScroller.Action)} or         * {@link #onSeekTargetStep(int, int, RecyclerView.State, SmoothScroller.Action)}.         */        final protected void stop() {            if (!mRunning) {                return;            }            onStop();            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;            mTargetView = null;            mTargetPosition = RecyclerView.NO_POSITION;            mPendingInitialRun = false;            mRunning = false;            // trigger a cleanup            mLayoutManager.onSmoothScrollerStopped(this);            // clear references to avoid any potential leak by a custom smooth scroller            mLayoutManager = null;            mRecyclerView = null;        }        /**         * Returns true if SmoothScroller has beens started but has not received the first         * animation         * callback yet.         *         * @return True if this SmoothScroller is waiting to start         */        public boolean isPendingInitialRun() {            return mPendingInitialRun;        }        /**         * @return True if SmoothScroller is currently active         */        public boolean isRunning() {            return mRunning;        }        /**         * Returns the adapter position of the target item         *         * @return Adapter position of the target item or         * {@link RecyclerView#NO_POSITION} if no target view is set.         */        public int getTargetPosition() {            return mTargetPosition;        }        private void onAnimation(int dx, int dy) {            if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION) {                stop();            }            mPendingInitialRun = false;            if (mTargetView != null) {                // verify target position                if (getChildPosition(mTargetView) == mTargetPosition) {                    onTargetFound(mTargetView, mRecyclerView.mState, mRecyclingAction);                    mRecyclingAction.runInNecessary(mRecyclerView);                    stop();                } else {                    Log.e(TAG, "Passed over target position while smooth scrolling.");                    mTargetView = null;                }            }            if (mRunning) {                onSeekTargetStep(dx, dy, mRecyclerView.mState, mRecyclingAction);                mRecyclingAction.runInNecessary(mRecyclerView);            }        }        /**         * @see RecyclerView#getChildPosition(android.view.View)         */        public int getChildPosition(View view) {            return mRecyclerView.getChildPosition(view);        }        /**         * @see RecyclerView#getChildCount()         */        public int getChildCount() {            return mRecyclerView.getChildCount();        }        /**         * @see RecyclerView.LayoutManager#findViewByPosition(int)         */        public View findViewByPosition(int position) {            return mRecyclerView.mLayout.findViewByPosition(position);        }        /**         * @see RecyclerView#scrollToPosition(int)         */        public void instantScrollToPosition(int position) {            mRecyclerView.scrollToPosition(position);        }        protected void onChildAttachedToWindow(View child) {            if (getChildPosition(child) == getTargetPosition()) {                mTargetView = child;                if (DEBUG) {                    Log.d(TAG, "smooth scroll target view has been attached");                }            }        }        /**         * Normalizes the vector.         * @param scrollVector The vector that points to the target scroll position         */        protected void normalize(PointF scrollVector) {            final double magnitute = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y *                    scrollVector.y);            scrollVector.x /= magnitute;            scrollVector.y /= magnitute;        }        /**         * Called when smooth scroll is started. This might be a good time to do setup.         */        abstract protected void onStart();        /**         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.         * @see #stop()         */        abstract protected void onStop();        /**         * <p>RecyclerView will call this method each time it scrolls until it can find the target         * position in the layout.</p>         * <p>SmoothScroller should check dx, dy and if scroll should be changed, update the         * provided {@link Action} to define the next scroll.</p>         *         * @param dx        Last scroll amount horizontally         * @param dy        Last scroll amount verticaully         * @param state     Transient state of RecyclerView         * @param action    If you want to trigger a new smooth scroll and cancel the previous one,         *                  update this object.         */        abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);        /**         * Called when the target position is laid out. This is the last callback SmoothScroller         * will receive and it should update the provided {@link Action} to define the scroll         * details towards the target view.         * @param targetView    The view element which render the target position.         * @param state         Transient state of RecyclerView         * @param action        Action instance that you should update to define final scroll action         *                      towards the targetView         * @return An {@link Action} to finalize the smooth scrolling         */        abstract protected void onTargetFound(View targetView, State state, Action action);        /**         * Holds information about a smooth scroll request by a {@link SmoothScroller}.         */        public static class Action {            public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;            private int mDx;            private int mDy;            private int mDuration;            private Interpolator mInterpolator;            private boolean changed = false;            // we track this variable to inform custom implementer if they are updating the action            // in every animation callback            private int consecutiveUpdates = 0;            /**             * @param dx Pixels to scroll horizontally             * @param dy Pixels to scroll vertically             */            public Action(int dx, int dy) {                this(dx, dy, UNDEFINED_DURATION, null);            }            /**             * @param dx       Pixels to scroll horizontally             * @param dy       Pixels to scroll vertically             * @param duration Duration of the animation in milliseconds             */            public Action(int dx, int dy, int duration) {                this(dx, dy, duration, null);            }            /**             * @param dx           Pixels to scroll horizontally             * @param dy           Pixels to scroll vertically             * @param duration     Duration of the animation in milliseconds             * @param interpolator Interpolator to be used when calculating scroll position in each             *                     animation step             */            public Action(int dx, int dy, int duration, Interpolator interpolator) {                mDx = dx;                mDy = dy;                mDuration = duration;                mInterpolator = interpolator;            }            private void runInNecessary(RecyclerView recyclerView) {                if (changed) {                    validate();                    if (mInterpolator == null) {                        if (mDuration == UNDEFINED_DURATION) {                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);                        } else {                            recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);                        }                    } else {                        recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);                    }                    consecutiveUpdates ++;                    if (consecutiveUpdates > 10) {                        // A new action is being set in every animation step. This looks like a bad                        // implementation. Inform developer.                        Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"                                + " you are not changing it unless necessary");                    }                    changed = false;                } else {                    consecutiveUpdates = 0;                }            }            private void validate() {                if (mInterpolator != null && mDuration < 1) {                    throw new IllegalStateException("If you provide an interpolator, you must"                            + " set a positive duration");                } else if (mDuration < 1) {                    throw new IllegalStateException("Scroll duration must be a positive number");                }            }            public int getDx() {                return mDx;            }            public void setDx(int dx) {                changed = true;                mDx = dx;            }            public int getDy() {                return mDy;            }            public void setDy(int dy) {                changed = true;                mDy = dy;            }            public int getDuration() {                return mDuration;            }            public void setDuration(int duration) {                changed = true;                mDuration = duration;            }            public Interpolator getInterpolator() {                return mInterpolator;            }            /**             * Sets the interpolator to calculate scroll steps             * @param interpolator The interpolator to use. If you specify an interpolator, you must             *                     also set the duration.             * @see #setDuration(int)             */            public void setInterpolator(Interpolator interpolator) {                changed = true;                mInterpolator = interpolator;            }            /**             * Updates the action with given parameters.             * @param dx Pixels to scroll horizontally             * @param dy Pixels to scroll vertically             * @param duration Duration of the animation in milliseconds             * @param interpolator Interpolator to be used when calculating scroll position in each             *                     animation step             */            public void update(int dx, int dy, int duration, Interpolator interpolator) {                mDx = dx;                mDy = dy;                mDuration = duration;                mInterpolator = interpolator;                changed = true;            }        }    }    static class AdapterDataObservable extends Observable<AdapterDataObserver> {        public boolean hasObservers() {            return !mObservers.isEmpty();        }        public void notifyChanged() {            // since onChanged() is implemented by the app, it could do anything, including            // removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onChanged();            }        }        public void notifyItemRangeChanged(int positionStart, int itemCount) {            // since onItemRangeChanged() is implemented by the app, it could do anything, including            // removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onItemRangeChanged(positionStart, itemCount);            }        }        public void notifyItemRangeInserted(int positionStart, int itemCount) {            // since onItemRangeInserted() is implemented by the app, it could do anything,            // including removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onItemRangeInserted(positionStart, itemCount);            }        }        public void notifyItemRangeRemoved(int positionStart, int itemCount) {            // since onItemRangeRemoved() is implemented by the app, it could do anything, including            // removing itself from {@link mObservers} - and that could cause problems if            // an iterator is used on the ArrayList {@link mObservers}.            // to avoid such problems, just march thru the list in the reverse order.            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);            }        }    }    static class SavedState extends BaseSavedState {        Parcelable mLayoutState;        /**         * called by CREATOR         */        SavedState(Parcel in) {            super(in);            mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());        }        /**         * Called by onSaveInstanceState         */        SavedState(Parcelable superState) {            super(superState);        }        @Override        public void writeToParcel(Parcel dest, int flags) {            super.writeToParcel(dest, flags);            dest.writeParcelable(mLayoutState, 0);        }        private void copyFrom(SavedState other) {            mLayoutState = other.mLayoutState;        }        public static final Parcelable.Creator<SavedState> CREATOR                = new Parcelable.Creator<SavedState>() {            @Override            public SavedState createFromParcel(Parcel in) {                return new SavedState(in);            }            @Override            public SavedState[] newArray(int size) {                return new SavedState[size];            }        };    }    /**     * <p>Contains useful information about the current RecyclerView state like target scroll     * position or view focus. State object can also keep arbitrary data, identified by resource     * ids.</p>     * <p>Often times, RecyclerView components will need to pass information between each other.     * To provide a well defined data bus between components, RecyclerView passes the same State     * object to component callbacks and these components can use it to exchange data.</p>     * <p>If you implement custom components, you can use State's put/get/remove methods to pass     * data between your components without needing to manage their lifecycles.</p>     */    public static class State {        private int mTargetPosition = RecyclerView.NO_POSITION;        private ArrayMap<ViewHolder, ItemHolderInfo> mPreLayoutHolderMap =                new ArrayMap<ViewHolder, ItemHolderInfo>();        private ArrayMap<ViewHolder, ItemHolderInfo> mPostLayoutHolderMap =                new ArrayMap<ViewHolder, ItemHolderInfo>();        private SparseArray<Object> mData;        /**         * Number of items adapter has.         */        private int mItemCount = 0;        /**         * Number of items adapter had in the previous layout.         */        private int mPreviousLayoutItemCount = 0;        /**         * Number of items that were NOT laid out but has been deleted from the adapter after the         * previous layout.         */        private int mDeletedInvisibleItemCountSincePreviousLayout = 0;        private boolean mStructureChanged = false;        private boolean mInPreLayout = false;        State reset() {            mTargetPosition = RecyclerView.NO_POSITION;            if (mData != null) {                mData.clear();            }            mItemCount = 0;            mStructureChanged = false;            return this;        }        public boolean isPreLayout() {            return mInPreLayout;        }        /**         * Removes the mapping from the specified id, if there was any.         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.* to         *                   preserve cross functionality and avoid conflicts.         */        public void remove(int resourceId) {            if (mData == null) {                return;            }            mData.remove(resourceId);        }        /**         * Gets the Object mapped from the specified id, or <code>null</code>         * if no such data exists.         *         * @param resourceId Id of the resource you want to remove. It is suggested to use R.id.*         *                   to         *                   preserve cross functionality and avoid conflicts.         */        public <T> T get(int resourceId) {            if (mData == null) {                return null;            }            return (T) mData.get(resourceId);        }        /**         * Adds a mapping from the specified id to the specified value, replacing the previous         * mapping from the specified key if there was one.         *         * @param resourceId Id of the resource you want to add. It is suggested to use R.id.* to         *                   preserve cross functionality and avoid conflicts.         * @param data       The data you want to associate with the resourceId.         */        public void put(int resourceId, Object data) {            if (mData == null) {                mData = new SparseArray<Object>();            }            mData.put(resourceId, data);        }        /**         * If scroll is triggered to make a certain item visible, this value will return the         * adapter index of that item.         * @return Adapter index of the target item or         * {@link RecyclerView#NO_POSITION} if there is no target         * position.         */        public int getTargetScrollPosition() {            return mTargetPosition;        }        /**         * Returns if current scroll has a target position.         * @return true if scroll is being triggered to make a certain position visible         * @see #getTargetScrollPosition()         */        public boolean hasTargetScrollPosition() {            return mTargetPosition != RecyclerView.NO_POSITION;        }        /**         * @return true if the structure of the data set has changed since the last call to         *         onLayoutChildren, false otherwise         */        public boolean didStructureChange() {            return mStructureChanged;        }        /**         * @return Total number of items to be laid out. Note that, this number is not necessarily         * equal to the number of items in the adapter, so you should always use this number for         * your position calculations and never call adapter directly.         */        public int getItemCount() {            return mInPreLayout ?                    (mPreviousLayoutItemCount - mDeletedInvisibleItemCountSincePreviousLayout) :                    mItemCount;        }    }    /**     * Internal listener that manages items after animations finish. This is how items are     * retained (not recycled) during animations, but allowed to be recycled afterwards.     * It depends on the contract with the ItemAnimator to call the appropriate dispatch*Finished()     * method on the animator's listener when it is done animating any item.     */    private class ItemAnimatorRestoreListener implements ItemAnimator.ItemAnimatorListener {        @Override        public void onRemoveFinished(ViewHolder item) {            item.setIsRecyclable(true);            removeAnimatingView(item.itemView);            removeDetachedView(item.itemView, false);        }        @Override        public void onAddFinished(ViewHolder item) {            item.setIsRecyclable(true);            removeAnimatingView(item.itemView);        }        @Override        public void onMoveFinished(ViewHolder item) {            item.setIsRecyclable(true);            removeAnimatingView(item.itemView);        }    };    /**     * This class defines the animations that take place on items as changes are made     * to the adapter.     *     * Subclasses of ItemAnimator can be used to implement custom animations for actions on     * ViewHolder items. The RecyclerView will manage retaining these items while they     * are being animated, but implementors must call the appropriate "Finished"     * method when each item animation is done ({@link #dispatchRemoveFinished(ViewHolder)},     * {@link #dispatchMoveFinished(ViewHolder)}, or {@link #dispatchAddFinished(ViewHolder)}).     *     * <p>By default, RecyclerView uses {@link DefaultItemAnimator}</p>     *     * @see #setItemAnimator(ItemAnimator)     */    public static abstract class ItemAnimator {        private ItemAnimatorListener mListener = null;        private ArrayList<ItemAnimatorFinishedListener> mFinishedListeners =                new ArrayList<ItemAnimatorFinishedListener>();        private long mAddDuration = 120;        private long mRemoveDuration = 120;        private long mMoveDuration = 250;        /**         * Gets the current duration for which all move animations will run.         *         * @return The current move duration         */        public long getMoveDuration() {            return mMoveDuration;        }        /**         * Sets the current duration for which all move animations will run.         *         * @param moveDuration The current move duration         */        public void setMoveDuration(long moveDuration) {            mMoveDuration = moveDuration;        }        /**         * Gets the current duration for which all add animations will run.         *         * @return The current add duration         */        public long getAddDuration() {            return mAddDuration;        }        /**         * Sets the current duration for which all add animations will run.         *         * @param addDuration The current add duration         */        public void setAddDuration(long addDuration) {            mAddDuration = addDuration;        }        /**         * Gets the current duration for which all remove animations will run.         *         * @return The current remove duration         */        public long getRemoveDuration() {            return mRemoveDuration;        }        /**         * Sets the current duration for which all remove animations will run.         *         * @param removeDuration The current remove duration         */        public void setRemoveDuration(long removeDuration) {            mRemoveDuration = removeDuration;        }        /**         * Internal only:         * Sets the listener that must be called when the animator is finished         * animating the item (or immediately if no animation happens). This is set         * internally and is not intended to be set by external code.         *         * @param listener The listener that must be called.         */        void setListener(ItemAnimatorListener listener) {            mListener = listener;        }        /**         * Called when there are pending animations waiting to be started. This state         * is governed by the return values from {@link #animateAdd(ViewHolder) animateAdd()},         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and         * {@link #animateRemove(ViewHolder) animateRemove()}, which inform the         * RecyclerView that the ItemAnimator wants to be called later to start the         * associated animations. runPendingAnimations() will be scheduled to be run         * on the next frame.         */        abstract public void runPendingAnimations();        /**         * Called when an item is removed from the RecyclerView. Implementors can choose         * whether and how to animate that change, but must always call         * {@link #dispatchRemoveFinished(ViewHolder)} when done, either         * immediately (if no animation will occur) or after the animation actually finishes.         * The return value indicates whether an animation has been set up and whether the         * ItemAnimators {@link #runPendingAnimations()} method should be called at the         * next opportunity. This mechanism allows ItemAnimator to set up individual animations         * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and         * {@link #animateRemove(ViewHolder) animateRemove()} come in one by one, then         * start the animations together in the later call to {@link #runPendingAnimations()}.         *         * <p>This method may also be called for disappearing items which continue to exist in the         * RecyclerView, but for which the system does not have enough information to animate         * them out of view. In that case, the default animation for removing items is run         * on those items as well.</p>         *         * @param holder The item that is being removed.         * @return true if a later call to {@link #runPendingAnimations()} is requested,         * false otherwise.         */        abstract public boolean animateRemove(ViewHolder holder);        /**         * Called when an item is added to the RecyclerView. Implementors can choose         * whether and how to animate that change, but must always call         * {@link #dispatchAddFinished(ViewHolder)} when done, either         * immediately (if no animation will occur) or after the animation actually finishes.         * The return value indicates whether an animation has been set up and whether the         * ItemAnimators {@link #runPendingAnimations()} method should be called at the         * next opportunity. This mechanism allows ItemAnimator to set up individual animations         * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and         * {@link #animateRemove(ViewHolder) animateRemove()} come in one by one, then         * start the animations together in the later call to {@link #runPendingAnimations()}.         *         * <p>This method may also be called for appearing items which were already in the         * RecyclerView, but for which the system does not have enough information to animate         * them into view. In that case, the default animation for adding items is run         * on those items as well.</p>         *         * @param holder The item that is being added.         * @return true if a later call to {@link #runPendingAnimations()} is requested,         * false otherwise.         */        abstract public boolean animateAdd(ViewHolder holder);        /**         * Called when an item is moved in the RecyclerView. Implementors can choose         * whether and how to animate that change, but must always call         * {@link #dispatchMoveFinished(ViewHolder)} when done, either         * immediately (if no animation will occur) or after the animation actually finishes.         * The return value indicates whether an animation has been set up and whether the         * ItemAnimators {@link #runPendingAnimations()} method should be called at the         * next opportunity. This mechanism allows ItemAnimator to set up individual animations         * as separate calls to {@link #animateAdd(ViewHolder) animateAdd()},         * {@link #animateMove(ViewHolder, int, int, int, int) animateMove()}, and         * {@link #animateRemove(ViewHolder) animateRemove()} come in one by one, then         * start the animations together in the later call to {@link #runPendingAnimations()}.         *         * @param holder The item that is being moved.         * @return true if a later call to {@link #runPendingAnimations()} is requested,         * false otherwise.         */        abstract public boolean animateMove(ViewHolder holder, int fromX, int fromY,                int toX, int toY);        /**         * Method to be called by subclasses when a remove animation is done.         *         * @param item The item which has been removed         */        public final void dispatchRemoveFinished(ViewHolder item) {            if (mListener != null) {                mListener.onRemoveFinished(item);            }        }        /**         * Method to be called by subclasses when a move animation is done.         *         * @param item The item which has been moved         */        public final void dispatchMoveFinished(ViewHolder item) {            if (mListener != null) {                mListener.onMoveFinished(item);            }        }        /**         * Method to be called by subclasses when an add animation is done.         *         * @param item The item which has been added         */        public final void dispatchAddFinished(ViewHolder item) {            if (mListener != null) {                mListener.onAddFinished(item);            }        }        /**         * Method called when an animation on a view should be ended immediately.         * This could happen when other events, like scrolling, occur, so that         * animating views can be quickly put into their proper end locations.         * Implementations should ensure that any animations running on the item         * are canceled and affected properties are set to their end values.         * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)}         * should be called since the animations are effectively done when this         * method is called.         *         * @param item The item for which an animation should be stopped.         */        abstract public void endAnimation(ViewHolder item);        /**         * Method called when all item animations should be ended immediately.         * This could happen when other events, like scrolling, occur, so that         * animating views can be quickly put into their proper end locations.         * Implementations should ensure that any animations running on any items         * are canceled and affected properties are set to their end values.         * Also, appropriate dispatch methods (e.g., {@link #dispatchAddFinished(ViewHolder)}         * should be called since the animations are effectively done when this         * method is called.         */        abstract public void endAnimations();        /**         * Method which returns whether there are any item animations currently running.         * This method can be used to determine whether to delay other actions until         * animations end.         *         * @return true if there are any item animations currently running, false otherwise.         */        abstract public boolean isRunning();        /**         * Like {@link #isRunning()}, this method returns whether there are any item         * animations currently running. Addtionally, the listener passed in will be called         * when there are no item animations running, either immediately (before the method         * returns) if no animations are currently running, or when the currently running         * animations are {@link #dispatchAnimationsFinished() finished}.         *         * <p>Note that the listener is transient - it is either called immediately and not         * stored at all, or stored only until it is called when running animations         * are finished sometime later.</p>         *         * @param listener A listener to be called immediately if no animations are running         * or later when currently-running animations have finished. A null listener is         * equivalent to calling {@link #isRunning()}.         * @return true if there are any item animations currently running, false otherwise.         */        public final boolean isRunning(ItemAnimatorFinishedListener listener) {            boolean running = isRunning();            if (listener != null) {                if (!running) {                    listener.onAnimationsFinished();                } else {                    mFinishedListeners.add(listener);                }            }            return running;        }        /**         * The interface to be implemented by listeners to animation events from this         * ItemAnimator. This is used internally and is not intended for developers to         * create directly.         */        private interface ItemAnimatorListener {            void onRemoveFinished(ViewHolder item);            void onAddFinished(ViewHolder item);            void onMoveFinished(ViewHolder item);        }        /**         * This method should be called by ItemAnimator implementations to notify         * any listeners that all pending and active item animations are finished.         */        public final void dispatchAnimationsFinished() {            final int count = mFinishedListeners.size();            for (int i = 0; i < count; ++i) {                mFinishedListeners.get(i).onAnimationsFinished();            }            mFinishedListeners.clear();        }        /**         * This interface is used to inform listeners when all pending or running animations         * in an ItemAnimator are finished. This can be used, for example, to delay an action         * in a data set until currently-running animations are complete.         *         * @see #isRunning(ItemAnimatorFinishedListener)         */        public interface ItemAnimatorFinishedListener {            void onAnimationsFinished();        }    }    /**     * Internal data structure that holds information about an item's bounds.     * This information is used in calculating item animations.     */    private static class ItemHolderInfo {        ViewHolder holder;        int left, top, right, bottom;        int position;        ItemHolderInfo(ViewHolder holder, int left, int top, int right, int bottom, int position) {            this.holder = holder;            this.left = left;            this.top = top;            this.right = right;            this.bottom = bottom;            this.position = position;        }    }}


3 4
原创粉丝点击