美文网首页高级UI
RecyclerView + DataBinding实现的下拉刷

RecyclerView + DataBinding实现的下拉刷

作者: 云烟渐成雨 | 来源:发表于2019-07-23 22:49 被阅读61次

1、简介

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

implementation 'com.jcodecraeer:xrecyclerview:1.5.9'

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


screen.gif

2、布局

2.1 gson数据解析

public class CategoryListBean {

    private boolean error;
    private List<ResultsBean> results;

    public boolean isError() {
        return error;
    }

    public void setError(boolean error) {
        this.error = error;
    }

    public List<ResultsBean> getResults() {
        return results;
    }

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

    public static class ResultsBean {

        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<String> 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<String> getImages() {
            return images;
        }

        public void setImages(List<String> images) {
            this.images = images;
        }
    }
}

2.2 数据源处理

public class CategoryBean extends BaseObservable {
    private ResultsBean bean;
    public ObservableField<Integer> 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<String> 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

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

            <com.jcodecraeer.xrecyclerview.XRecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    </RelativeLayout>

</layout>

2.4 RecyclerView中的item布局

item_categorybean.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="categorybean"
            type="com.fr.konwledge.bean.CategoryBean" />

        <variable
            name="date"
            type="java.util.Date" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="95dp">

        <RelativeLayout
            android:id="@+id/list_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/selectableItemBackground"
            android:padding="8dp">

            <TextView
                android:id="@+id/desc"
                android:layout_toStartOf="@id/image_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentStart="true"
                android:ellipsize="end"
                android:maxLines="3"
                android:text="@{categorybean.desc}"
                android:textColor="@color/text_color" />

            <TextView
                android:id="@+id/type"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:text="@{categorybean.type}"
                android:textColor="@color/text_color" />

            <TextView
                android:id="@+id/date"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginLeft="80dp"
                android:layout_toEndOf="@id/type"
                android:text="@{categorybean.date}"
                android:textColor="@color/text_color" />

            <ImageView
            android:id="@+id/image_view"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_centerVertical="true"
            android:layout_margin="4dp"
            android:visibility="@{categorybean.imageViewVisibility}"
            android:src="@drawable/ic_launcher_background"
            app:imageUrl="@{categorybean.imageUrl}" />
        </RelativeLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_alignParentBottom="true"
            android:background="@color/black_view" />
    </RelativeLayout>
</layout>

3、接口实现

3.1 监听网络请求

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

public interface BaseLoadListener<T> {

    /**
     * 加载数据成功
     *
     * @param list
     */
    void loadSuccess(List<T> 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<VB extends ViewDataBinding> 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<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {

    protected Context mContext;
    protected List<T> 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<T> data) {
        mList.clear();
        mList.addAll(data);
        notifyDataSetChanged();
    }

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

}

4.2 适配器的实现

RecyclerView的适配器

public class RVCategoryAdapter extends RVBaseAdapter<ResultsBean,BaseViewHolder<ItemCategorybeanBinding>> {

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

    @Override
    public BaseViewHolder<ItemCategorybeanBinding> 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<ItemCategorybeanBinding> 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<Response<CategoryListBean>> 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<ResultsBean> loadListener) {
        RetrofitManager
                .getRequest()
                .getCategoryBean(category, count, page)
                .compose(SchedulerProvider.getInstance().applySchedulers())
                .subscribe(new DisposableObserver<Response<CategoryListBean>>() {

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

                    @Override
                    public void onNext(Response<CategoryListBean> 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<T> {
    protected T adapter;

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

    protected abstract void getData();

}

5.3 ViewModel的实现

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

public class CategoryViewModel extends BaseViewModel<RVCategoryAdapter> implements BaseLoadListener<ResultsBean> {

    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<ResultsBean> 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<V extends ViewDataBinding> 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<FragmentClassifiedChildBinding,CategoryViewModel> 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/konwledge

相关文章

网友评论

    本文标题:RecyclerView + DataBinding实现的下拉刷

    本文链接:https://www.haomeiwen.com/subject/glmzlctx.html