- 工作原理
- 支持的架构类型
- 3个核心类
- 3种不同的分页方式
- PositionalDataSource实例
- PageKeyedDataSource实例
- ItemKeyedDataSource实例
- BoundaryCallback使用流程分析
1.工作原理
DiffUtil:差分,只跟新需要更新的数据,而不是全部更新
- 写好api接口
- 写好retrofit单例
- 写DataSource加载数据
- DataSourceFactory工厂创建DataSouce实例
- PagList配置
- PagListAdapter缓存数据
2.支持的架构类型
①直接从网络中读取数据
②直接从数据库中读取数据
③将网络数据先缓存到数据库中,再从数据库中读取数据
3.3个核心类
①PagedListAdapter:
RecyclerView需要搭配适配器使用, 如果使用Paging组件,适配器就需要继承自PagedListAdapter
②PagedList:
负责通知DataSource什么时候获取数据(如在向上滑动RecyclerView时,距离底部还有几个数据时,开始加载下一组数据),如何获取数据(第一页加载的数量,最大加载数量…),从DataSource中获取的数据将存储在PagedList中
③DataSource:
执行具体的数据加载工作,数据的载入需要在工作线程中执行
4.3种不同的分页方式
①PositionalDataSource
适用于可以通过任意位置加载数据,且目标数据源数量固定的情况。
②PagedKeyedDataSource
适用于数据源以页的方式进行请求的情况
③ItemKeyedDataSource
适用于当目标数据下一页需要上一页数据中最后一个对象中的某个字段作为key的情况
5.PositionalDataSource实例
- 写好api接口
- 写好retrofit单例
- 写DataSource加载数据
- DataSourceFactory工厂创建DataSouce实例
- PagList配置
- PagListAdapter缓存数据
1.写好api接口
public interface Api {
@GET("page/{page}/count/{count}")
Call<JsonRootBean> getUsers(
@Path("page") int page,
@Path("count") int pagesize
);
}
2.写好retrofit单例
public class RetrofitClient {
//根路径
private static final String BASE_URL = "https://gank.io/api/v2/data/category/Girl/type/Girl/";
private static RetrofitClient mInstance;
private final Retrofit retrofit;
private RetrofitClient() {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(getClient())
.build();
}
private OkHttpClient getClient() {
return new OkHttpClient.Builder().build();
}
//单例
public static synchronized RetrofitClient getInstance(){
if (mInstance == null){
mInstance = new RetrofitClient();
}
return mInstance;
}
//单一接口
public Api getApi(){
return retrofit.create(Api.class);
}
//多个接口 传参
// public static <T> T create(Class<T> serviceClass){
// //将接口转换为实现类
// return retrofit.create(serviceClass);
// }
}
3.写DataSource加载数据
public class MovieDataSource extends PositionalDataSource<Data> {
public static final int PER_PAGE = 10;
public int startPosition = 1;
//页面首次加载数据调用
@Override
public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Data> callback) {
RetrofitClient.getInstance()
.getApi()
.getUsers(startPosition,PER_PAGE)
.enqueue(new Callback<JsonRootBean>() {
@Override
public void onResponse(Call<JsonRootBean> call, Response<JsonRootBean> response) {
if (response != null){
//把数据传递给PagedList
callback.onResult(response.body().getData(),
response.body().getPage(),
response.body().getTotal_counts());
Log.d("jiang", "loadRange" + response.body());
}
}
@Override
public void onFailure(Call<JsonRootBean> call, Throwable t) {
Log.d("jiang", "onFailure");
}
});
}
//加载下一页
@Override
public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Data> callback) {
RetrofitClient.getInstance()
.getApi()
.getUsers(startPosition,PER_PAGE)
.enqueue(new Callback<JsonRootBean>() {
@Override
public void onResponse(Call<JsonRootBean> call, Response<JsonRootBean> response) {
startPosition = startPosition + 1;
callback.onResult(response.body().getData());
}
@Override
public void onFailure(Call<JsonRootBean> call, Throwable t) {
Log.d("jiang", "onFailure");
}
});
}
}
4.DataSourceFactory工厂创建DataSouce实例
public class MovieDataSourceFactory extends DataSource.Factory<Integer, Data> {
@NonNull
@Override
public DataSource<Integer, Data> create() {
return new MovieDataSource();
}
}
5.PagList配置
public class MovieViewModel extends ViewModel {
public LiveData<PagedList<Data>> moviePagedList;
public MovieViewModel(){
PagedList.Config config = new PagedList.Config.Builder()
//设置控件占位
.setEnablePlaceholders(false)
//可以让MovieDataSource知道下一次从哪开始
.setPageSize(MovieDataSource.PER_PAGE)
//设置当距离底部还有多少条数据时开始加载下一条
.setPrefetchDistance(2)
//设置首次加载的数量
.setInitialLoadSizeHint(MovieDataSource.PER_PAGE * 2)
//设置
.setMaxSize(65536 * MovieDataSource.PER_PAGE)
.build();
moviePagedList = new LivePagedListBuilder<>(
new MovieDataSourceFactory(),config)
.build();
}
}
6.PagListAdapter缓存数据
public class MoviePagedListAdapter extends PagedListAdapter<Data,MoviePagedListAdapter.MovieViewHolder> {
//差分 只更新需要更新的数据,而不是整个刷新数据源
private static final DiffUtil.ItemCallback<Data> DIFF_CALLBACK = new DiffUtil.ItemCallback<Data>() {
@Override
public boolean areItemsTheSame(@NonNull Data oldItem, @NonNull Data newItem) {
return oldItem == newItem;
}
@Override
public boolean areContentsTheSame(@NonNull Data oldItem, @NonNull Data newItem) {
return oldItem.equals(newItem);
}
};
private final Context context;
public MoviePagedListAdapter(Context context) {
//差分 只更新需要更新的数据
super(DIFF_CALLBACK);
this.context = context;
}
@NonNull
@Override
public MovieViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
return new MovieViewHolder(root);
}
@Override
public void onBindViewHolder(@NonNull MovieViewHolder holder, int position) {
Data data = getItem(position);
if (data != null){
Picasso.get()
.load(data.getUrl())
.placeholder(R.drawable.ic_launcher_background)
.error(R.drawable.ic_launcher_background)
.into(holder.imageView);
holder.textViewName.setText(data.getTitle());
holder.textViewScore.setText(data.get_id());
}
}
public static class MovieViewHolder extends RecyclerView.ViewHolder {
private final TextView textViewName;
private final TextView textViewScore;
private final ImageView imageView;
public MovieViewHolder(@NonNull View itemView) {
super(itemView);
this.imageView = itemView.findViewById(R.id.imageView);
this.textViewName = itemView.findViewById(R.id.textViewName);
this.textViewScore = itemView.findViewById(R.id.textViewScore);
}
}
}
8.BoundaryCallback使用流程分析
解决:从网络上请求数据,再将数据缓存到数据库中,再从数据库中读取数据显示到页面上 的问题
好处:即使没有网络也可以显示一部分数据 UI优化