美文网首页
Android jetpack全家桶的paging简单使用

Android jetpack全家桶的paging简单使用

作者: 坑逼的严 | 来源:发表于2020-03-13 16:07 被阅读0次
    1584084454134.gif

    本来不想写这篇文章的,但是本人在学习的时候遇到一个问题,那就是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就好了,当然,我这里还做了一个滑动到底部才触发的判断。

    相关文章

      网友评论

          本文标题:Android jetpack全家桶的paging简单使用

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