com.netease.hearttouch.htrefreshrecyclerview.base.HTBaseRecyclerView.java Source code

Java tutorial

Introduction

Here is the source code for com.netease.hearttouch.htrefreshrecyclerview.base.HTBaseRecyclerView.java

Source

/*
 * This source code is licensed under the MIT-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.netease.hearttouch.htrefreshrecyclerview.base;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.netease.hearttouch.htrefreshrecyclerview.HTLoadMoreListener;
import com.netease.hearttouch.htrefreshrecyclerview.HTRecyclerViewDragListener;
import com.netease.hearttouch.htrefreshrecyclerview.HTRefreshListener;
import com.netease.hearttouch.htrefreshrecyclerview.R;
import com.netease.hearttouch.htrefreshrecyclerview.viewimpl.HTDefaultHorizontalRefreshViewHolder;
import com.netease.hearttouch.htrefreshrecyclerview.viewimpl.HTDefaultVerticalRefreshViewHolder;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static com.netease.hearttouch.htrefreshrecyclerview.base.HTOrientation.VERTICAL_DOWN;
import static com.netease.hearttouch.htrefreshrecyclerview.base.HTOrientation.VERTICAL_UP;

/**
 * 
 */
public abstract class HTBaseRecyclerView extends ViewGroup implements HTRefreshRecyclerViewInterface {
    private static final String TAG = HTBaseRecyclerView.class.getSimpleName();
    /**
     * ?
     */
    private static Class<? extends HTBaseViewHolder> sViewHolderClass;
    /**
     * ?
     */
    public HTRefreshListener mRefreshDelegate;
    /**
     * ??
     */
    public HTLoadMoreListener mLoadMoreDelegate;
    /**
     * ?
     */
    protected HTBaseViewHolder mHTViewHolder;

    protected HTViewHolderTracker mHTViewHolderTracker;

    protected RecyclerView.OnScrollListener mInnerScrollListener;
    /**
     * Adapter
     */
    protected HTWrapperAdapter mHTWrapperAdapter;
    /**
     * Adapter
     */
    protected RecyclerView.Adapter mInnerAdapter;
    /**
     * ???(????),
     */
    protected boolean mLoadMoreViewDisplay = true;
    /**
     * view
     */
    protected final ViewGroup mRefreshContainerView;
    /**
     * view
     */
    protected final ViewGroup mLoadMoreContainerView;
    /**
     * ?RecyclerView
     */
    protected final RecyclerView mRecyclerView;
    /**
     * ???
     */
    protected HTRefreshUIChangeListener mRefreshUIChangeListener;
    /**
     * ???
     */
    protected HTLoadMoreUIChangeListener mLoadMoreUIChangeListener;
    /**
     * ?
     */
    protected HTRecyclerViewDragListener mRecyclerViewDragListener;
    /**
     * ?
     */
    protected int mLoadMoreStatus = LoadMoreStatus.IDLE;
    /**
     * ?
     */
    protected int mRefreshStatus = RefreshStatus.IDLE;
    /**
     * ?,??
     */
    protected int mHTOrientation = VERTICAL_DOWN;
    /**
     * ??
     */
    protected boolean mHasMore = true;
    /**
     * ?
     */
    private final ArrayList<OnScrollListener> mScrollListeners = new ArrayList<>();

    /**
     * ???
     */
    protected boolean mEnableScrollOnReFresh = false;

    protected final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

    private Paint mRefreshBgPaint;

    public HTBaseRecyclerView(Context context) {
        this(context, null, 0);
    }

    public HTBaseRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HTBaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context);
        setWillNotDraw(false);
        //??
        mRecyclerView = new RecyclerView(getContext(), attrs, defStyleAttr);//?attrsRecyclerView
        mRefreshContainerView = new FrameLayout(getContext());
        mLoadMoreContainerView = new LinearLayout(getContext());
        //??
        initRefreshBgPaint(context);
        initAttrs(attrs);
        initViews();
    }

    private void initRefreshBgPaint(Context context) {
        mRefreshBgPaint = new Paint();
        mRefreshBgPaint.setColor(ContextCompat.getColor(context, android.R.color.transparent));
        mRefreshBgPaint.setStyle(Paint.Style.FILL);
        mRefreshBgPaint.setAntiAlias(true);
    }

    private void initAttrs(AttributeSet attrs) {
        if (attrs == null)
            return;
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.HTRefreshRecyclerView);
        try {
            mHTOrientation = typedArray.getInteger(R.styleable.HTRefreshRecyclerView_htOrientation, 1);
        } finally {
            typedArray.recycle();
        }
    }

    private void initViews() {
        //RecyclerView?,attrsRecyclerView,???
        mRecyclerView.setId(View.NO_ID);//Id?attrs??
        mRecyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);//

        //???
        removeAllViews();
        mRecyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        setViewLayoutParams(mRefreshContainerView);
        setViewLayoutParams(mLoadMoreContainerView);
        addView(mRecyclerView);
        addView(mRefreshContainerView);

        //?,?
        if (sViewHolderClass != null) {
            try {
                Constructor constructor = sViewHolderClass.getConstructor(Context.class);
                try {
                    setRefreshViewHolder((HTBaseViewHolder) constructor.newInstance(getContext()));
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        } else {
            HTBaseViewHolder viewHolder;
            if (checkOrientationVertical()) {
                viewHolder = new HTDefaultVerticalRefreshViewHolder(getContext());
                ((HTDefaultVerticalRefreshViewHolder) viewHolder).setDefaultRefreshViewArrow(mHTOrientation);
            } else {
                viewHolder = new HTDefaultHorizontalRefreshViewHolder(getContext());
                ((HTDefaultHorizontalRefreshViewHolder) viewHolder).setDefaultRefreshViewArrow(mHTOrientation);
            }
            setRefreshViewHolder(viewHolder);//?
        }
    }

    private void initListeners() {
        //RecyclerView?,?RecyclerViewbug
        //        setRecyclerViewOnTouchListener();
        //RecyclerView?
        setRecyclerViewOnScrollListener();
    }

    /**
     * ?
     */
    public static void setRefreshViewHolderClass(@NonNull Class<? extends HTBaseViewHolder> mViewHolderClass) {
        HTBaseRecyclerView.sViewHolderClass = mViewHolderClass;
    }

    /**
     * ?
     */
    public void setRefreshViewHolder(@NonNull HTBaseViewHolder refreshViewHolder) {
        mHTViewHolder = refreshViewHolder;
        mHTViewHolderTracker = mHTViewHolder.getViewHolderTracker();
        mHTViewHolderTracker.setOrientation(mHTOrientation);
        mHTViewHolder.computeViewSize(checkOrientationVertical() ? VERTICAL : HORIZONTAL);

        resetRefreshViewHolderView();
        initRefreshView();
        initLoadMoreView();
    }

    private void resetRefreshViewHolderView() {
        mRefreshContainerView.removeAllViews();
        mLoadMoreContainerView.removeAllViews();
    }

    private void initRefreshView() {
        if (mHTViewHolder == null)
            return;
        View refreshView = mHTViewHolder.getRefreshView();
        if (refreshView != null) {
            if (refreshView.getParent() != null) {
                ((ViewGroup) refreshView.getParent()).removeView(refreshView);
            }
            int res = mHTViewHolder.getRefreshViewBackgroundResId();
            if (res != 0) {//?
                mRefreshContainerView.setBackgroundResource(res);
            } else {
                mRefreshContainerView.setBackgroundResource(android.R.color.transparent);
            }
            mRefreshContainerView.removeAllViews();
            setViewLayoutParams(refreshView);
            mRefreshContainerView.addView(refreshView);
            //            hideRefreshView(true);
        }
        setRefreshUIChangeListener(mHTViewHolder);
    }

    private void initLoadMoreView() {
        if (mHTViewHolder == null)
            return;
        View loadMoreView = mHTViewHolder.getLoadMoreView();
        if (loadMoreView != null) {
            if (loadMoreView.getParent() != null) {
                ((ViewGroup) loadMoreView.getParent()).removeView(loadMoreView);
            }
            int res = mHTViewHolder.getLoadMoreViewBackgroundResId();
            if (res != 0) {//?
                mLoadMoreContainerView.setBackgroundResource(res);
            } else {
                mLoadMoreContainerView.setBackgroundResource(android.R.color.transparent);
            }
            mLoadMoreContainerView.removeAllViews();
            setViewLayoutParams(loadMoreView);
            mLoadMoreContainerView.addView(loadMoreView);
            hideLoadMoreView(true);
        }
        setLoadMoreUIChangeListener(mHTViewHolder);
    }

    private void setViewLayoutParams(View view) {
        if (view == null)
            return;
        ViewGroup.LayoutParams lp = view.getLayoutParams() == null ? new ViewGroup.LayoutParams(0, 0)
                : view.getLayoutParams();
        lp.width = checkOrientationVertical() ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
        lp.height = checkOrientationVertical() ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
        view.setLayoutParams(lp);
    }

    protected boolean checkOrientationVertical() {
        return mHTOrientation == VERTICAL_UP || mHTOrientation == VERTICAL_DOWN;
    }

    protected boolean checkOrientationReverse() {
        return mHTOrientation == HTOrientation.HORIZONTAL_LEFT || mHTOrientation == VERTICAL_UP;
    }

    private void checkChildren() {
        final int childCount = getChildCount();
        if (childCount > 2) {
            throw new IllegalStateException("HTRefreshRecyclerView can only contains 2 children");
        }
    }

    public void addView(View child) {
        checkChildren();
        super.addView(child);
    }

    public void addView(View child, int index) {
        checkChildren();
        super.addView(child, index);
    }

    public void addView(View child, int index, LayoutParams params) {
        checkChildren();
        super.addView(child, index, params);
    }

    public void addView(View child, LayoutParams params) {
        checkChildren();
        super.addView(child, params);
    }

    public void addView(View child, int width, int height) {
        checkChildren();
        super.addView(child, width, height);
    }

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mRefreshContainerView != null) {
            measureChild(mRefreshContainerView, widthMeasureSpec, heightMeasureSpec);
        }
        if (mRecyclerView != null) {
            measureChild(mRecyclerView, widthMeasureSpec, heightMeasureSpec);
        }
    }

    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //        super.onLayout(changed, l, t, r, b);
        if (checkOrientationVertical()) {
            layoutVertical(mHTOrientation == VERTICAL_DOWN);
        } else {
            layoutHorizontal(mHTOrientation == HTOrientation.HORIZONTAL_RIGHT);
        }
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHTViewHolderTracker.isOverRefreshViewSize() && mHTViewHolder.getRefreshViewBackgroundResId() > 0) {
            int drawOffset = Math
                    .abs(mHTViewHolderTracker.getCurrentPos() - mHTViewHolderTracker.getRefreshViewSize());
            mRefreshBgPaint
                    .setColor(ContextCompat.getColor(getContext(), mHTViewHolder.getRefreshViewBackgroundResId()));
            switch (mHTOrientation) {
            case VERTICAL_DOWN:
                canvas.drawRect(0, 0, getWidth(), drawOffset, mRefreshBgPaint);
                break;
            case HTOrientation.VERTICAL_UP:
                canvas.drawRect(0, getHeight() - drawOffset, getWidth(), getHeight(), mRefreshBgPaint);
                break;
            case HTOrientation.HORIZONTAL_RIGHT:
                canvas.drawRect(0, 0, drawOffset, getHeight(), mRefreshBgPaint);
                break;
            case HTOrientation.HORIZONTAL_LEFT:
                canvas.drawRect(getWidth() - drawOffset, 0, getWidth(), getHeight(), mRefreshBgPaint);
                break;
            }

        }
    }

    void layoutVertical(boolean isTop) {
        int offset = mHTViewHolderTracker.getCurrentPos();
        int size = mHTViewHolderTracker.getRefreshViewSize();

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();

        int left, top, right, bottom;

        if (mRefreshContainerView != null) {
            left = paddingLeft;
            if (isTop) {
                top = -(size - paddingTop - offset);
            } else {
                top = mRecyclerView.getMeasuredHeight() - offset;
            }
            right = left + mRefreshContainerView.getMeasuredWidth();
            bottom = top + mRefreshContainerView.getMeasuredHeight();
            mRefreshContainerView.layout(left, top, right, bottom);
        }
        if (mRecyclerView != null) {
            left = paddingLeft;
            if (isTop) {
                top = paddingTop + offset;
            } else {
                top = paddingTop - offset;
            }
            right = left + mRecyclerView.getMeasuredWidth();
            bottom = top + mRecyclerView.getMeasuredHeight();
            mRecyclerView.layout(left, top, right, bottom);
        }

    }

    void layoutHorizontal(boolean isLeft) {
        int offset = mHTViewHolderTracker.getCurrentPos();
        int size = mHTViewHolderTracker.getRefreshViewSize();

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();

        int left, top, right, bottom;
        if (mRefreshContainerView != null) {
            if (isLeft) {
                left = -(size - paddingLeft - offset);
            } else {
                left = mRecyclerView.getMeasuredWidth() - offset;
            }
            top = paddingTop;
            right = left + mRefreshContainerView.getMeasuredWidth();
            bottom = top + mRefreshContainerView.getMeasuredHeight();
            mRefreshContainerView.layout(left, top, right, bottom);
        }
        if (mRecyclerView != null) {
            if (isLeft) {
                left = paddingLeft + offset;
            } else {
                left = paddingLeft - offset;
            }
            top = paddingTop;
            right = left + mRecyclerView.getMeasuredWidth();
            bottom = top + mRecyclerView.getMeasuredHeight();
            mRecyclerView.layout(left, top, right, bottom);
        }
    }

    /**
     * ??,???
     */
    public boolean checkChildScroll() {
        switch (mHTOrientation) {
        case VERTICAL_UP:
            return ViewCompat.canScrollVertically(mRecyclerView, 1);
        case VERTICAL_DOWN:
            return ViewCompat.canScrollVertically(mRecyclerView, -1);
        case HTOrientation.HORIZONTAL_LEFT:
            return ViewCompat.canScrollHorizontally(mRecyclerView, 1);
        case HTOrientation.HORIZONTAL_RIGHT:
            return ViewCompat.canScrollHorizontally(mRecyclerView, -1);
        default:
            return false;
        }

    }

    /**
     * ???
     */
    protected void processLoadMoreStatusChanged() {
        if (mLoadMoreUIChangeListener == null)
            return;
        switch (mLoadMoreStatus) {
        case LoadMoreStatus.IDLE:
            mLoadMoreUIChangeListener.onLoadMoreComplete(mHasMore);
            break;
        case LoadMoreStatus.LOADING:
            mLoadMoreUIChangeListener.onLoadMoreStart(mHasMore);
            break;
        default:
            break;
        }
    }

    /**
     * ?
     */
    public void hideLoadMoreView(boolean isHide) {
        if (mLoadMoreContainerView != null && mHTViewHolder != null) {
            int size = 0;
            if (isHide) {
                size = -mHTViewHolderTracker.getLoadMoreSize();
            }
            switch (mHTOrientation) {
            case VERTICAL_DOWN:
                mLoadMoreContainerView.setPadding(0, 0, 0, isHide ? size : 0);
                break;
            case VERTICAL_UP:
                mLoadMoreContainerView.setPadding(0, isHide ? size : 0, 0, 0);
                break;
            case HTOrientation.HORIZONTAL_LEFT:
                mLoadMoreContainerView.setPadding(isHide ? size : 0, 0, 0, 0);
                break;
            case HTOrientation.HORIZONTAL_RIGHT:
                mLoadMoreContainerView.setPadding(0, 0, isHide ? size : 0, 0);
                break;
            }
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        initListeners();
        RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
        if (adapter != null && !(adapter instanceof HTWrapperAdapter)) {//?,?HTWrapperAdapter
            throw new IllegalArgumentException("the type of adapter is incorrect !");
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        clear();//?
        super.onDetachedFromWindow();
    }

    private void clear() {
        mRecyclerView.clearOnScrollListeners();
        mRecyclerView.setOnTouchListener(null);
    }

    public void setEnableScrollOnRefresh(boolean enableScrollOnReFresh) {
        mEnableScrollOnReFresh = enableScrollOnReFresh;
    }

    /**
     * ?
     * ?? RecyclerView BugIndexOutOfBoundsException: Inconsistency detected. Invalid item position
     */

    /*   private void setRecyclerViewOnTouchListener() {
    mRecyclerView.setOnTouchListener(
            new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    return !mEnableScrollOnReFresh && mRefreshStatus == RefreshStatus.REFRESHING;
                }
            }
    );
       }*/
    private void setRecyclerViewOnScrollListener() {
        mInnerScrollListener = new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                if (newState == RecyclerView.SCROLL_STATE_IDLE && shouldHandleLoadMore()) {
                    performLoadMore();//???
                }
                if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                    if (mRecyclerViewDragListener != null) {
                        mRecyclerViewDragListener.onRecyclerViewScroll();
                    }
                }
                for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
                    OnScrollListener scrollListener = mScrollListeners.get(i);
                    if (scrollListener != null) {
                        scrollListener.onScrollStateChanged(recyclerView, newState);
                    }
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
                    OnScrollListener scrollListener = mScrollListeners.get(i);
                    if (scrollListener != null) {
                        scrollListener.onScrolled(recyclerView, dx, dy);
                    }
                }
            }
        };
        mRecyclerView.addOnScrollListener(mInnerScrollListener);
    }

    /**
     * ?
     */
    protected abstract Boolean handleMoveAction(MotionEvent event);

    /**
     * ??
     */
    protected abstract boolean shouldHandleRefresh();

    /**
     * ??
     */
    protected abstract boolean shouldHandleLoadMore();

    /**
     * 
     */
    protected abstract void performRefresh();

    /**
     * 
     */
    protected abstract void performLoadMore();

    /**
     * ?
     */
    protected abstract void endRefresh();

    /**
     * ?
     */
    protected abstract void endLoadMore();

    @Override
    public void setOnLoadMoreListener(HTLoadMoreListener loadMoreDelegate) {
        mLoadMoreDelegate = loadMoreDelegate;
    }

    @Override
    public void setOnRefreshListener(HTRefreshListener refreshDelegate) {
        mRefreshDelegate = refreshDelegate;
    }

    private void setRefreshUIChangeListener(HTRefreshUIChangeListener refreshUIChangeListener) {
        mRefreshUIChangeListener = refreshUIChangeListener;
    }

    private void setLoadMoreUIChangeListener(HTLoadMoreUIChangeListener loadMoreUIChangeListener) {
        mLoadMoreUIChangeListener = loadMoreUIChangeListener;
    }

    public void setLoadMoreViewShow(boolean loadMoreShow) {
        this.mLoadMoreViewDisplay = loadMoreShow;
    }

    @Override
    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }

    @Override
    public RecyclerView.LayoutManager getLayoutManager() {
        return mRecyclerView.getLayoutManager();
    }

    @Override
    public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
        boolean reverse = checkOrientationReverse();
        int orientation = checkOrientationVertical() ? OrientationHelper.VERTICAL : OrientationHelper.HORIZONTAL;
        //???,layoutManager
        if (layoutManager instanceof GridLayoutManager) {
            GridLayoutManager realLayoutManager = (GridLayoutManager) layoutManager;
            realLayoutManager.setReverseLayout(reverse);
            realLayoutManager.setOrientation(orientation);
        } else if (layoutManager instanceof LinearLayoutManager) {
            LinearLayoutManager realLayoutManager = (LinearLayoutManager) layoutManager;
            realLayoutManager.setReverseLayout(reverse);
            realLayoutManager.setOrientation(orientation);
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            StaggeredGridLayoutManager realLayoutManager = (StaggeredGridLayoutManager) layoutManager;
            realLayoutManager.setReverseLayout(reverse);
            realLayoutManager.setOrientation(orientation);
        }
        mRecyclerView.setLayoutManager(layoutManager);
    }

    @Override
    public RecyclerView.ItemAnimator getItemAnimator() {
        return mRecyclerView.getItemAnimator();
    }

    @Override
    public void setItemAnimator(RecyclerView.ItemAnimator animator) {
        mRecyclerView.setItemAnimator(animator);
    }

    @Override
    public void addItemDecoration(RecyclerView.ItemDecoration decor) {
        if (decor != null) {//,?view
            addItemDecoration(new HTItemDecoration(decor), -1);
        }
    }

    @Override
    public void removeItemDecoration(RecyclerView.ItemDecoration decor) {
        mRecyclerView.removeItemDecoration(decor);
    }

    @Override
    public void setHasFixedSize(boolean hasFixedSize) {
        mRecyclerView.setHasFixedSize(hasFixedSize);
    }

    @Override
    public boolean hasFixedSize() {
        return getRecyclerView().hasFixedSize();
    }

    @Override
    public RecyclerView.Adapter getAdapter() {
        return mInnerAdapter;
    }

    public void setAdapter(RecyclerView.Adapter adapter) {
        if (adapter == null)
            return;
        //??viewAdapter
        mInnerAdapter = adapter;
        mHTWrapperAdapter = new HTWrapperAdapter(adapter, mLoadMoreContainerView);
        mRecyclerView.setAdapter(mHTWrapperAdapter);
    }

    @Override
    public void setRecyclerListener(RecyclerView.RecyclerListener listener) {
        mRecyclerView.setRecyclerListener(listener);
    }

    @Override
    public void clearOnScrollListeners() {
        mScrollListeners.clear();
    }

    @Override
    public RecyclerView.ViewHolder getChildViewHolder(View child) {
        return mRecyclerView.getChildViewHolder(child);
    }

    @Override
    public void addOnItemTouchListener(RecyclerView.OnItemTouchListener listener) {
        mRecyclerView.addOnItemTouchListener(listener);
    }

    @Override
    public void removeOnItemTouchListener(RecyclerView.OnItemTouchListener listener) {
        mRecyclerView.removeOnItemTouchListener(listener);
    }

    @Override
    public void addOnScrollListener(OnScrollListener listener) {
        mScrollListeners.add(listener);
    }

    @Override
    public void removeOnScrollListener(OnScrollListener listener) {
        mScrollListeners.remove(listener);
    }

    @Override
    public void setRecyclerViewDragListener(HTRecyclerViewDragListener recyclerViewDragListener) {
        mRecyclerViewDragListener = recyclerViewDragListener;
    }

    @Override
    public void addItemDecoration(RecyclerView.ItemDecoration decor, int index) {
        if (decor != null) {
            mRecyclerView.addItemDecoration(new HTItemDecoration(decor), index);
        }
    }

    /**
     * ?
     */
    public static class RefreshStatus {
        /**
         * ??
         */
        public static final int IDLE = 0;
        /**
         * ?
         */
        public static final int REFRESH_PREPARE = 1;
        /**
         * ?
         */
        public static final int REFRESHING = 2;
        /**
         * ??
         */
        public static final int COMPLETE = 3;
    }

    /**
     * ?
     */
    public static class LoadMoreStatus {
        public static final int IDLE = 0;
        public static final int LOADING = 1;
    }

    /**
     * RecyclerView??
     */
    public interface OnScrollListener {
        void onScrollStateChanged(RecyclerView recyclerView, int newState);

        void onScrolled(RecyclerView recyclerView, int dx, int dy);
    }

    /**
     * ??,??????
     */
    protected interface HTRefreshUIChangeListener {
        /**
         * ?
         */
        void onReset();

        /**
         * 
         */
        void onRefreshPrepare();

        /**
         * ?
         */
        void onRefreshing();

        /**
         * ??
         */
        void onRefreshComplete();

        /**
         * ??(?{@link RefreshStatus#IDLE}{@link RefreshStatus#REFRESHING})
         * ????
         *
         * @param scale             0  11  0
         * @param moveDistance      ??
         * @param refreshStatus     ??{@link RefreshStatus#IDLE}{@link RefreshStatus#REFRESH_PREPARE}{@link RefreshStatus#REFRESHING}{@link RefreshStatus#COMPLETE}
         * @param viewHolderTracker ???{@link HTViewHolderTracker}
         */
        void onRefreshPositionChange(float scale, float moveDistance, int refreshStatus,
                HTViewHolderTracker viewHolderTracker);
    }

    /**
     * ??
     */
    protected interface HTLoadMoreUIChangeListener {
        /**
         * ?
         *
         * @param hasMore ??
         */
        void onLoadMoreStart(boolean hasMore);

        /**
         * ??
         *
         * @param hasMore ??
         */
        void onLoadMoreComplete(boolean hasMore);
    }
}