RecyclerView更全解析之 - 打造通用的下拉刷新上拉加載

1.概述


這期我們在上一期的RecyclerView更全解析之 - 為它優(yōu)雅的添加頭部和底部的基礎(chǔ)上再去增加功能,我相信我們在真正的實(shí)踐開發(fā)過程中肯定少不了下拉刷新和上拉加載。
  我們需要思考一個問題上拉刷新下拉加載風(fēng)格各式各樣,淘寶和京東的列表刷新樣式就肯定不一樣,我們怎么樣做到版本迭代的時候可以快速的更改樣式。有時還需要顯示正在加載數(shù)據(jù)或者無數(shù)據(jù),比如篩選的時候有可能會出現(xiàn)沒有數(shù)據(jù)的情況會顯示無數(shù)據(jù)頁面,怎么快速做到?當(dāng)然如果你對系統(tǒng)架構(gòu)比較了解那就非常簡單了,又或者是你對面向?qū)ο蟮牧蠡驹瓌t比較熟悉也行。

相關(guān)文章:
  
  RecyclerView更全解析之 - 基本使用和分割線解析
  
  RecyclerView更全解析之 - 打造通用的萬能Adapter
  
  RecyclerView更全解析之 - 為它優(yōu)雅的添加頭部和底部
  
  RecyclerView更全解析之 - 打造通用的下拉刷新上拉加載
  
  RecyclerView更全解析之 - 仿支付寶側(cè)滑刪除和拖動排序
  
  
  

這里寫圖片描述

2.基本思路


我們在寫項(xiàng)目或是搭建架構(gòu)的時候需要考慮最多的是擴(kuò)展,而不是先把所有的功能寫在一起或是全部寫好,或者說代碼過度設(shè)計(jì)本來很簡單的東西你非得跟人解釋這怎么怎么的,很忌諱。
  肯定是希望目前寫好的東西,以后如果出現(xiàn)什么問題或者添加新的功能都不需要去修改我們已經(jīng)寫好的代碼,而是在原來的基礎(chǔ)上利用面向?qū)ο蟮乃枷肴U(kuò)展無論你是繼承也好還是實(shí)現(xiàn)也好都行,就不會出現(xiàn)需求改變的時候我們的代碼就改成了別人口中說的改成了......
  
  本著這個原則我們大致的思想就是:

  • 先處理下拉刷新,同時考慮刷新列表的不同風(fēng)格樣式,確保這個項(xiàng)目還是下一個項(xiàng)目都能用
  • 再處理上拉加載更多,只需去繼承寫好的下拉刷新控件即可
  • 可以適當(dāng)?shù)脑黾右恍┗竟δ埽缯诩虞d列表樣式或者說是無頁面數(shù)據(jù)樣式
  • 封裝通用默認(rèn)的樣式,封裝好整個項(xiàng)目的通用樣式,如果下次需要修改擴(kuò)展即可
  • 最后思考一下我們這樣去寫合不合理,給自己的同事用用自己和他們都做一下測評和修改

3.基本實(shí)現(xiàn)


3.1 下拉刷新
  先處理下拉刷新,同時考慮刷新列表的不同風(fēng)格樣式,確保這個項(xiàng)目還是下一個項(xiàng)目都能用。這里我們肯定是繼承上一期的可以直接添加頭部和底部的WrapRecyclerView,為了確保實(shí)現(xiàn)不同的樣式,需要一個額外的輔助類:

/**
 * Created by Darren on 2017/1/3.
 * Email: 240336124@qq.com
 * Description: 下拉刷新的輔助類為了匹配所有效果
 */

public abstract class RefreshViewCreator {

    /**
     * 獲取下拉刷新的View
     *
     * @param context 上下文
     * @param parent  RecyclerView
     */
    public abstract View getRefreshView(Context context, ViewGroup parent);

    /**
     * 正在下拉
     * @param currentDragHeight   當(dāng)前拖動的高度
     * @param refreshViewHeight  總的刷新高度
     * @param currentRefreshStatus 當(dāng)前狀態(tài)
     */
    public abstract void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus);

    /**
     * 正在刷新中
     */
    public abstract void onRefreshing();

    /**
     * 停止刷新
     */
    public abstract void onStopRefresh();
}

/**
 * Created by Darren on 2017/1/3.
 * Email: 240336124@qq.com
 * Description: 下拉刷新的RecyclerView
 */
public class RefreshRecyclerView extends WrapRecyclerView {
    // 下拉刷新的輔助類
    private RefreshViewCreator mRefreshCreator;
    // 下拉刷新頭部的高度
    private int mRefreshViewHeight = 0;
    // 下拉刷新的頭部View
    private View mRefreshView;
    // 手指按下的Y位置
    private int mFingerDownY;
    // 手指拖拽的阻力指數(shù)
    private float mDragIndex = 0.35f;
    // 當(dāng)前是否正在拖動
    private boolean mCurrentDrag = false;
    // 當(dāng)前的狀態(tài)
    private int mCurrentRefreshStatus;
    // 默認(rèn)狀態(tài)
    public int REFRESH_STATUS_NORMAL = 0x0011;
    // 下拉刷新狀態(tài)
    public int REFRESH_STATUS_PULL_DOWN_REFRESH = 0x0022;
    // 松開刷新狀態(tài)
    public int REFRESH_STATUS_LOOSEN_REFRESHING = 0x0033;
    // 正在刷新狀態(tài)
    public int REFRESH_STATUS_REFRESHING = 0x0033;

    public RefreshRecyclerView(Context context) {
        super(context);
    }

    public RefreshRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RefreshRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    // 先處理下拉刷新,同時考慮刷新列表的不同風(fēng)格樣式,確保這個項(xiàng)目還是下一個項(xiàng)目都能用
    // 所以我們不能直接添加View,需要利用輔助類
    public void addRefreshViewCreator(RefreshViewCreator refreshCreator) {
        this.mRefreshCreator = refreshCreator;
        addRefreshView();
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);
        addRefreshView();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 記錄手指按下的位置 ,之所以寫在dispatchTouchEvent那是因?yàn)槿绻覀兲幚砹藯l目點(diǎn)擊事件,
                // 那么就不會進(jìn)入onTouchEvent里面,所以只能在這里獲取
                mFingerDownY = (int) ev.getRawY();
                break;

            case MotionEvent.ACTION_UP:
                if (mCurrentDrag) {
                    restoreRefreshView();
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    /**
     * 重置當(dāng)前刷新狀態(tài)狀態(tài)
     */
    private void restoreRefreshView() {
        int currentTopMargin = ((MarginLayoutParams) mRefreshView.getLayoutParams()).topMargin;
        int finalTopMargin = -mRefreshViewHeight + 1;
        if (mCurrentRefreshStatus == REFRESH_STATUS_LOOSEN_REFRESHING) {
            finalTopMargin = 0;
            mCurrentRefreshStatus = REFRESH_STATUS_REFRESHING;
            if (mRefreshCreator != null) {
                mRefreshCreator.onRefreshing();
            }
            if (mListener != null) {
                mListener.onRefresh();
            }
        }

        int distance = currentTopMargin - finalTopMargin;

        // 回彈到指定位置
        ValueAnimator animator = ObjectAnimator.ofFloat(currentTopMargin, finalTopMargin).setDuration(distance);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentTopMargin = (float) animation.getAnimatedValue();
                setRefreshViewMarginTop((int) currentTopMargin);
            }
        });
        animator.start();
        mCurrentDrag = false;
    }


    @Override
    public boolean onTouchEvent(MotionEvent e) {
        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:
                // 如果是在最頂部才處理,否則不需要處理
                if (canScrollUp() || mCurrentRefreshStatus == REFRESH_STATUS_REFRESHING) {
                    // 如果沒有到達(dá)最頂端,也就是說還可以向上滾動就什么都不處理
                    return super.onTouchEvent(e);
                }

                // 解決下拉刷新自動滾動問題
                if (mCurrentDrag) {
                    scrollToPosition(0);
                }

                // 獲取手指觸摸拖拽的距離
                int distanceY = (int) ((e.getRawY() - mFingerDownY) * mDragIndex);
                // 如果是已經(jīng)到達(dá)頭部,并且不斷的向下拉,那么不斷的改變r(jià)efreshView的marginTop的值
                if (distanceY > 0) {
                    int marginTop = distanceY - mRefreshViewHeight;
                    setRefreshViewMarginTop(marginTop);
                    updateRefreshStatus(marginTop);
                    mCurrentDrag = true;
                    return false;
                }
                break;
        }

        return super.onTouchEvent(e);
    }

    /**
     * 更新刷新的狀態(tài)
     */
    private void updateRefreshStatus(int marginTop) {
        if (marginTop <= -mRefreshViewHeight) {
            mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
        } else if (marginTop < 0) {
            mCurrentRefreshStatus = REFRESH_STATUS_PULL_DOWN_REFRESH;
        } else {
            mCurrentRefreshStatus = REFRESH_STATUS_LOOSEN_REFRESHING;
        }

        if (mRefreshCreator != null) {
            mRefreshCreator.onPull(marginTop, mRefreshViewHeight, mCurrentRefreshStatus);
        }
    }

    /**
     * 添加頭部的刷新View
     */
    private void addRefreshView() {
        RecyclerView.Adapter adapter = getAdapter();
        if (adapter != null && mRefreshCreator != null) {
            // 添加頭部的刷新View
            View refreshView = mRefreshCreator.getRefreshView(getContext(), this);
            if (refreshView != null) {
                addHeaderView(refreshView);
                this.mRefreshView = refreshView;
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            if (mRefreshView != null && mRefreshViewHeight <= 0) {
                // 獲取頭部刷新View的高度
                mRefreshViewHeight = mRefreshView.getMeasuredHeight();
                if (mRefreshViewHeight > 0) {
                    // 隱藏頭部刷新的View  marginTop  多留出1px防止無法判斷是不是滾動到頭部問題
                    setRefreshViewMarginTop(-mRefreshViewHeight + 1);
                }
            }
        }
    }

    /**
     * 設(shè)置刷新View的marginTop
     */
    public void setRefreshViewMarginTop(int marginTop) {
        MarginLayoutParams params = (MarginLayoutParams) mRefreshView.getLayoutParams();
        if (marginTop < -mRefreshViewHeight + 1) {
            marginTop = -mRefreshViewHeight + 1;
        }
        params.topMargin = marginTop;
        mRefreshView.setLayoutParams(params);
    }


    /**
     * @return Whether it is possible for the child view of this layout to
     * scroll up. Override this if the child view is a custom view.
     * 判斷是不是滾動到了最頂部,這個是從SwipeRefreshLayout里面copy過來的源代碼
     */
    public boolean canScrollUp() {
        if (android.os.Build.VERSION.SDK_INT < 14) {
            return ViewCompat.canScrollVertically(this, -1) || this.getScrollY() > 0;
        } else {
            return ViewCompat.canScrollVertically(this, -1);
        }
    }

    /**
     * 停止刷新
     */
    public void onStopRefresh() {
        mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
        restoreRefreshView();
        if (mRefreshCreator != null) {
            mRefreshCreator.onStopRefresh();
        }
    }

    // 處理刷新回調(diào)監(jiān)聽
    private OnRefreshListener mListener;

    public void setOnRefreshListener(OnRefreshListener listener) {
        this.mListener = listener;
    }

    public interface OnRefreshListener {
        void onRefresh();
    }
}

我們來寫一個默認(rèn)的下拉刷新效果測試一下,這個gif錄制軟件的效果不是特別給力

/**
 * Created by Darren on 2017/1/3.
 * Email: 240336124@qq.com
 * Description: 默認(rèn)樣式的頭部刷新
 *              如淘寶、京東、不同的樣式可以自己去實(shí)現(xiàn)
 */

public class DefaultRefreshCreator extends RefreshViewCreator {
    // 加載數(shù)據(jù)的ImageView
    private View mRefreshIv;

    @Override
    public View getRefreshView(Context context, ViewGroup parent) {
        View refreshView = LayoutInflater.from(context).inflate(R.layout.layout_refresh_header_view, parent, false);
        mRefreshIv = refreshView.findViewById(R.id.refresh_iv);
        return refreshView;
    }

    @Override
    public void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus) {
        float rotate = ((float) currentDragHeight) / refreshViewHeight;
        // 不斷下拉的過程中不斷的旋轉(zhuǎn)圖片
        mRefreshIv.setRotation(rotate * 360);
    }

    @Override
    public void onRefreshing() {
        // 刷新的時候不斷旋轉(zhuǎn)
        RotateAnimation animation = new RotateAnimation(0, 720,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setRepeatCount(-1);
        animation.setDuration(1000);
        mRefreshIv.startAnimation(animation);
    }

    @Override
    public void onStopRefresh() {
        // 停止加載的時候清除動畫
        mRefreshIv.setRotation(0);
        mRefreshIv.clearAnimation();
    }
}

這里寫圖片描述

3.2 處理上拉加載更多
  再處理上拉加載更多,只需去繼承寫好的下拉刷新控件即可。我們的確可以在原來的這個下拉刷新的控件中去寫,但是有幾個問題都寫到一堆出了問題找誰?別人怎么看代碼?如果該需求只要下拉刷新呢?說好的擴(kuò)展。所以我們新寫一個控件繼承已經(jīng)寫好的下拉刷新控件每個類負(fù)責(zé)單獨(dú)的事情

 /**
 * Created by Darren on 2017/1/3.
 * Email: 240336124@qq.com
 * Description: 上拉加載更多的輔助類為了匹配所有效果
 */

public abstract class LoadViewCreator {

    /**
     * 獲取上拉加載更多的View
     *
     * @param context 上下文
     * @param parent  RecyclerView
     */
    public abstract View getLoadView(Context context, ViewGroup parent);

    /**
     * 正在上拉
     *
     * @param currentDragHeight    當(dāng)前拖動的高度
     * @param loadViewHeight    總的加載高度
     * @param currentLoadStatus 當(dāng)前狀態(tài)
     */
    public abstract void onPull(int currentDragHeight, int loadViewHeight, int currentLoadStatus);

    /**
     * 正在加載中
     */
    public abstract void onLoading();

    /**
     * 停止加載
     */
    public abstract void onStopLoad();
}

/**
 * Created by Darren on 2017/1/3.
 * Email: 240336124@qq.com
 * Description: 下拉刷新上拉加載更多的RecyclerView
 */
public class LoadRefreshRecyclerView extends RefreshRecyclerView {
    // 上拉加載更多的輔助類
    private LoadViewCreator mLoadCreator;
    // 上拉加載更多頭部的高度
    private int mLoadViewHeight = 0;
    // 上拉加載更多的頭部View
    private View mLoadView;
    // 手指按下的Y位置
    private int mFingerDownY;
    // 當(dāng)前是否正在拖動
    private boolean mCurrentDrag = false;
    // 當(dāng)前的狀態(tài)
    private int mCurrentLoadStatus;
    // 默認(rèn)狀態(tài)
    public int LOAD_STATUS_NORMAL = 0x0011;
    // 上拉加載更多狀態(tài)
    public static int LOAD_STATUS_PULL_DOWN_REFRESH = 0x0022;
    // 松開加載更多狀態(tài)
    public static int LOAD_STATUS_LOOSEN_LOADING = 0x0033;
    // 正在加載更多狀態(tài)
    public int LOAD_STATUS_LOADING = 0x0044;

    public LoadRefreshRecyclerView(Context context) {
        super(context);
    }

    public LoadRefreshRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public LoadRefreshRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    // 先處理上拉加載更多,同時考慮加載列表的不同風(fēng)格樣式,確保這個項(xiàng)目還是下一個項(xiàng)目都能用
    // 所以我們不能直接添加View,需要利用輔助類
    public void addLoadViewCreator(LoadViewCreator loadCreator) {
        this.mLoadCreator = loadCreator;
        addRefreshView();
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);
        addRefreshView();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 記錄手指按下的位置 ,之所以寫在dispatchTouchEvent那是因?yàn)槿绻覀兲幚砹藯l目點(diǎn)擊事件,
                // 那么就不會進(jìn)入onTouchEvent里面,所以只能在這里獲取
                mFingerDownY = (int) ev.getRawY();
                break;

            case MotionEvent.ACTION_UP:
                if (mCurrentDrag) {
                    restoreLoadView();
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    /**
     * 重置當(dāng)前加載更多狀態(tài)
     */
    private void restoreLoadView() {
        int currentBottomMargin = ((MarginLayoutParams) mLoadView.getLayoutParams()).bottomMargin;
        int finalBottomMargin = 0;
        if (mCurrentLoadStatus == LOAD_STATUS_LOOSEN_LOADING) {
            mCurrentLoadStatus = LOAD_STATUS_LOADING;
            if (mLoadCreator != null) {
                mLoadCreator.onLoading();
            }
            if (mListener != null) {
                mListener.onLoad();
            }
        }

        int distance = currentBottomMargin - finalBottomMargin;

        // 回彈到指定位置
        ValueAnimator animator = ObjectAnimator.ofFloat(currentBottomMargin, finalBottomMargin).setDuration(distance);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float currentTopMargin = (float) animation.getAnimatedValue();
                setLoadViewMarginBottom((int) currentTopMargin);
            }
        });
        animator.start();
        mCurrentDrag = false;
    }


    @Override
    public boolean onTouchEvent(MotionEvent e) {
        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:
                // 如果是在最底部才處理,否則不需要處理
                if (canScrollDown() || mCurrentLoadStatus == LOAD_STATUS_LOADING) {
                    // 如果沒有到達(dá)最頂端,也就是說還可以向上滾動就什么都不處理
                    return super.onTouchEvent(e);
                }

                if (mLoadCreator != null) {
                    mLoadViewHeight = mLoadView.getMeasuredHeight();
                }
                // 解決上拉加載更多自動滾動問題
                if (mCurrentDrag) {
                    scrollToPosition(getAdapter().getItemCount() - 1);
                }

                // 獲取手指觸摸拖拽的距離
                int distanceY = (int) ((e.getRawY() - mFingerDownY) * mDragIndex);
                // 如果是已經(jīng)到達(dá)頭部,并且不斷的向下拉,那么不斷的改變r(jià)efreshView的marginTop的值
                if (distanceY < 0) {
                    setLoadViewMarginBottom(-distanceY);
                    updateLoadStatus(-distanceY);
                    mCurrentDrag = true;
                    return true;
                }
                break;
        }

        return super.onTouchEvent(e);
    }

    /**
     * 更新加載的狀態(tài)
     */
    private void updateLoadStatus(int distanceY) {
        if (distanceY <= 0) {
            mCurrentLoadStatus = LOAD_STATUS_NORMAL;
        } else if (distanceY < mLoadViewHeight) {
            mCurrentLoadStatus = LOAD_STATUS_PULL_DOWN_REFRESH;
        } else {
            mCurrentLoadStatus = LOAD_STATUS_LOOSEN_LOADING;
        }

        if (mLoadCreator != null) {
            mLoadCreator.onPull(distanceY, mLoadViewHeight, mCurrentLoadStatus);
        }
    }

    /**
     * 添加底部加載更多View
     */
    private void addRefreshView() {
        Adapter adapter = getAdapter();
        if (adapter != null && mLoadCreator != null) {
            // 添加底部加載更多View
            View loadView = mLoadCreator.getLoadView(getContext(), this);
            if (loadView != null) {
                addFooterView(loadView);
                this.mLoadView = loadView;
            }
        }
    }

    /**
     * 設(shè)置加載View的marginBottom
     */
    public void setLoadViewMarginBottom(int marginBottom) {
        MarginLayoutParams params = (MarginLayoutParams) mLoadView.getLayoutParams();
        if (marginBottom < 0) {
            marginBottom = 0;
        }
        params.bottomMargin = marginBottom;
        mLoadView.setLayoutParams(params);
    }


    /**
     * @return Whether it is possible for the child view of this layout to
     * scroll up. Override this if the child view is a custom view.
     * 判斷是不是滾動到了最頂部,這個是從SwipeRefreshLayout里面copy過來的源代碼
     */
    public boolean canScrollDown() {
        return ViewCompat.canScrollVertically(this, 1);
    }

    /**
     * 停止加載更多
     */
    public void onStopLoad() {
        mCurrentLoadStatus = LOAD_STATUS_NORMAL;
        restoreLoadView();
        if (mLoadCreator != null) {
            mLoadCreator.onStopLoad();
        }
    }

    // 處理加載更多回調(diào)監(jiān)聽
    private OnLoadMoreListener mListener;

    public void setOnLoadMoreListener(OnLoadMoreListener listener) {
        this.mListener = listener;
    }

    public interface OnLoadMoreListener {
        void onLoad();
    }
}
這里寫圖片描述

3.3 增加一些基本通用功能

最后我們在這個基礎(chǔ)在增加一些基本的功能,如正在加載數(shù)據(jù)的頁面,或者數(shù)據(jù)是空的頁面,所以決定找一層最合適的方法去改,那就是我們上一期的WrapRecyclerView的基礎(chǔ)上去改,因?yàn)槟鞘俏覀傾dapter密切聯(lián)系的一層。

/**
 * Created by Darren on 2016/12/29.
 * Email: 240336124@qq.com
 * Description: 可以添加頭部和底部的RecyclerView
 */
public class WrapRecyclerView extends RecyclerView {
    // 增加一些通用功能
    // 空列表數(shù)據(jù)應(yīng)該顯示的空View
    // 正在加載數(shù)據(jù)頁面,也就是正在獲取后臺接口頁面
    private View mEmptyView, mLoadingView;

    // 省略...上一期已有代碼

    private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
        @Override
        public void onChanged() {
            if (mAdapter == null) return;
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒效果
            if (mWrapRecyclerAdapter != mAdapter)
                mWrapRecyclerAdapter.notifyDataSetChanged();

            dataChanged();
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            if (mAdapter == null) return;
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged沒效果
            if (mWrapRecyclerAdapter != mAdapter)
                mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
            dataChanged();
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            if (mAdapter == null) return;
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved沒效果
            if (mWrapRecyclerAdapter != mAdapter)
                mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
            dataChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            if (mAdapter == null) return;
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒效果
            if (mWrapRecyclerAdapter != mAdapter)
                mWrapRecyclerAdapter.notifyItemChanged(positionStart);
            dataChanged();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            if (mAdapter == null) return;
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged沒效果
            if (mWrapRecyclerAdapter != mAdapter)
                mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
            dataChanged();
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            if (mAdapter == null) return;
            // 觀察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted沒效果
            if (mWrapRecyclerAdapter != mAdapter)
                mWrapRecyclerAdapter.notifyItemInserted(positionStart);
            dataChanged();
        }
    };

    /**
     * 添加一個空列表數(shù)據(jù)頁面
     */
    public void addEmptyView(View emptyView) {
        this.mEmptyView = emptyView;
    }

    /**
     * 添加一個正在加載數(shù)據(jù)的頁面
     */
    public void addLoadingView(View loadingView) {
        this.mLoadingView = loadingView;
    }

    /**
     * Adapter數(shù)據(jù)改變的方法
     */
    private void dataChanged() {
        if (mAdapter.getItemCount() == 0) {
            // 沒有數(shù)據(jù)
            if (mEmptyView != null) {
                mEmptyView.setVisibility(VISIBLE);
            } else {
                mEmptyView.setVisibility(GONE);
            }
        }
    }
    // 省略...上一期已有代碼
}

上一期的代碼就已經(jīng)省略了RecyclerView更全解析之 - 為它優(yōu)雅的添加頭部和底部,到目前應(yīng)該所有的這些列表刷新和加載樣式都可以實(shí)現(xiàn),具體的一些要求可以自己修改修改。我這里就不在把它使用到具體的項(xiàng)目中了,我自己也用到了自己的項(xiàng)目中,之所以之前沒寫這一期的博客是因?yàn)樵谑褂玫倪^程中出現(xiàn)了一些Bug,所以才等到這個時候。

這里寫圖片描述

所有分享大綱:Android進(jìn)階之旅 - 自定義View篇

視頻講解地址:http://pan.baidu.com/s/1kUGvvwj

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容