RecyclerView + DataBinding实现的下拉刷新与加载更多

1、简介

RecyclerView的刷新与加载样式用到第三方库,添加依赖:

implementation 'com.jcodecraeer:xrecyclerview:1.5.9'

在使用RecyclerView来展示网络请求的数据的时候,常用到下拉刷新和加载更多,直接上效果图:


gif.gif
  • 项目中使用的接口来源:http://gank.io/api
  • 以下介绍并非图中体现的完整实现,部分Activity与Fragment等代码未贴出
  • 项目源码地址:https://github.com/fr1014/LoadAndRefresh
  • 项目实战:https://github.com/fr1014/konwledge

2、布局

2.1 gson数据解析

public class ItemBean {
    /**
     * _id : 5d2f24d19d2122031ea5223f
     * createdAt : 2019-07-17T13:38:25.504Z
     * desc : 状态切换,让View状态的切换和Activity彻底分离开。用builder模式来自由的添加需要的状态View,可以设置有数据,数据为空,加载数据错误,网络错误,加载中等多种状态,并且支持自定义状态的布局。
     * images : ["http://img.gank.io/4b63f35c-f631-417b-9d88-916e70901634","http://img.gank.io/b7de0a96-0023-4c4c-b120-9fcbf8c6046c","http://img.gank.io/45f8f9b5-0be1-410b-a50d-b8d72046abb2","http://img.gank.io/89a0e411-1049-4322-80a1-9bd8d6dfb35e","http://img.gank.io/f3e3b597-0317-45cd-8288-d866888ce297"]
     * publishedAt : 2019-07-17T13:40:33.502Z
     * source : web
     * type : Android
     * url : https://github.com/yangchong211/YCStateLayout/blob/master/README_CH.md
     * used : true
     * who : 潇湘剑雨
     */

    private String _id;
    private String createdAt;
    private String desc;
    private String publishedAt;
    private String source;
    private String type;
    private String url;
    private boolean used;
    private String who;
    private List images;

    public String get_id() {
        return _id;
    }

    public void set_id(String _id) {
        this._id = _id;
    }

    public String getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(String createdAt) {
        this.createdAt = createdAt;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getPublishedAt() {
        return publishedAt;
    }

    public void setPublishedAt(String publishedAt) {
        this.publishedAt = publishedAt;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public boolean isUsed() {
        return used;
    }

    public void setUsed(boolean used) {
        this.used = used;
    }

    public String getWho() {
        return who;
    }

    public void setWho(String who) {
        this.who = who;
    }

    public List getImages() {
        return images;
    }

    public void setImages(List images) {
        this.images = images;
    }
}
public class CategoryListBean {

    private boolean isError;

    private List results;

    public boolean isError() {
        return isError;
    }

    public List getResults() {
        return results;
    }

    public void setResults(List results) {
        this.results = results;
    }

}

2.2 数据源处理

public class CategoryBean extends BaseObservable {
    private ResultsBean bean;
    public ObservableField imageViewVisibility;
    private int mPosition;

    public CategoryBean(ResultsBean bean, int position) {
        this.bean = bean;
        this.mPosition = position;
        initData();

    }

    public String getDesc() {
        return bean.getDesc();
    }

    public List getImageUrls() {
        return bean.getImages();
    }

    public String getImageUrl() {
        if (bean.getImages()!=null)
            return bean.getImages().get(0);
        else
            imageViewVisibility.set(View.GONE);
        return null;
    }

    public Date getDate(){
        return Utils.StrToDate(bean.getPublishedAt());
    }

    public String getUrl(){
        return bean.getUrl();
    }

    public String getType(){
        return bean.getType();
    }

    private void initData() {
        imageViewVisibility = new ObservableField<>();
        imageViewVisibility.set(View.VISIBLE);
    }
}

2.3 RecyclerView的布局

fragment_classified_child.xml




    

            
    


2.4 RecyclerView中的item布局

item_categorybean.xml




    

        

        
    

    

        

            

            

            

            
        

        
    

3、接口实现

3.1 监听网络请求

在请求网络时,负责将各种状态下的信息传出

public interface BaseLoadListener {

    /**
     * 加载数据成功
     *
     * @param list
     */
    void loadSuccess(List list);

    /**
     * 加载失败
     *
     * @param message
     */
    void loadFailure(String message);

    /**
     * 开始加载
     */
    void loadStart();

    /**
     * 加载结束
     */
    void loadComplete();
}

3.2 UI消息展示

负责将加载状态展示给用户

public interface IBaseView {

    /**
     * 开始加载
     *
     * @param loadType 加载的类型 0:第一次记载 1:下拉刷新 2:上拉加载更多
     */
    void loadStart(int loadType);

    /**
     * 加载完成
     */
    void loadComplete();

    /**
     * 加载失败
     *
     * @param message
     */
    void loadFailure(String message);
}

4、适配器

4.1 适配器的基类实现

RecyclerView的ViewHolder基类

public class BaseViewHolder extends RecyclerView.ViewHolder {

    private VB mBinding;

    public BaseViewHolder(VB binding) {
        super(binding.getRoot());
        this.mBinding = binding;
    }

    public VB getBinding(){
        return mBinding;
    }
}

RecyclerView适配器的通用基类

public abstract class RVBaseAdapter extends RecyclerView.Adapter {

    protected Context mContext;
    protected List mList; // 数据源
    protected LayoutInflater inflater;

    public RVBaseAdapter(Context context) {
        this.mContext = context;
        this.mList = new ArrayList<>();
        inflater = LayoutInflater.from(context);
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    @NotNull
    @Override
    public VH onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
        return onCreateVH(parent, viewType);
    }

    @Override
    public void onBindViewHolder(@NotNull VH holder, int position) {
        onBindVH(holder, position);
    }

    /**
     * 创建 View Holder
     *
     * @param parent   parent
     * @param viewType item type
     * @return view holder
     */
    public abstract VH onCreateVH(ViewGroup parent, int viewType);

    /**
     * 绑定 View Holder
     *
     * @param holder   view holder
     * @param position position
     */
    public abstract void onBindVH(VH holder, int position);

    /**
     * 刷新数据
     *
     * @param data 数据源
     */
    public void refreshData(List data) {
        mList.clear();
        mList.addAll(data);
        notifyDataSetChanged();
    }

    /**
     * 加载更多
     *
     * @param data 加载的新数据
     */
    public void loadMoreData(List data) {
        mList.addAll(data);
        notifyDataSetChanged();
    }

}

4.2 适配器的实现

RecyclerView的适配器

public class RVCategoryAdapter extends RVBaseAdapter> {

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

    @Override
    public BaseViewHolder onCreateVH(ViewGroup parent, int viewType) {
        ViewDataBinding dataBinding = DataBindingUtil.inflate(inflater, R.layout.item_categorybean,parent,false);
        return new BaseViewHolder<>((ItemCategorybeanBinding) dataBinding);
    }

    @Override
    public void onBindVH(BaseViewHolder holder, int position) {
        ItemCategorybeanBinding binding = holder.getBinding();
        CategoryBean bean = new CategoryBean(mList.get(position), position);
        binding.setVariable(BR.categorybean,bean);
        binding.executePendingBindings();
    }

}

5、model类

网络请求的部分封装这里就不详写了,文末有项目源码地址,仅供参考。

 //获取分类干货
    @GET("data/{category}/{count}/{page}")
    Observable>> getCategoryBean(@Path("category") String category, @Path("count") int count, @Path("page") int page);
public class CategoryModel {

    public static void getCategoryListBean(String category, int count, int page, BaseLoadListener loadListener) {
        RetrofitManager
                .getRequest()
                .getCategoryBean(category, count, page)
                .compose(SchedulerProvider.getInstance().applySchedulers())
                .subscribe(new DisposableObserver>() {

                    @Override
                    protected void onStart() {
                        loadListener.loadStart();
                    }

                    @Override
                    public void onNext(Response categoryListBeanResponse) {
                        if (categoryListBeanResponse != null && !categoryListBeanResponse.body().isError()){
                            loadListener.loadSuccess(categoryListBeanResponse.body().getResults());
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        loadListener.loadFailure(e.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        loadListener.loadComplete();
                    }
                });
    }
}

5、ViewModel类

5.1 常量

public class MainConstant {

    public class LoadData{
        public static final int FIRST_LOAD = 0; //首次加载
        public static final int REFRESH = 1; //下拉刷新
        public static final int LOAD_MORE = 2; //上拉加载更多
    }
}

5.2 ViewModel基类

public abstract class BaseViewModel {
    protected T adapter;

    public BaseViewModel(T adapter){
        this.adapter = adapter;
    }

    protected abstract void getData();

}

5.3 ViewModel的实现

继承实现网络请求监听接口,判断UI的不同状态,做出第一次进入界面、刷新数据、加载更多的不同操作。

public class CategoryViewModel extends BaseViewModel implements BaseLoadListener {

    private ICategoryView mICategoryView;
    private String mCategory;
    private int mCurrPage = 1; //当前页面
    private int mLoadType; //加载数据的类型

    public CategoryViewModel(ICategoryView iCategoryView,RVCategoryAdapter adapter,String category) {
        super(adapter);
        this.mICategoryView = iCategoryView;
        this.mCategory = category;
        getData();
    }

    /**
     * 第一次获取分类数据
     */
    @Override
    protected void getData() {
        mLoadType = MainConstant.LoadData.FIRST_LOAD;
        CategoryModel.getCategoryListBean(mCategory,10,mCurrPage,this);
    }


    public void loadRefreshData(){
        mLoadType = MainConstant.LoadData.REFRESH;
        mCurrPage = 1;
        CategoryModel.getCategoryListBean(mCategory,30,mCurrPage,this);
    }

    public void loadMoreData(){
        mLoadType = MainConstant.LoadData.LOAD_MORE;
        mCurrPage++;
        CategoryModel.getCategoryListBean(mCategory,30,mCurrPage,this);
    }

    @Override
    public void loadSuccess(List list) {
        if (mCurrPage > 1){
            //上拉加载的数据
            adapter.loadMoreData(list);
        }else {
            //第一次加载或者下拉刷新的数据
            adapter.refreshData(list);
        }
    }

    @Override
    public void loadFailure(String message) {
        //加载失败后的提示
        if (mCurrPage > 1){
            //加载失败需要回到加载前的页面
            mCurrPage--;
        }
        mICategoryView.loadFailure(message);
    }

    @Override
    public void loadStart() {
        mICategoryView.loadStart(mLoadType);
    }

    @Override
    public void loadComplete() {
        mICategoryView.loadComplete();
    }
}

6、Fragment

6.1 Fragment基类

public abstract class BaseFragment extends Fragment {
    protected V binding;
    
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(inflater, initContentView(inflater, container, savedInstanceState), container, false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        initViewDataBinding();
        //页面数据初始化方法
        initData();
    }

    public abstract void initData();

    private void initViewDataBinding() {

    }

    public abstract int initContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
    
}

6.2、Fragment的实现

这里的仅为ClassifiedChildFragment的代码,Activity及ClassifiedChildFragment的父布局Fragment都未贴出。

public class ClassifiedChildFragment extends BaseFragmentVM implements ICategoryView, XRecyclerView.LoadingListener{
    private String mClassified;

    public ClassifiedChildFragment(String classified){
        this.mClassified = classified;
    }

    @Override
    public int initContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return R.layout.fragment_classified_child;
    }

    @Override
    public void initData() {
        binding.recyclerView.setRefreshProgressStyle(ProgressStyle.BallClipRotate); //设置下拉刷新的样式
        binding.recyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallClipRotate); //设置上拉加载更多的样式
        binding.recyclerView.setArrowImageView(R.mipmap.pull_down_arrow);
        binding.recyclerView.setLoadingListener(this);

        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        binding.recyclerView.setLayoutManager(layoutManager);

        RVCategoryAdapter mRVCategoryAdapter = new RVCategoryAdapter(getContext());
        binding.recyclerView.setAdapter(mRVCategoryAdapter);
        viewModel = new CategoryViewModel(this, mRVCategoryAdapter, mClassified);
    }

    @Override
    public void onRefresh() {
        //下拉刷新
        viewModel.loadRefreshData();
    }

    @Override
    public void onLoadMore() {
        //上拉加载更多
        viewModel.loadMoreData();
    }

    @Override
    public void loadStart(int loadType) {
        if (loadType == FIRST_LOAD) {
            DialogHelper.getInstance().show(getContext(), "加载中...");
        }
    }

    @Override
    public void loadComplete() {
        DialogHelper.getInstance().close();
        binding.recyclerView.refreshComplete();
        binding.recyclerView.loadMoreComplete();
    }

    @Override
    public void loadFailure(String message) {
        DialogHelper.getInstance().close();
        binding.recyclerView.loadMoreComplete();
        binding.recyclerView.refreshComplete();
        Utils.ToastShort(getContext(), message);
    }

}

6.3 DialogHelper

public class DialogHelper {
    public static DialogHelper getInstance() {
        return LoadDialogHolder.instance;
    }

    private static class LoadDialogHolder {
        static DialogHelper instance = new DialogHelper();
    }

    /**
     * 展示加载框
     *
     * @param context context
     * @param msg     加载信息
     */
    public void show(Context context, String msg) {
        close();
        createDialog(context, msg);
        if (progressDialog != null && !progressDialog.isShowing()) {
            progressDialog.show();
        }
    }

    /**
     * 关闭加载框
     */
    public void close() {
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }

    /**
     * progressDialog
     */
    private ProgressDialog progressDialog;

    /**
     * 创建加载框
     *
     * @param context context
     * @param msg     msg
     */
    private void createDialog(Context context, String msg) {
        progressDialog = new ProgressDialog(context);
        progressDialog.setCancelable(false);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.setMessage(msg);
        progressDialog.setOnCancelListener(dialog -> progressDialog = null);
    }
}

7、项目源码地址

https://github.com/fr1014/LoadAndRefresh

你可能感兴趣的:(RecyclerView + DataBinding实现的下拉刷新与加载更多)