1. 程式人生 > >RecyclerView的重新整理分頁

RecyclerView的重新整理分頁

在開發中常常使用到重新整理分頁,這裡實現一個 RecyclerView 的簡單的重新整理分頁操作,測試效果見文末,實現過程參考如下:

實現思路

  1. 載入更多資料使用到 RecyclerView 載入多種佈局,根據 ViewType 判斷載入資料 Item 還是載入 FooterItem ;
  2. 通過執行緒模擬載入資料;
  3. 為 RecyclerView 新增 addOnScrollListener 事件來監聽使用者的滑動操作;
  4. 根據使用者滑動狀態以及具體情況開始載入資料
  5. 通知資料更新;

如何獲得 firstVisibleItemPosition

為了能夠在資料載入中動態判斷什麼時候載入資料,需要知道螢幕上顯示的第一個可見的 Item 的位置,當然了這裡使用的是佈局管理器是 LinearLayoutManager ,這樣查詢螢幕上第一個可見的 Item 就顯得容易多了,下面介紹一些 LinearLayoutManager 的四個方法:

findFirstVisibleItemPosition()

獲得螢幕上第一個可見 Item 的 position,只要該 Item 有一部分可見,那麼返回的 position 就是該Item 的 position。

findFirstCompletelyVisibleItemPosition()

獲得螢幕上第一個完整可見的 Item 的 position,只要該 Item 有一部分不可見,那麼返回的 position 就是該 Item 對應的下一個能顯示完整的 Item 的position。

findLastVisibleItemPosition()

獲得螢幕上最後一個可見 Item 的 position,只要該 Item 有一部分可見,那麼返回的 position 就是該Item 的 position。

findLastCompletelyVisibleItemPosition()

獲得螢幕上最後一個完整可見的 Item 的 position,只要該 Item 有一部分不可見,那麼返回的 position 就是該 Item 對應的上一個能顯示完整的 Item 的position。

準備資料

    /**
     * 初始化資料
     * @return
     */
    public void  initData(){
        for (int i=0;i<30;i++){
            arrayList.add("第"+i+"條資料");
        }
    }

    /**
     * 執行緒模擬載入資料
     */
    class LoadDataThread extends Thread{
        @Override
        public void run() {
            initData();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //通知主執行緒更新資料
            Message message = handler.obtainMessage();
            message.what = UPDATE_DATA;
            message.obj = arrayList;
            handler.sendMessage(message);
        }
    }

程式碼參考

主佈局


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.manu.mrecyclerview.MainActivity">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</LinearLayout>

Item佈局

/**item.xml**/
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp">
    <TextView
        android:id="@+id/tv_recycle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="data"
        android:background="#cac3c3"
        android:padding="10dp"
        android:textSize="20sp"/>
</LinearLayout>

/**item_footer.xml**/
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal">
    <ProgressBar
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar" />
    <TextView
        android:text="正在努力載入中,請稍後..."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView" />
</LinearLayout>

Adapter

這裡使用了 RecyclerView 根據不同的 ViewType 載入多種佈局的用法,使用時根據不同的佈局建立不同的 ViewHolder , 然後根據不同的 Viewholder 為對應的 Item 新增資料,注意 getItemViewType() 方法的用法,Adapter 程式碼參考如下:

/**
 * Created by jzman on 2017/6/04
 * RecycleView的Adapter
 */
public class RvAdapter1 extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements
        View.OnClickListener{
    private static final int ITEM_FOOTER = 0x1;
    private static final int ITEM_DATA = 0x2;
    private Context mContext;
    private RecyclerView recyclerView;
    private ArrayList<String> mList;

    public RvAdapter1() {}

    public RvAdapter1(Context mContext, ArrayList<String> mList) {
        this.mContext = mContext;
        this.mList = mList;
    }

    public void setmList(ArrayList<String> mList) {
        this.mList = mList;
    }

    /**
     * 用於建立ViewHolder
     * @param parent
     * @param viewTypez
     * @return
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view ;
        RecyclerView.ViewHolder vh = null;
        switch (viewType){
            case ITEM_DATA:
                view = LayoutInflater.from(mContext).inflate(R.layout.item,null);
                view.setOnClickListener(this);
                vh = new DataViewHolder(view);
                //使用程式碼設定寬高(xml佈局設定無效時)
                view.setLayoutParams(new ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT));
                break;
            case ITEM_FOOTER:
                view = LayoutInflater.from(mContext).inflate(R.layout.item_footer,null);
                //使用程式碼設定寬高(xml佈局設定無效時)
                vh = new FooterViewHolder(view);
                view.setLayoutParams(new ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT));
                break;
        }
        return vh;
    }

    /**
     * 獲取Item的View型別
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        //根據 Item 的 position 返回不同的 Viewtype
        if (position == (getItemCount())-1){
            return ITEM_FOOTER;
        }else{
            return ITEM_DATA;
        }
    }

    /**
     * 繫結資料
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof DataViewHolder){
            DataViewHolder dataViewHolder = (DataViewHolder) holder;
            dataViewHolder.tv_data.setText(mList.get(position));
        }else if (holder instanceof FooterViewHolder){

        }
    }

    /**
     * 選項總數
     * @return
     */
    @Override
    public int getItemCount() {
        return mList.size()+1;
    }

    @Override
    public void onClick(View view) {
        //根據RecyclerView獲得當前View的位置
        int position = recyclerView.getChildAdapterPosition(view);
        //程式執行到此,會去執行具體實現的onItemClick()方法
        if (onItemClickListener!=null){
            onItemClickListener.onItemClick(recyclerView,view,position,mList.get(position));
        }
    }

    /**
     * 建立ViewHolder
     */
    public static class DataViewHolder extends RecyclerView.ViewHolder{
        TextView tv_data;
        public DataViewHolder(View itemView) {
            super(itemView);
            tv_data = (TextView) itemView.findViewById(R.id.tv_recycle);
        }
    }

    /**
     * 建立footer的ViewHolder
     */
    public static class FooterViewHolder extends RecyclerView.ViewHolder{
        public FooterViewHolder(View itemView) {
            super(itemView);
        }
    }

    private OnItemClickListener onItemClickListener;
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = onItemClickListener;
    }

    /**
     * 定義RecyclerView選項單擊事件的回撥介面
     */
    public interface OnItemClickListener{
        //引數(父元件,當前單擊的View,單擊的View的位置,資料)
        void onItemClick(RecyclerView parent,View view, int position, String data);
    }
    /**
     *   將RecycleView附加到Adapter上
     */
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        this.recyclerView= recyclerView;
    }
    /**
     *   將RecycleView從Adapter解除
     */
    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        this.recyclerView = null;
    }
}

MainActivity

這裡主要注意 rv.addOnScrollListener(new OnScrollListener() …裡面的具體實現,MainActivity 程式碼參考如下:

/**
 * Created by jzman on 2017/6/04 0013.
 */
public class MainActivity extends AppCompatActivity {
    private static final int UPDATE_DATA = 0x3;

    private RecyclerView rv;
    RvAdapter1 adapter;

    private ArrayList<String> arrayList = new ArrayList<>();
    //載入更多資料時最後一項的索引
    private int lastLoadDataItemPosition;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rv = (RecyclerView) findViewById(R.id.rv);

        //設定佈局管理器
        rv.setLayoutManager(new LinearLayoutManager(this));//線性
//        rv.setLayoutManager(new GridLayoutManager(this,4));//線性
//        rv.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));//線性
        initData();
        adapter = new RvAdapter1(this,arrayList);
        adapter.setOnItemClickListener(new RvAdapter1.OnItemClickListener() {
            @Override
            public void onItemClick(RecyclerView parent, View view, int position, String data) {
                Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        });

        rv.addOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                if (newState == SCROLL_STATE_IDLE &&
                        lastLoadDataItemPosition == adapter.getItemCount()){
                    new LoadDataThread().start();
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

                LayoutManager layoutManager = recyclerView.getLayoutManager();
                if (layoutManager instanceof LinearLayoutManager){
                    LinearLayoutManager manager = (LinearLayoutManager) layoutManager;
                    int firstVisibleItem = manager.findFirstVisibleItemPosition();
                    int l = manager.findLastCompletelyVisibleItemPosition();
                    lastLoadDataItemPosition = firstVisibleItem+(l-firstVisibleItem)+1;
                }
            }
        });

        rv.setAdapter(adapter);
    }

    /**
     * 初始化資料
     * @return
     */
    public void  initData(){
        for (int i=0;i<25;i++){
            arrayList.add("第"+i+"條資料");
        }
    }

    /**
     * 執行緒模擬載入資料
     */
    class LoadDataThread extends Thread{
        @Override
        public void run() {
            initData();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message message = handler.obtainMessage();
            message.what = UPDATE_DATA;
            message.obj = arrayList;
            handler.sendMessage(message);
        }
    }

    private Handler handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case UPDATE_DATA:
                    arrayList = (ArrayList<String>) msg.obj;
                    adapter.setmList(arrayList);
                    adapter.notifyDataSetChanged();
                    break;
            }
        }
    };
}

測試效果

jzman-blog

可關注微信公眾號:jzman-blog 獲取最新文章更新,一起交流學習!

這裡寫圖片描述