1. 程式人生 > >自定義RecyclerView實現下拉重新整理和上拉載入(第一種實現方式)

自定義RecyclerView實現下拉重新整理和上拉載入(第一種實現方式)

說明:該自定義RecyclerView只適用於layoutManager為LinearLayoutManager的情況,使用的還是RecyclerView.Adapter。

效果圖

使用

1、編寫layout檔案

<?xml version="1.0" encoding="utf-8"?>
<com.shbj.refreashrvdemo.ui.RefreashRecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rv"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</com.shbj.refreashrvdemo.ui.RefreashRecyclerView>

2、定義HeaderView

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:visibility="visible"
        android:id="@+id/tv_refresh"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:gravity="center"
        android:text="下拉重新整理"
        android:textAllCaps="false"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <LinearLayout
        android:id="@+id/ll_refreshing"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:gravity="center"
        android:orientation="horizontal"
        android:visibility="invisible">

        <ProgressBar
            android:layout_width="30dp"
            android:layout_height="30dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:text="重新整理中..."
            android:textSize="15sp"/>
    </LinearLayout>
</FrameLayout>

3、定義FooterView

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_load"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="載入更多"
        android:textSize="15sp"/>

    <LinearLayout
        android:id="@+id/ll_loading"
        android:visibility="invisible"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:orientation="horizontal">

        <ProgressBar
            android:layout_width="30dp"
            android:layout_height="30dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="20dp"
            android:text="載入中..."
            android:textSize="15sp"/>
    </LinearLayout>

</FrameLayout>

4、Java程式碼實現

package com.shbj.refreashrvdemo;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.shbj.refreashrvdemo.ui.RefreashRecyclerView;

import java.util.ArrayList;

public class Demo1Fragment extends Fragment {

    private final String TAG = "Demo1Fragment";
    private ArrayList<String> mStrings;
    private final int TYPE_REFRESH = 1;
    private final int TYPE_LOAD = 2;
    private final int TYPE_NO_LOAD_MORE = 3;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case TYPE_REFRESH:
                    mRv.refreshEnd();//重新整理完成後需呼叫refreshEnd,更新HeaderView顯示狀態
                    mDemo1Adapter.setDatas(mStrings);
                    //由於自定義RecycleView中使用了自定義的adapter所以資料改變時需呼叫自定義adapter的notifyDataSetChanged
                    mRv.getAdapter().notifyDataSetChanged();
                    TextView tvHeader = mHeaderView.findViewById(R.id.tv_refresh);
                    LinearLayout llRefreshing = mHeaderView.findViewById(R.id.ll_refreshing);
                    tvHeader.setVisibility(View.VISIBLE);
                    llRefreshing.setVisibility(View.INVISIBLE);
                    break;
                case TYPE_LOAD:
                    mDemo1Adapter.setDatas(mStrings);
                    mRv.getAdapter().notifyDataSetChanged();
                    TextView tvLoad = mFooterView.findViewById(R.id.tv_load);
                    LinearLayout llLoading = mFooterView.findViewById(R.id.ll_loading);
                    tvLoad.setVisibility(View.VISIBLE);
                    llLoading.setVisibility(View.INVISIBLE);
                    break;
                case TYPE_NO_LOAD_MORE:
                    mRv.removeOnLoadMoreListener();//當沒有更多資料時可移除載入更多監聽
                    tvLoad = mFooterView.findViewById(R.id.tv_load);
                    llLoading = mFooterView.findViewById(R.id.ll_loading);
                    tvLoad.setVisibility(View.VISIBLE);
                    llLoading.setVisibility(View.INVISIBLE);
                    tvLoad.setText("沒有更多資料了");
                    break;
            }

        }
    };
    private RefreashRecyclerView mRv;
    private Demo1Adapter mDemo1Adapter;
    private View mHeaderView;
    private View mFooterView;
    private int mLoadCount;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view = View.inflate(getContext(), R.layout.fragment_demo1, null);
        mStrings = new ArrayList<>();
        for (int i = 0; i < 50; i++) {//初始資料
            mStrings.add("條目 " + i);
        }
        mRv = view.findViewById(R.id.rv);
        mRv.setLayoutManager(new LinearLayoutManager(getContext()));
        mRv.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
        mHeaderView = LayoutInflater.from(this.getContext()).inflate(R.layout.item_header, mRv, false);
        mRv.setHeaderView(mHeaderView);//設定HeaderView,需在setLayoutManager後設置否則會報錯
        mFooterView = LayoutInflater.from(this.getContext()).inflate(R.layout.item_footer, mRv, false);
        mRv.setFooterView(mFooterView);//設定FooterView
        mDemo1Adapter = new Demo1Adapter(getContext(), mStrings);
        mRv.setAdapter(mDemo1Adapter);//設定adapter,照樣繼承的是RecycleView中自帶的adapter
        mRv.setOnRefreshListener(new RefreashRecyclerView.OnRefreshListener() {//設定下拉重新整理監聽
            @Override
            public void onStartRefresh() {
                TextView tvHeader = mHeaderView.findViewById(R.id.tv_refresh);
                LinearLayout llRefreshing = mHeaderView.findViewById(R.id.ll_refreshing);
                tvHeader.setVisibility(View.INVISIBLE);
                llRefreshing.setVisibility(View.VISIBLE);
                refresh();//監聽到開始重新整理時呼叫重新整理函式
            }
        });
        mRv.setOnPullDownListener(new RefreashRecyclerView.OnPullDownListener() {//設定下拉過程的監聽
            @Override
            public void onPullDownProgress(float progress) {//獲取下拉的佔比,一些特殊要求可能會用到,比如下面實現了下拉過程中字型逐漸變大
                if (progress > 1) {
                    progress = 1;
                }
                TextView tvHeader = mHeaderView.findViewById(R.id.tv_refresh);
                tvHeader.setTextSize(15 + 20 * progress);
            }
        });
        mRv.setOnLoadMoreListener(new RefreashRecyclerView.OnLoadMoreListener() {//設定上拉載入更多的監聽
            @Override
            public void onLoadMoreStart() {
                TextView tvLoad = mFooterView.findViewById(R.id.tv_load);
                LinearLayout llLoading = mFooterView.findViewById(R.id.ll_loading);
                tvLoad.setVisibility(View.INVISIBLE);
                llLoading.setVisibility(View.VISIBLE);
                loadMore();//監聽到開始載入時呼叫載入更多函式
            }
        });
        return view;
    }

    private void refresh() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(3000);
                mStrings.clear();
                for (int i = 0; i < 50; i++) {
                    mStrings.add("欄目 " + i);
                }
                mHandler.sendEmptyMessage(TYPE_REFRESH);
            }
        }).start();
    }

    private void loadMore() {
        mLoadCount++;
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(3000);
                if (mLoadCount == 3) {//模擬沒有更多資料
                    mHandler.sendEmptyMessage(TYPE_NO_LOAD_MORE);
                } else {
                    for (int i = 0; i < 20; i++) {
                        mStrings.add("新欄目 " + i);
                    }
                    Log.d(TAG, "run: loadMore");
                    mHandler.sendEmptyMessage(TYPE_LOAD);
                }
            }
        }).start();
    }
}

5、定義adapter,使用的還是RecyclerView.Adapter

package com.shbj.refreashrvdemo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

public class Demo1Adapter extends RecyclerView.Adapter {
    private Context mContext;
    private List<String> mDatas;
    public Demo1Adapter(Context context, List<String> datas){
        mContext = context;
        mDatas = datas;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_normal, parent, false);
        RecyclerView.ViewHolder holder = new NormalHolder(view);
        return holder;
    }

    public void setDatas(List<String> datas){
        mDatas=datas;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((NormalHolder)holder).setData(position);
    }

    @Override
    public int getItemCount() {
        if (mDatas != null) {
            return mDatas.size();
        }
        return 0;
    }

    class NormalHolder extends RecyclerView.ViewHolder {
        private View mItemView;

        public NormalHolder(View itemView) {
            super(itemView);
            mItemView = itemView;
        }

        public void setData(int postion) {
            ((TextView) mItemView).setText(mDatas.get(postion));
        }
    }
}

自定義RecyclerView實現過程

1、在onlayout時獲取HeaderView的高度,並修改HeaderView的TopMargin值使其隱藏

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        Log.d(TAG, "onLayout: " + sHeaderViewMeasuredHeight);
        if (isFristLayout) {
            //將sHeaderViewMeasuredHeight設定為static,只要是為了防止頁面間切換出現bug
            if (sHeaderViewMeasuredHeight == 0) {//只有當第一次layout時才會為sHeaderViewMeasuredHeight賦值
                sHeaderViewMeasuredHeight = mHeaderView.getMeasuredHeight();//獲取HeaderView的高度
            }
            updateMargin(-sHeaderViewMeasuredHeight);//修改HeaderView的TopMargin值使其隱藏
        }
    }

2、重寫onTouchEvent,實現下拉重新整理和上拉載入的效果

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        //下拉重新整理處理
        if (mHeaderView == getChildAt(0)) {
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownX = e.getX();
                    mDownY = e.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float moveX = e.getX();
                    float moveY = e.getY();
                    mDx = moveX - mDownX;
                    mDy = moveY - mDownY;
                    if (Math.abs(mDy) > Math.abs(mDx) && mDy > 0) {
                        if (mDy < 2 * sHeaderViewMeasuredHeight) {//小於兩倍height時,逐漸修改TopMargin,使HeaderView逐漸顯現出來
                            updateMargin(-sHeaderViewMeasuredHeight + (int) mDy);
                            if (mOnPullDownListener != null) {
                                mOnPullDownListener.onPullDownProgress(mDy / sHeaderViewMeasuredHeight);//將佔比返回給監聽者
                            }
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    float upX = e.getX();
                    float upY = e.getY();
                    mDx = upX - mDownX;
                    mDy = upY - mDownY;
                    if (Math.abs(mDy) > Math.abs(mDx) && mDy > 0 && mHeaderView.getTop() > -sHeaderViewMeasuredHeight) {
                        if (mDy < sHeaderViewMeasuredHeight * 2 / 3) {//小於兩倍height時,TopMargin置為-sHeaderViewMeasuredHeight,使其隱藏
                            updateMargin(-sHeaderViewMeasuredHeight);
                        } else {//否則使其完全顯現
                            updateMargin(0);
                            if (mOnRefreshListener != null) {
                                //下拉釋放後開始重新整理
                                mOnRefreshListener.onStartRefresh();//通知監聽者開始重新整理
                            }
                        }
                    }
                    break;
            }
        }

        //上拉載入處理
        if (mFooterView == getChildAt(getChildCount() - 1)) {
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownX = e.getX();
                    mDownY = e.getY();
                    break;
                case MotionEvent.ACTION_UP:
                    float upX = e.getX();
                    float upY = e.getY();
                    mDx = upX - mDownX;
                    mDy = upY - mDownY;
                    if (Math.abs(mDy) > Math.abs(mDx) && mDy < -80) {//上拉載入時,mDy < -80可以通過此處調節載入的靈敏度,值越小上拉所需的距離約大
                        if (mOnLoadMoreListener != null) {
                            mOnLoadMoreListener.onLoadMoreStart();//通知監聽者開始載入
                        }
                    }
                    break;
            }
        }
        return super.onTouchEvent(e);
    }

3、重新整理完成後,處理邏輯

    //重新整理結束後呼叫該方法隱藏HeaderView
    public void refreshEnd() {
        if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
            updateMargin(-sHeaderViewMeasuredHeight);
        } else {
            mHeaderView.post(new Runnable() {
                @Override
                public void run() {
                    updateMargin(-sHeaderViewMeasuredHeight);
                }
            });
        }
    }

4、定義各類監聽介面

    //下拉重新整理的程序監聽,可以獲取到下拉過程中下拉的佔比
    private OnPullDownListener mOnPullDownListener;

    public void setOnPullDownListener(OnPullDownListener onPullDownListener) {
        mOnPullDownListener = onPullDownListener;
    }

    public interface OnPullDownListener {
        void onPullDownProgress(float progress);
    }

    //下拉重新整理的監聽
    private OnRefreshListener mOnRefreshListener;

    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        mOnRefreshListener = onRefreshListener;
    }

    public interface OnRefreshListener {
        void onStartRefresh();
    }

    //載入更多監聽
    private OnLoadMoreListener mOnLoadMoreListener;

    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
        mOnLoadMoreListener = onLoadMoreListener;
    }

    //如果沒有更多的載入專案可以移除OnLoadMoreListener
    public void removeOnLoadMoreListener() {
        mOnLoadMoreListener = null;
    }

    public interface OnLoadMoreListener {
        void onLoadMoreStart();
    }

5、設定HeaderView和FooterView

    public void setHeaderView(View headerView) {
        mHeaderView = headerView;
    }

    public void setFooterView(View footerView) {
        mFooterView = footerView;
    }

6、自定義Adapter

    //自定義adapter加入header和footer
    private class RefreashAdapter extends RecyclerView.Adapter {
        private final int HEADER_TYPE = 0;
        private final int NORMAL_TYPE = 1;
        private final int FOOTER_TYPE = 2;
        private Adapter mAdapter;

        public RefreashAdapter(Adapter adapter) {
            mAdapter = adapter;
        }

        @Override
        public int getItemViewType(int position) {
            int type = 0;
            if (position == 0) {
                type = HEADER_TYPE;
            } else if (position == getItemCount() - 1) {
                type = FOOTER_TYPE;
            } else {
                type = NORMAL_TYPE;
            }
            return type;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            RecyclerView.ViewHolder holder;
            switch (viewType) {
                case HEADER_TYPE:
                    holder = new HeaderHolder(mHeaderView);
                    break;
                case FOOTER_TYPE:
                    holder = new FooterHolder(mFooterView);
                    break;
                default:
                    holder = mAdapter.onCreateViewHolder(parent, viewType);
                    break;
            }
            return holder;
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            if (getItemViewType(position) == NORMAL_TYPE) {
                mAdapter.onBindViewHolder(holder, position - 1);
            }
        }

        @Override
        public int getItemCount() {
            return mAdapter.getItemCount() + 2;
        }

        class HeaderHolder extends RecyclerView.ViewHolder {
            public HeaderHolder(View itemView) {
                super(itemView);
            }
        }

        class FooterHolder extends RecyclerView.ViewHolder {
            public FooterHolder(View itemView) {
                super(itemView);
            }
        }
    }

原始碼地址

相關推薦

no