1、简介
RecyclerView的刷新与加载样式用到第三方库,添加依赖:
implementation 'com.jcodecraeer:xrecyclerview:1.5.9'
在使用RecyclerView来展示网络请求的数据的时候,常用到下拉刷新和加载更多,直接上效果图:
screen.gif
- 项目中使用的接口来源:http://gank.io/api
- 以下介绍并非图中体现的完整实现,部分Activity与Fragment等代码未贴出
- 项目源码地址:https://github.com/fr1014/konwledge
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);
}
}
网友评论