天天看點

RecyclerView 下拉重新整理上拉加載更多

近期我重新封裝了RecyclerView 下拉重新整理上拉加載更多,最新連結可以看這裡http://blog.csdn.net/zly921112/article/details/53432959

這幾天研究了下RecyclerView的使用和封裝,發現還是蠻好用的,現在把學習成果分享給大家,主要是關于上拉加載更多的封裝,之是以沒有封裝下拉重新整理也是因為SwipeRefreshLayout的存在,并且它更加符合MD風格.

注意該封裝隻适用于類似listview這種清單形式的

附上效果

RecyclerView 下拉重新整理上拉加載更多

先來看看activity中使用該控件的代碼

package com.byzk.www.recyclerviewpackage;

import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private List<String> dataList = new ArrayList<>();
    private RefreshRecyclerView rv;
    private MyAdapter myAdapter;
    private SwipeRefreshLayout srl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initData();
        initListener();
    }

    private void initView() {
        srl = (SwipeRefreshLayout) findViewById(R.id.srl);
        srl.setColorSchemeResources(android.R.color.holo_red_light, android.R.color.holo_blue_light, android.R.color.holo_green_light);
        rv = (RefreshRecyclerView) findViewById(R.id.rv);
        rv.setLayoutManager(new LinearLayoutManager(this));
        rv.setLoadMoreEnable(true);//允許加載更多
        rv.setFooterResource(R.layout.item_footer);//設定腳布局
        myAdapter = new MyAdapter(dataList);
        rv.setAdapter(myAdapter);
    }

    private void initData() {
        for (int i = 0; i < 20; i++) {
            dataList.add("資料" + i);
        }
        rv.notifyData();
    }

    private void initListener() {
        rv.setOnLoadMoreListener(new RefreshRecyclerView.OnLoadMoreListener() {
            @Override
            public void loadMoreListener() {
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 10; i++) {
                            dataList.add("更多資料" + i);
                        }
                        rv.notifyData();//重新整理資料
                    }
                }, 2000);
            }
        });
        srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        srl.setRefreshing(false);
                        dataList.add(0, "最新資料");
                        rv.notifyData();//重新整理資料
                    }
                }, 2000);
            }
        });
    }


    private Handler handler = new Handler();


}
           

可以發現activity中,recyclerview的寫法沒有太大變化,隻是有如下幾點需要注意

1.如果想要加載更多需要調用(必須)

rv.setLoadMoreEnable(true)

2.如果希望加載更多時候有腳布局提示需要調用(不是必須的)

rv.setFooterResource(int res)

3.實作加載更多回調(必須)

rv.setOnLoadMoreListener()

4.重新整理資料調用(必須)

rv.notifyData()

activity布局代碼

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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.byzk.www.recyclerviewpackage.MainActivity">
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <com.byzk.www.recyclerviewpackage.RefreshRecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </android.support.v4.widget.SwipeRefreshLayout>


</RelativeLayout>
           

下拉重新整理用的是v4包中的SwipeRefreshLayout直接包裹在自己封裝的RecyclerView的外層,上拉加載更多用的是自己封裝的RecyclerView.

前方高能,接下來有一大波代碼來襲,核心代碼RecyclerView的封裝如下

package com.byzk.www.recyclerviewpackage;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import jp.wasabeef.recyclerview.animators.adapters.SlideInBottomAnimationAdapter;

/**
 * Author: zhuliyuan
 * Time: 下午 3:26
 * 将腳布局放在最後一個條目,當加載更多的時候顯示,加載完成的時候隐藏
 *
 */

public class RefreshRecyclerView extends RecyclerView {

    private AutoLoadAdapter autoLoadAdapter;

    public RefreshRecyclerView(Context context) {
        this(context, null);
    }

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

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

    private boolean isLoadingMore = false;//是否正在加載更多
    private OnLoadMoreListener loadMoreListener;//加載資料監聽
    private boolean loadMoreEnable = false;//是否允許加載更多
    private int footerResource = -1;//腳布局
    private boolean footer_visible = false;//腳部是否可以見
    private void init() {
        setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                if (getAdapter() != null && getLayoutManager() != null) {
                    int lastVisiblePosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
                    int itemCount = getAdapter().getItemCount();
                    /**
                     * 控制下拉重新整理回調
                     * itemCount != 0 排除沒有資料情況
                     * lastVisiblePosition + 4 >= itemCount - 1 最後可見+4 >= 總條數 加載更多
                     * distanceY < 0 為上拉的時候才重新整理
                     */
                    if (distanceY < 0 && itemCount != 0 && lastVisiblePosition + 4 >= itemCount - 1 && !isLoadingMore && loadMoreEnable) {
                        Log.i("test","加載更多");
                        //正在加載更多
                        loading();
                        if (footerResource != -1){//有腳布局
                            //顯示腳布局
                            footer_visible = true;
                            getAdapter().notifyItemChanged(itemCount - 1);
                        }
                        if (loadMoreListener != null) {
                            loadMoreListener.loadMoreListener();
                        }
                    }
                }
            }
        });
    }

    /**
     * 判斷滑動方向
     */
    private float distanceY = 0;
    float startY = 0;
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        float y = ev.getRawY();
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                startY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                distanceY = y - startY;
                startY = y;
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        SlideInBottomAnimationAdapter slideInBottomAnimationAdapter = new SlideInBottomAnimationAdapter(adapter);
        slideInBottomAnimationAdapter.setDuration(600);
        autoLoadAdapter = new AutoLoadAdapter(slideInBottomAnimationAdapter);//添加動畫
        super.setAdapter(autoLoadAdapter);
    }

    /**
     * 設定是否允許加載更多
     *
     * @param isEnable
     */
    public void setLoadMoreEnable(boolean isEnable) {
        this.loadMoreEnable = isEnable;
    }

    /**
     * 設定腳布局
     */
    public void setFooterResource(int footerResource) {
        this.footerResource = footerResource;
    }


    /**
     * 加載完成
     */
    private void loadMoreComplete() {
        this.isLoadingMore = false;
    }

    /**
     * 正在重新整理
     */
    private void loading(){
        this.isLoadingMore = true;//設定正在重新整理
    }

    /**
     * 加載更多資料回調
     *
     * @param listener
     */
    public void setOnLoadMoreListener(OnLoadMoreListener listener) {
        this.loadMoreListener = listener;
    }

    public interface OnLoadMoreListener {
        void loadMoreListener();//上拉重新整理
    }


    /**
     * 重新整理資料
     */
    public void notifyData() {
        if (getAdapter() != null) {
            loadMoreComplete();
            if(footerResource != -1 && loadMoreEnable){
                //隐藏腳布局
                footer_visible = false;
            }
            getAdapter().notifyDataSetChanged();

        }
    }

    public class AutoLoadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        private Adapter dataAdapter;//資料adapter
        private final int TYPE_FOOTER = Integer.MAX_VALUE;//底部布局

        public AutoLoadAdapter(RecyclerView.Adapter adapter) {
            this.dataAdapter = adapter;
        }

        @Override
        public int getItemViewType(int position) {
            if (position == getItemCount() - 1 && loadMoreEnable && footerResource != -1 && footer_visible) {
                return TYPE_FOOTER;
            }
            if (dataAdapter.getItemViewType(position) == TYPE_FOOTER) {
                throw new RuntimeException("adapter中itemType不能為:" + Integer.MAX_VALUE);
            }
            return dataAdapter.getItemViewType(position);

        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            ViewHolder holder = null;
            if (viewType == TYPE_FOOTER) {//腳部
                holder = new FooterViewHolder(LayoutInflater.from(getContext()).inflate(footerResource, parent, false));
            } else {//資料
                holder = dataAdapter.onCreateViewHolder(parent, viewType);
            }
            return holder;
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            int itemViewType = getItemViewType(position);
            if (itemViewType != TYPE_FOOTER) {
                dataAdapter.onBindViewHolder(holder, position);
            }
        }

        @Override
        public int getItemCount() {
            if (dataAdapter.getItemCount() != 0) {
                int count = dataAdapter.getItemCount();
                if (loadMoreEnable && footerResource != -1 && footer_visible) {
                    count++;
                }
                return count;
            }
            return 0;
        }

        public class FooterViewHolder extends RecyclerView.ViewHolder {

            public FooterViewHolder(View itemView) {
                super(itemView);
            }
        }

    }

}
           

這裡封裝了上拉加載更多,自己寫了個adpter殼子,當允許上拉加載更多并且設定了腳布局的時候将腳布局加載到最後一個條目,根據最後可見條目位置與總條數的比較來回調加載更多重新整理,其他時候資料都來源于自己寫的DataAdapter,下拉重新整理可以按同樣原理實作為了MD設計風格,這裡我就直接套的SwipeRefreshLayout了.

提供資料Adapter如下

package com.byzk.www.recyclerviewpackage;

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;

/**
 * Author: zhuliyuan
 * Time: 下午 5:36
 */

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {


    private List<String> dataList;

    public MyAdapter(List<String> dataList) {
        this.dataList = dataList;
    }

    @Override
    public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        MyViewHolder myViewHolder = new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false));
        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(MyAdapter.MyViewHolder holder, int position) {
        holder.tv.setText(dataList.get(position));
    }

    public class MyViewHolder extends RecyclerView.ViewHolder{

        private TextView tv;

        public MyViewHolder(View itemView) {
            super(itemView);
            tv = (TextView) itemView.findViewById(R.id.tv);
        }
    }

    @Override
    public int getItemCount() {
        return (dataList == null || dataList.size() == 0)?0:dataList.size();
    }

}
           

item布局如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="10dp"
    android:clickable="true"
    android:foreground="?attr/selectableItemBackground"
    card_view:cardBackgroundColor="#0094ff"
    card_view:cardCornerRadius="6dp"
    card_view:cardElevation="5dp">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="哈哈哈"
        android:textColor="#fff" />
</android.support.v7.widget.CardView>
           

提供資料adapter和item布局都是普通寫法,這裡不過多贅言

源碼傳送門

項目中recyclerview動畫位址https://github.com/wasabeef/recyclerview-animators

到此封裝完成,,如果有bug或者建議歡迎留言.

繼續閱讀