Android--Jetpack--Paging详解
不尝世间醋与墨,怎知人间酸与苦。
择一业谋食养命,等一运扭转乾坤。
你见过哪些令你膛目结舌的代码技巧?
一,定义
在我们的 Android 项目中使用 RecyclerViews 时,我们会显示一个包含很多项目的列表。有时我们有一些用例,比如从手机中获取联系人列表并将其显示在列表中。在列表中一次加载大量数据并不是一项非常有效的任务。为了克服这个问题,我们在 Android 中进行了分页。 Paging就是google为了分页而推出的一个库。Paging库可以帮助您一次加载和显示多个小的数据块,按需载入部分数据可以减少网络宽带和系统资源的使用量。
二,优点
①:
分页库可以更加轻松地在应用程序中的Recyclerview逐步和优雅的加载数据
?②:数据请求消耗的网络带宽更少,系统资源更少
?③:即使在数据更新和刷新期间,应用程序仍会继续快速响应用户输入 ?
④:不过多浪费,显示多少就用多少
三,角色
①:DataSource(数据源,包含了多种形式,例如:Room来源,PositionalDataSource来源,PageKeyedDataSource来源,ItemKeyedDataSource来源)
 数据源就是数据的来源,可以有多种来源渠道,例如:“网络数据”,“本地数据”,“数据库数据”
②:PagedList(UIModel数据层,通过Factory的方式拿到数据源)
 创建 管理 数据源 的工厂,为什么有一个工厂,除了可以去创建数据源之外,为了后续的扩展
③:PagedAdapter(不再是之前使用RecycleView的那种适配器了,而是和Paging配套的PagedListAdapter)
 数据模型其实就是 ViewModel,用来管理数据
 PagedList: 数据源获取的数据最终靠PagedList来承载。
 对于PagedList,我们可以这样来理解,它就是一页数据的集合。
 每请求一页,就是新的一个PagedList对象。
④:RecycleView(我们之前一直用的RecycleView,只不过setAdapter的时候,绑定的适配器是 PagedAdapter)
 这个Adapter就是一个RecyclerView的Adapter。
 不过我们在使用paging实现RecyclerView的分页加载效果,
 不能直接继承RecyclerView的Adapter,而是需要继承PagedListAdapter。
 LiveData观察到的数据,把感应到的数据 给 适配器,适配器又绑定了 RecyclerView,那么RecyclerView的列表数据就改变了
四,PositionalDataSource来源的使用
1,添加依赖
implementation 'androidx.paging:paging-runtime:2.1.0'
2,创建bean类
public class YuanZhen {
    private String id;
    private String name;
    private String age;
    public void setId(String id) {
        this.id = id;
    }
    public String getId() {
        return id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public String getAge() {
        return age;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        YuanZhen student = (YuanZhen) o;
        return id.equals(student.id) &&
                name.equals(student.name) &&
                age.equals(student.age);
    }
    // 比较的函数
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public int hashCode() {
        return Objects.hash(id, name, age);
    }
}
3,创建PositionalDataSource来源的数据源
public class YuanZhenDataSource extends PositionalDataSource<YuanZhen> {
    /**
     * 加载第一页数据的时候,会执行此函数来完成
     * 加载初始化数据,加载的是第一页的数据。
     * 形象的说,当我们第一次打开页面,需要回调此方法来获取数据。
     */
    @Override
    public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<YuanZhen> callback) {
        callback.onResult(getStudents(0, 20), 0, 1000);
    }
    /**
     * 当有了初始化数据之后,滑动的时候如果需要加载数据的话,会调用此方法。
     */
    @Override
    public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<YuanZhen> callback) {
        callback.onResult(getStudents(params.startPosition, params.loadSize));
    }
    /**
     * 数据源,数据的来源(数据库,文件,网络服务器响应  等等)
     */
    private List<YuanZhen> getStudents(int startPosition, int pageSize) {
        List<YuanZhen> list = new ArrayList<>();
        for (int i = startPosition; i < startPosition + pageSize; i++) {
            YuanZhen yuanZhen = new YuanZhen();
            yuanZhen.setName("袁震:" + i);
            yuanZhen.setAge("年龄:" + i);
            list.add(yuanZhen);
        }
        return list;
    }
}
4,创建数据工厂
public class YuanZhenDataSourceFactory extends DataSource.Factory<Integer,YuanZhen> {
    @NonNull
    @Override
    public DataSource<Integer, YuanZhen> create() {
        YuanZhenDataSource yuanZhenDataSource =new YuanZhenDataSource();
        return yuanZhenDataSource;
    }
}
5,创建ViewModel
public class YuanZhenViewModel extends ViewModel {
    private final LiveData<PagedList<YuanZhen>> listLiveData;
    public YuanZhenViewModel() {
        YuanZhenDataSourceFactory factory = new YuanZhenDataSourceFactory();
        // 初始化 ViewModel
        this.listLiveData = new LivePagedListBuilder<Integer, YuanZhen>(factory, 20).build();
    }
    public LiveData<PagedList<YuanZhen>> getListLiveData() {
        return listLiveData;
    }
}
6,创建adapter
public class RecyclerPagingAdapter extends PagedListAdapter<YuanZhen,RecyclerPagingAdapter.MyRecyclerViewHolder> {
    private static DiffUtil.ItemCallback<YuanZhen> DIFF_STUDNET = new
            DiffUtil.ItemCallback<YuanZhen>() {
                // 一般是比较 唯一性的内容, ID
                @Override
                public boolean areItemsTheSame(@NonNull YuanZhen oldItem, @NonNull YuanZhen newItem) {
                    return oldItem.getId().equals(newItem.getId());
                }
                // 对象本身的比较
                @Override
                public boolean areContentsTheSame(@NonNull YuanZhen oldItem, @NonNull YuanZhen newItem) {
                    return oldItem.equals(newItem);
                }
            };
    protected RecyclerPagingAdapter() {
        super(DIFF_STUDNET);
    }
    @NonNull
    @Override
    public MyRecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null);
        return new MyRecyclerViewHolder(view);
    }
    @Override
    public void onBindViewHolder(@NonNull MyRecyclerViewHolder holder, int position) {
        YuanZhen yuanzhen = getItem(position);
        // item view 出来了, 分页库还在加载数据中,我就显示 Id加载中
        if (null == yuanzhen) {
            
            holder.tvName.setText("Name加载中");
            holder.tvAge.setText("age加载中");
        } else {
            
            holder.tvName.setText(yuanzhen.getName());
            holder.tvAge.setText(yuanzhen.getAge());
        }
    }
    // Item 优化的 ViewHolder
    public static class MyRecyclerViewHolder extends RecyclerView.ViewHolder {
        TextView tvId;
        TextView tvName;
        TextView tvAge;
        public MyRecyclerViewHolder(View itemView) {
            super(itemView);
            
            tvName = itemView.findViewById(R.id.tv_name); // 名称
            tvAge = itemView.findViewById(R.id.tv_age); // 性别
        }
    }
}
item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:layout_weight="1"
        android:gravity="center"
        android:layout_marginLeft="5dp"
        />
    <TextView
        android:id="@+id/tv_age"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:textColor="@android:color/black"
        android:layout_weight="1"
        android:gravity="center"
        android:layout_marginLeft="5dp"
        />
</LinearLayout>
7,使用
public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    RecyclerPagingAdapter recyclerPagingAdapter;
    YuanZhenViewModel viewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView =  findViewById(R.id.recycle_view);
        recyclerPagingAdapter = new RecyclerPagingAdapter();
        
        viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory())
                .get(YuanZhenViewModel.class);
        // LiveData 观察者 感应更新
        viewModel.getListLiveData().observe(this, new Observer<PagedList<YuanZhen>>() {
            @Override
            public void onChanged(PagedList<YuanZhen> students) {
                // 再这里更新适配器数据
                recyclerPagingAdapter.submitList(students);
            }
        });
        recyclerView.setAdapter(recyclerPagingAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycle_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
7,运行效果

五,ItemKeyedDataSource来源的使用
1,创建数据仓库
public class DataRepository {
    private List<YuanZhen> dataList = new ArrayList<>();
    public DataRepository() {
        for (int i = 0; i < 1000; i++) {
            YuanZhen person = new YuanZhen();
            person.setName("袁震:" + i);
            person.setAge("年龄:" + i);
            dataList.add(person);
        }
    }
    public List<YuanZhen> initData(int size) {
        return dataList.subList(0, size);
    }
    public List<YuanZhen> loadPageData(int page, int size) {
        int totalPage;
        if (dataList.size() % size == 0) {
            totalPage = dataList.size() / size;
        } else {
            totalPage = dataList.size() / size + 1;
        }
        if (page > totalPage || page < 1) {
            return null;
        }
        if (page == totalPage) {
            return dataList.subList((page - 1) * size, dataList.size());
        }
        return dataList.subList((page - 1) * size, page * size);
    }
}
2,创建ItemKeyedDataSource
public class CustomItemDataSource extends ItemKeyedDataSource<Integer, YuanZhen> {
    private DataRepository dataRepository;
    CustomItemDataSource(DataRepository dataRepository) {
        this.dataRepository = dataRepository;
    }
    // loadInitial 初始加载数据
    @Override
    public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<YuanZhen> callback) {
        List<YuanZhen> dataList = dataRepository.initData(params.requestedLoadSize);
        callback.onResult(dataList);
    }
    @NonNull
    @Override
    public Integer getKey(@NonNull YuanZhen item) {
        return (int) System.currentTimeMillis();
    }
    // loadBefore 向前分页加载数据
    @Override
    public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<YuanZhen> callback) {
        List<YuanZhen> dataList = dataRepository.loadPageData(params.key, params.requestedLoadSize);
        if (dataList != null) {
            callback.onResult(dataList);
        }
    }
    // loadAfter 向后分页加载数据
    @Override
    public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<YuanZhen> callback) {
        List<YuanZhen> dataList = dataRepository.loadPageData(params.key, params.requestedLoadSize);
        if (dataList != null) {
            callback.onResult(dataList);
        }
    }
}
3,创建YuanZhenDataSourceFactory
public class YuanZhenDataSourceFactory extends DataSource.Factory<Integer,YuanZhen> {
   
    @NonNull
    @Override
    public DataSource<Integer, YuanZhen> create() {
        return new CustomItemDataSource(new DataRepository());
    }
}
六,PageKeyedDataSource来源的使用
1,创建PageKeyedDataSource
public class CustomPageDataSource extends PageKeyedDataSource<Integer, YuanZhen> {
    private DataRepository dataRepository;
    CustomPageDataSource(DataRepository dataRepository) {
        this.dataRepository = dataRepository;
    }
    // loadInitial 初始加载数据
    @Override
    public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, YuanZhen> callback) {
        List<YuanZhen> dataList = dataRepository.initData(params.requestedLoadSize);
        callback.onResult(dataList, null, 2);
    }
    // loadBefore 向前分页加载数据
    @Override
    public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, YuanZhen> callback) {
        List<YuanZhen> dataList = dataRepository.loadPageData(params.key, params.requestedLoadSize);
        if (dataList != null) {
            callback.onResult(dataList, params.key - 1);
        }
    }
    // loadAfter 向后分页加载数据
    @Override
    public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, YuanZhen> callback) {
        List<YuanZhen> dataList = dataRepository.loadPageData(params.key, params.requestedLoadSize);
        if (dataList != null) {
            callback.onResult(dataList, params.key + 1);
        }
    }
}
2,创建数据工厂
public class YuanZhenDataSourceFactory extends DataSource.Factory<Integer,YuanZhen> {
  
    @NonNull
    @Override
    public DataSource<Integer, YuanZhen> create() {
        return new CustomPageDataSource(new DataRepository());
    }
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!