本来不想写这篇文章的,但是本人在学习的时候遇到一个问题,那就是paging框架只要有一次请求失败,那么他就不会再出发自动请求加载下一页的回调,事先在网上找了一圈,简书,csdn里面查了半天,也给博主们私信了,但是没有任何结果,这方面的东西太少了,所以想分享一下。顺便简单测试下删除添加等。
DataBinding
jetpack是个全家桶,有数据库room,也有很好的分页控件paging,还有DataBinding等。本人还在学习中,小菜鸡路过......
这个示例中用到了DataBinding,我们先通过一个简单示例了解一下他。
先说布局文件
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.easyar.lib_network.bean.VersionBean"/>
<import type="com.easyar.jetpackdeme.MainActivity"/>
<import type="com.easyar.jetpackdeme.tools.TestIntent"/>
<variable
name="versionBean"
type="VersionBean" />
<variable
name="testIntent"
type="TestIntent" />
<variable
name="mainActivity"
type="MainActivity" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{versionBean==null?testIntent.tipsNull:versionBean.getVersion()}"
android:textColor="#333"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#333"
android:textSize="18sp"
android:text="@{versionBean==null?testIntent.tipsNull:versionBean.getTips()}"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请求"
android:onClick="@{()->mainActivity.click()}"/>
</LinearLayout>
</layout>
layout、data等标签不需要自己手动敲,只要在原始布局的跟布局点击 alt+回车 就能自动生成
image.png
然后,绑定数据,比如TextView绑定的文字来自Javabean中,那么就要引入相应的类,如上面的VersionBean类,先用import 导入然后在variable标签中添加使用时候的属性名。然后绑定数据的时候采用@{ }的形势有点像lambda表达式。这里为了能注册android:onClick属性,我也把MainActivity加进来了。然后在activity页面就能监听点击事件了,不用像以前一样setOnclick了。
public class MainActivity extends AppCompatActivity implements ViewModelStoreOwner {
private VersionViewModle mVersionViewModle;
private MutableLiveData<VersionBean> liveData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivityMainBindingImpl activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
activityMainBinding.setLifecycleOwner(this);
activityMainBinding.setMainActivity(this);
ViewModelProvider mModelProvider = new ViewModelProvider(this, new ViewModelProvider.Factory() {
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new VersionViewModle();
}
});
mVersionViewModle = mModelProvider.get(VersionViewModle.class);
liveData = mVersionViewModle.getLiveData();
liveData.observe(this, new Observer<VersionBean>() {
@Override
public void onChanged(VersionBean versionBean) {
activityMainBinding.setVersionBean(versionBean);
}
});
}
public void click(){
Log.d("yanjin","requestMain");
Call<VersionBean> versionMessage = RequestCenter.mRequestAPI.getVersionMessage();
versionMessage.enqueue(new Callback<VersionBean>() {
@Override
public void onResponse(Call<VersionBean> call, Response<VersionBean> response) {
VersionBean body = response.body();
Log.d("yanjin","requestMain---"+body.toString());
mVersionViewModle.getLiveData().setValue(body);
}
@Override
public void onFailure(Call<VersionBean> call, Throwable t) {
}
});
}
}
有些同学看到这个代码,可能会问setContentView();去哪了?没错DataBindingUtil做了一件大事,那就是他接管了对view的管理,DataBindingUtil.setContentView(this, R.layout.activity_main);就能获得DataBinding对象注意这里我们的activity名字是MainActivity所以他的DataBinding名字是ActivityMainBindingImpl,掌握好规律不要找错了。ActivityMainBindingImpl 这个类在你将布局转换成databading布局的时候就已经生成。
然后设置我们布局中要的参数一种一个就是activity,activityMainBinding.setMainActivity(this);然后ViewModelProvider的获取就是模板代码。然后在liveData.observe的onChanged里面设置会变动的数据。VersionViewModle是继承于ViewModel的类
public class VersionViewModle extends ViewModel {
private MutableLiveData<VersionBean> liveData = new MutableLiveData<>();
public MutableLiveData<VersionBean> getLiveData() {
return liveData;
}
public void setLiveData(VersionBean versionBean) {
this.liveData.setValue(versionBean);
}
}
他主要是来管理数据的。
下面的RequestCenter是基于retorfit的网络请求,在成功的时候重新设置了数据。最后是数据bean类
public class VersionBean {
/**
* version : 1
* tips : 增加新功能
* url : http://47.103.195.133/mai_1.1.apk
*/
private int version;
private String tips;
private String url;
public String getVersion() {
return version+"";
}
public void setVersion(int version) {
this.version = version;
}
public String getTips() {
return tips;
}
public void setTips(String tips) {
this.tips = tips;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "VersionBean{" +
"version=" + version +
", tips='" + tips + '\'' +
", url='" + url + '\'' +
'}';
}
}
很简单的测试了文字的设置。
paging
先来看看布局
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.easyar.jetpackdeme.list.view.activity.ImageListActivity"/>
<variable
name="imageListActivity"
type="ImageListActivity" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".list.view.activity.ImageListActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout
android:id="@+id/buttom_btns"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:visibility="gone"
android:layout_alignParentBottom="true">
<Button
android:id="@+id/btn_add"
android:layout_width="0dp"
android:layout_weight="1"
android:text="增加"
android:onClick="@{()->imageListActivity.addAItem()}"
android:layout_height="match_parent"/>
<Button
android:id="@+id/btn_delete"
android:layout_width="0dp"
android:layout_weight="1"
android:text="删除"
android:onClick="@{()->imageListActivity.deleteAItem()}"
android:layout_height="match_parent"/>
</LinearLayout>
</RelativeLayout>
</layout>
很简单一个recyclerview一个底部增加和删除按钮,老规矩,如果要android:onClick那么就要加入相应的类。这里我们把click绑定在activity里面
public class ImageListActivity extends AppCompatActivity implements ImageListViewHelper.DataChangedCallBack {
private ActivityImageListBinding mBinding;
private RecyclerView mRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
private ImageListPagedAdapter mAdapter;
private ImageListViewHelper mImageListViewHelper;
private View mHeaderView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_image_list);
mBinding.setImageListActivity(this);
mBinding.setLifecycleOwner(this);
initView();
}
private void initView() {
mRecyclerView = mBinding.recyclerView;
mLinearLayoutManager = new LinearLayoutManager(this);
mLinearLayoutManager.setOrientation(RecyclerView.VERTICAL);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mAdapter = new ImageListPagedAdapter();
mRecyclerView.setAdapter(mAdapter);
mImageListViewHelper = new ImageListViewHelper(mRecyclerView,mAdapter);
mImageListViewHelper.createPagedListLiveData(this,this);
mHeaderView = LayoutInflater.from(ImageListActivity.this).inflate(R.layout.header_item_layout, mRecyclerView, false);
}
@Override
public void onChanged(List<ImageBean.DataBean> data) {
if(data == null || data.size()==0){
mBinding.buttomBtns.setVisibility(View.GONE);
return;
}else{
mAdapter.addHeaderView(mHeaderView);
mBinding.buttomBtns.setVisibility(View.VISIBLE);
}
}
/**
* 测试添加一条数据
*/
public void addAItem(){
mImageListViewHelper.addAItem();
}
public void deleteAItem(){
mImageListViewHelper.deleteAItem();
}
}
这里我们尽量不要在activity中写太多东西,所以对paging的操作统一放到了ImageListViewHelper中。activity主要留了对recyclerview的操作。
然后封装一个adapter的抽象类,支持添加头部和底部的那种
public abstract class AbsPagedListAdapter<T,VH extends RecyclerView.ViewHolder> extends PagedListAdapter<T,VH> {
private SparseArray<View> mHeader = new SparseArray<>();
private SparseArray<View> mFooter = new SparseArray<>();
private int BASE_ITEM_HEADER_RANGE=10000;
private int BASE_ITEM_FOOTER_RANGE=20000;
public AbsPagedListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
super(diffCallback);
}
public void addHeaderView(View view){
if(mHeader.indexOfValue(view)<0){
mHeader.put(BASE_ITEM_HEADER_RANGE++,view);
notifyDataSetChanged();
}
}
public void addEmptyView(View view) {
if(mHeader.size()>0){
mHeader.clear();
}
addHeaderView(view);
notifyDataSetChanged();
}
public void removeHeaderView(View view){
int key = mHeader.indexOfValue(view);
if(key>=0){
mHeader.removeAt(key);
notifyDataSetChanged();
}
}
public void removeEmptyView(View view) {
removeHeaderView(view);
}
public void addFooterView(View view){
if(mFooter.indexOfValue(view)<0){
mFooter.put(BASE_ITEM_FOOTER_RANGE++,view);
notifyDataSetChanged();
}
}
@Override
public int getItemCount() {
int itemCount = super.getItemCount();
return itemCount+mHeader.size()+mFooter.size();
}
//获取真实条目数量
public int getOriginalItemCount(){
return getItemCount() - mHeader.size() - mFooter.size();
}
@Override
public int getItemViewType(int position) {
if(isHeaderView(position)){
return mHeader.keyAt(position);
}
if(isFooterView(position)){
position = position - getOriginalItemCount()-mHeader.size();
return mFooter.keyAt(position);
}
//正常条目,potion需要重新计算
position = position - mHeader.size();
return getItemViewType2(position);
}
private int getItemViewType2(int position) {
return 0;
}
private boolean isFooterView(int position) {
return position>=getOriginalItemCount()+mHeader.size();
}
private boolean isHeaderView(int position) {
return position<=mHeader.size()-1;
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if(mHeader.indexOfKey(viewType)>=0){
View view = mHeader.get(viewType);
return (VH) new RecyclerView.ViewHolder(view){};
}
if(mFooter.indexOfKey(viewType)>=0){
View view = mFooter.get(viewType);
return (VH) new RecyclerView.ViewHolder(view){};
}
return onCreateViewHolder2(parent,viewType);
}
protected abstract VH onCreateViewHolder2(ViewGroup parent, int viewType);
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
//头部和底部不能参与绑定数据
if(isHeaderView(position) || isFooterView(position)){
return;
}
position = position - mHeader.size();
onBindViewHolder2(holder,position);
}
protected abstract void onBindViewHolder2(VH holder, int position);
@Override
public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
super.registerAdapterDataObserver(new MyObserver(observer));
}
private class MyObserver extends RecyclerView.AdapterDataObserver{
private RecyclerView.AdapterDataObserver observer;
public MyObserver(RecyclerView.AdapterDataObserver observer) {
this.observer = observer;
}
public void onChanged() {
// Do nothing
observer.onChanged();
}
public void onItemRangeChanged(int positionStart, int itemCount) {
// do nothing
observer.onItemRangeChanged(positionStart+mHeader.size(),itemCount);
}
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
observer.onItemRangeChanged(positionStart+mHeader.size(),itemCount,payload);
}
public void onItemRangeInserted(int positionStart, int itemCount) {
// do nothing
observer.onItemRangeInserted(positionStart+mHeader.size(),itemCount);
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// do nothing
observer.onItemRangeRemoved(positionStart+mHeader.size(),itemCount);
}
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
// do nothing
observer.onItemRangeMoved(fromPosition+mHeader.size(),toPosition+mHeader.size(),itemCount);
}
}
}
以前觉得封装这些东西挺麻烦的,操作多了后发现是一步步来就行的。先安排两个容器装头部和底部
private SparseArray<View> mHeader = new SparseArray<>();
private SparseArray<View> mFooter = new SparseArray<>();
然后开拓一些方法操作view,并且操作完都要刷新下adapter
public void addHeaderView(View view){
if(mHeader.indexOfValue(view)<0){
mHeader.put(BASE_ITEM_HEADER_RANGE++,view);
notifyDataSetChanged();
}
}
public void addEmptyView(View view) {
if(mHeader.size()>0){
mHeader.clear();
}
addHeaderView(view);
notifyDataSetChanged();
}
public void removeHeaderView(View view){
int key = mHeader.indexOfValue(view);
if(key>=0){
mHeader.removeAt(key);
notifyDataSetChanged();
}
}
public void removeEmptyView(View view) {
removeHeaderView(view);
}
public void addFooterView(View view){
if(mFooter.indexOfValue(view)<0){
mFooter.put(BASE_ITEM_FOOTER_RANGE++,view);
notifyDataSetChanged();
}
}
重新计算数量
@Override
public int getItemCount() {
int itemCount = super.getItemCount();
return itemCount+mHeader.size()+mFooter.size();
}
重判断条目类型
@Override
public int getItemViewType(int position) {
if(isHeaderView(position)){
return mHeader.keyAt(position);
}
if(isFooterView(position)){
position = position - getOriginalItemCount()-mHeader.size();
return mFooter.keyAt(position);
}
//正常条目,potion需要重新计算
position = position - mHeader.size();
return getItemViewType2(position);
}
对头部和底部的的ViewHolder封装
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if(mHeader.indexOfKey(viewType)>=0){
View view = mHeader.get(viewType);
return (VH) new RecyclerView.ViewHolder(view){};
}
if(mFooter.indexOfKey(viewType)>=0){
View view = mFooter.get(viewType);
return (VH) new RecyclerView.ViewHolder(view){};
}
return onCreateViewHolder2(parent,viewType);
}
然后对于绑定数据时,对头部和底部跳过就行
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
//头部和底部不能参与绑定数据
if(isHeaderView(position) || isFooterView(position)){
return;
}
position = position - mHeader.size();
onBindViewHolder2(holder,position);
}
最后最主要的是对recyclerview的AdapterDataObserver重新封装
@Override
public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
super.registerAdapterDataObserver(new MyObserver(observer));
}
private class MyObserver extends RecyclerView.AdapterDataObserver{
private RecyclerView.AdapterDataObserver observer;
public MyObserver(RecyclerView.AdapterDataObserver observer) {
this.observer = observer;
}
public void onChanged() {
// Do nothing
observer.onChanged();
}
public void onItemRangeChanged(int positionStart, int itemCount) {
// do nothing
observer.onItemRangeChanged(positionStart+mHeader.size(),itemCount);
}
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
observer.onItemRangeChanged(positionStart+mHeader.size(),itemCount,payload);
}
public void onItemRangeInserted(int positionStart, int itemCount) {
// do nothing
observer.onItemRangeInserted(positionStart+mHeader.size(),itemCount);
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// do nothing
observer.onItemRangeRemoved(positionStart+mHeader.size(),itemCount);
}
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
// do nothing
observer.onItemRangeMoved(fromPosition+mHeader.size(),toPosition+mHeader.size(),itemCount);
}
}
对于所有的position都要加上头部的数量,不然的话头部和正常条目会混在一起。
这样的话我们就提供了onCreateViewHolder2与onBindViewHolder2两个方法让子类实现
public class ImageListPagedAdapter extends AbsPagedListAdapter<ImageBean.DataBean,ImageListPagedAdapter.ViewHolder>{
public ImageListPagedAdapter() {
super(new DiffUtil.ItemCallback<ImageBean.DataBean>() {
@Override
public boolean areItemsTheSame(@NonNull ImageBean.DataBean oldItem, @NonNull ImageBean.DataBean newItem) {
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull ImageBean.DataBean oldItem, @NonNull ImageBean.DataBean newItem) {
return oldItem.equals(newItem);
}
});
}
@Override
protected ViewHolder onCreateViewHolder2(ViewGroup parent, int viewType) {
ImagesListItemLayoutBinding bind = DataBindingUtil.bind(LayoutInflater.from(parent.getContext()).inflate(R.layout.images_list_item_layout, parent, false));
return new ViewHolder(bind);
}
@Override
protected void onBindViewHolder2(ViewHolder holder, int position) {
ImageBean.DataBean dataBean = getItem(position);
holder.dindData(dataBean);
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImagesListItemLayoutBinding mViewDataBinding;
public ViewHolder(@NonNull ImagesListItemLayoutBinding viewDataBinding) {
super(viewDataBinding.getRoot());
mViewDataBinding = viewDataBinding;
}
public void dindData(ImageBean.DataBean item) {
mViewDataBinding.setDataBean(item);
}
}
}
条目布局很简单就是展示个图片
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="com.easyar.lib_network.bean.ImageBean.DataBean"/>
<variable
name="dataBean"
type="DataBean" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.easyar.jetpackdeme.view.BindingImageView
android:id="@+id/iv_item"
android:layout_width="match_parent"
android:layout_height="200dp"
tools:src="@mipmap/ic_launcher"
android:layout_centerHorizontal="true"
app:iamge_url="@{dataBean.getUrl}"
android:layout_marginBottom="10dp"
app:isCircle="@{false}" />
</RelativeLayout>
</layout>
值的注意的是我们在这里直接用ImageView的src展示图片是不行的,所以我们创建一个自定义imageview。
public class BindingImageView extends AppCompatImageView {
public BindingImageView(Context context) {
this(context,null);
}
public BindingImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public BindingImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@BindingAdapter(value = {"iamge_url","isCircle"},requireAll = true)
public static void setImageUrl(BindingImageView view,String iamgeUrl,boolean isCircle){
RequestBuilder<Drawable> builder = Glide.with(view).load(iamgeUrl);
if(isCircle){
builder.transform(new CircleCrop());
}else{
}
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
if(layoutParams!=null && layoutParams.width>0 && layoutParams.height>0){
builder.override(layoutParams.width,layoutParams.height);
}
builder.into(view);
}
}
其他都简单,唯一注意@BindingAdapter(value = {"iamge_url","isCircle"},requireAll = true),value 表示硕有的参数 ,requireAll = true表示设置了所有参数才能出发这个方法。
所以adapter方面就完成了。下面轮到了ImageListViewHelper
public class ImageListViewHelper extends RecyclerView.OnScrollListener implements ListEmptyView.EmptyViewClickListener {
private RecyclerView mRecyclerView;
private ImageListPagedAdapter mAdapter;
private PagedList.Config mConfig;
private ImageListDataSource mDataSource;
private DataChangedCallBack mDataChangedCallBack;
private final ListEmptyView mListEmptyView;
public ImageListViewHelper(RecyclerView recyclerView, ImageListPagedAdapter adpater){
this.mRecyclerView = recyclerView;
this.mAdapter = adpater;
mListEmptyView = new ListEmptyView(recyclerView.getContext());
mListEmptyView.setListener(this);
mRecyclerView.addOnScrollListener(this);
}
public void createPagedListLiveData(LifecycleOwner lifecycleOwner,DataChangedCallBack dataChangedCallBack){
mDataChangedCallBack = dataChangedCallBack;
mConfig = getConfig();
setLiveData(lifecycleOwner);
}
private void setLiveData(LifecycleOwner lifecycleOwner) {
LiveData<PagedList<ImageBean.DataBean>> liveData = new LivePagedListBuilder(new DataSource.Factory() {
@NonNull
@Override
public DataSource create() {
mDataSource = new ImageListDataSource(new DataChangedCallBack() {
@Override
public void onChanged(List<ImageBean.DataBean> data) {
showOrHindEmpty(data);
if(mDataChangedCallBack!=null){
mDataChangedCallBack.onChanged(data);
}
}
});
return mDataSource;
}
}, mConfig).build();
//观察者模式,将Adapter注册进去,当liveData发生改变事通知Adapter
liveData.observe(lifecycleOwner, new Observer<PagedList<ImageBean.DataBean>>() {
@Override
public void onChanged(@Nullable PagedList<ImageBean.DataBean> subjectsBeans) {
mAdapter.submitList(subjectsBeans);
}
});
}
private PagedList.Config getConfig(){
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(5) //每页显示的词条数
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(5) //首次加载的数据量
.setPrefetchDistance(1) //距离底部还有多少条数据时开始预加载
.build();
return config;
}
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if(layoutManager instanceof LinearLayoutManager){
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
int itemCount = recyclerView.getLayoutManager().getItemCount();
int lastPosition = linearLayoutManager.findLastCompletelyVisibleItemPosition();
if(lastPosition == itemCount-1){
//滑动到最后一条,重新开启pagelist监听
reSetPaging(Collections.<ImageBean.DataBean>emptyList());
}
}
}
public void reSetPaging(List<ImageBean.DataBean> list) {
reSetPaging(list,false);
}
public void reSetPaging(List<ImageBean.DataBean> list,boolean delete) {
MutableItemKeyedDataSource<Integer, ImageBean.DataBean> mutableItemKeyedDataSource = new MutableItemKeyedDataSource<Integer, ImageBean.DataBean>(mDataSource) {
@NonNull
@Override
public Integer getKey(@NonNull ImageBean.DataBean item) {
return item.getId();
}
};
List<ImageBean.DataBean> data = mutableItemKeyedDataSource.data;
data.clear();
PagedList<ImageBean.DataBean> currentList = ImageListViewHelper.this.mAdapter.getCurrentList();
data.addAll(currentList);
if(list!=null){
if(delete){
//如果是要删除
for (ImageBean.DataBean dataBean : list) {
data.remove(dataBean);
}
}else{
data.addAll(list);
}
}
PagedList<ImageBean.DataBean> dataBeans = mutableItemKeyedDataSource.buildNewPagedList(mConfig);
ImageListViewHelper.this.mAdapter.submitList(dataBeans);
}
private void reStartLoadData() {
RequestCenter.mRequestAPI.getImageData().enqueue(new Callback<ImageBean>() {
@Override
public void onResponse(Call<ImageBean> call, Response<ImageBean> response) {
ImageBean body = response.body();
List<ImageBean.DataBean> data = body.getData();
reSetPaging(data);
showOrHindEmpty(data);
if(mDataChangedCallBack!=null){
mDataChangedCallBack.onChanged(data);
}
}
@Override
public void onFailure(Call<ImageBean> call, Throwable t) {
}
});
}
private void showOrHindEmpty(List<ImageBean.DataBean> data) {
if(data == null || data.size()==0){
//显示空白页面
mAdapter.addEmptyView(mListEmptyView);
}else{
mAdapter.removeEmptyView(mListEmptyView);
}
}
@Override
public void clickRestart() {
reStartLoadData();
}
public void addAItem() {
Log.d("yanjin","addAItem");
List<ImageBean.DataBean> list = new ArrayList<>();
ImageBean.DataBean dataBean = new ImageBean.DataBean();
dataBean.setId(10);
dataBean.setTips("测试插入");
dataBean.setUrl("http://img2.imgtn.bdimg.com/it/u=1050962453,2431854183&fm=26&gp=0.jpg");
list.add(dataBean);
reSetPaging(list);
}
public void deleteAItem() {
Log.d("yanjin","addAItem");
List<ImageBean.DataBean> list = new ArrayList<>();
ImageBean.DataBean dataBean = new ImageBean.DataBean();
dataBean.setId(10);
dataBean.setTips("测试插入");
dataBean.setUrl("http://img2.imgtn.bdimg.com/it/u=1050962453,2431854183&fm=26&gp=0.jpg");
list.add(dataBean);
reSetPaging(list,true);
}
public interface DataChangedCallBack{
void onChanged(List<ImageBean.DataBean> data);
}
}
paging需要一个PagedList.Config
private PagedList.Config getConfig(){
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(5) //每页显示的词条数
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(5) //首次加载的数据量
.setPrefetchDistance(1) //距离底部还有多少条数据时开始预加载
.build();
return config;
}
同样的需要liveData绑定数据
private void setLiveData(LifecycleOwner lifecycleOwner) {
LiveData<PagedList<ImageBean.DataBean>> liveData = new LivePagedListBuilder(new DataSource.Factory() {
@NonNull
@Override
public DataSource create() {
mDataSource = new ImageListDataSource(new DataChangedCallBack() {
@Override
public void onChanged(List<ImageBean.DataBean> data) {
showOrHindEmpty(data);
if(mDataChangedCallBack!=null){
mDataChangedCallBack.onChanged(data);
}
}
});
return mDataSource;
}
}, mConfig).build();
//观察者模式,将Adapter注册进去,当liveData发生改变事通知Adapter
liveData.observe(lifecycleOwner, new Observer<PagedList<ImageBean.DataBean>>() {
@Override
public void onChanged(@Nullable PagedList<ImageBean.DataBean> subjectsBeans) {
mAdapter.submitList(subjectsBeans);
}
});
}
当然我这里把空界面的view也放在这个类里面,activity只保管头部和底部布局就行。
最后就是我们关心的问题,paging框架只要有一次请求失败,那么他就不会再出发自动请求加载下一页的回调。
这个问题我们搞一个配合类,记录下paging加载过的数据
public abstract class MutableItemKeyedDataSource<Key, Value> extends ItemKeyedDataSource<Key, Value> {
private ItemKeyedDataSource mDataSource;
public List<Value> data = new ArrayList<>();
public PagedList<Value> buildNewPagedList(PagedList.Config config) {
PagedList<Value> pagedList = new PagedList.Builder<Key, Value>(this, config)
.setFetchExecutor(ArchTaskExecutor.getIOThreadExecutor())
.setNotifyExecutor(ArchTaskExecutor.getMainThreadExecutor())
.build();
return pagedList;
}
public MutableItemKeyedDataSource(ItemKeyedDataSource dataSource) {
mDataSource = dataSource;
}
@Override
public void loadInitial(@NonNull LoadInitialParams<Key> params, @NonNull LoadInitialCallback<Value> callback) {
callback.onResult(data);
}
@Override
public void loadAfter(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Value> callback) {
if (mDataSource != null) {
//一旦 和当前DataSource关联的PagedList被提交到PagedListAdapter。那么ViewModel中创建的DataSource 就不会再被调用了
//我们需要在分页的时候 代理一下 原来的DataSource,迫使其继续工作
mDataSource.loadAfter(params, callback);
}
}
@Override
public void loadBefore(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Value> callback) {
callback.onResult(Collections.<Value>emptyList());
}
@NonNull
@Override
public abstract Key getKey(@NonNull Value item);
}
然后,我们在替换数据就行了
public void reSetPaging(List<ImageBean.DataBean> list,boolean delete) {
MutableItemKeyedDataSource<Integer, ImageBean.DataBean> mutableItemKeyedDataSource = new MutableItemKeyedDataSource<Integer, ImageBean.DataBean>(mDataSource) {
@NonNull
@Override
public Integer getKey(@NonNull ImageBean.DataBean item) {
return item.getId();
}
};
List<ImageBean.DataBean> data = mutableItemKeyedDataSource.data;
data.clear();
PagedList<ImageBean.DataBean> currentList = ImageListViewHelper.this.mAdapter.getCurrentList();
data.addAll(currentList);
if(list!=null){
if(delete){
//如果是要删除
for (ImageBean.DataBean dataBean : list) {
data.remove(dataBean);
}
}else{
data.addAll(list);
}
}
PagedList<ImageBean.DataBean> dataBeans = mutableItemKeyedDataSource.buildNewPagedList(mConfig);
ImageListViewHelper.this.mAdapter.submitList(dataBeans);
}
先把数据清空,然后传入新的数据和adapter中记录的原来的数据,重新submitList就好了,当然,我这里还做了一个滑动到底部才触发的判断。
网友评论