Google 2018 I/O大会上,Google正式推出了AndroidJetpack ——这是一套组件、工具和指导,可以帮助开发者构建出色的 Android 应用。
架构组件可帮助您设计稳健、可测试且易维护的应用。
-
以声明方式将可观察数据绑定到界面元素
-
管理您的 Activity 和 Fragment 生命周期
-
在底层数据库更改时通知视图
-
处理应用内导航所需的一切
-
逐步从您的数据源按需加载信息
-
流畅地访问 SQLite 数据库
-
以注重生命周期的方式管理界面相关的数据
而今天我们要将的是
Paging分页组件
有英文阅读能力,并且已经掌握了Kotlin的可以通过下面链接浏览官方文档,毕竟官方的才是最接近真理的:https://developer.android.google.cn/topic/libraries/architecture/paging/
喜欢简单一点的,我只想先学会简单地访问网络加载数据,怎么办?那你找对地方了,功能没实现,你说得天花乱坠都是瞎扯淡。那我们一步一步来吧!
引入库
3个都可以实现paging,这里为了简单,就不去涉及rxjava了,我们引入runtime版本(如果不需要调试功能的,可以使用common版本,博主使用过common版本,总觉得少点什么东西,后来就换回runtime版本)
dependencies {
//...
implementation 'android.arch.paging:runtime:1.0.1'
//...
}
实现分页加载功能
为了方便大家理解,我将它们分成两个部分:
- 准备数据部分
- 绑定UI部分
准备数据部分
1、定义DataSource,这里放关键的加载数据逻辑
/*
//step8、定制自己的DataSource,继承哪个DataSource是根据后台接口来定的,这里的接口是pageCount和pageSize
所以,我们需要继承PositionalDataSource<T>,并且实现他们的抽象函数
*/
class MyDataSource extends PositionalDataSource<MusicBean>{
List<MusicBean> listData = new ArrayList<>();
/**
* recyclerView第一次加载时自动调用
*
* @param params 包含当前加载的位置position、下一页加载的长度count
* @param callback 将数据回调给UI界面使用callback.onResult
*/
@Override
public void loadInitial(@NonNull LoadInitialParams params, @NonNull final LoadInitialCallback<MusicBean> callback) {
//step9、在这里请求网络加载数据
LogUtils.i(this,"loadInitial "+pageCount);
//计算显示到第几条
final int position = computeInitialLoadPosition(params, PAGE_SIZE);
//recyclerView第一次加载时我们调用OkHttp进行数据的加载
Map<String, String> paramsGet = new HashMap<>();
paramsGet.put("page", pageCount+"");
paramsGet.put("count", PAGE_SIZE + "");
paramsGet.put("type", "video");
HttpUtils.getAllApiInCompany().getVideoBean(paramsGet).enqueue(new Callback<MusicResponeBean>() {
@Override
public void onResponse(Call<MusicResponeBean> call, Response<MusicResponeBean> response) {
List<MusicBean> tmpList = response.body().getResult();
listData.addAll(tmpList);
//step10、
//最重要的一步,paging是基于观察者模式,我们在这里调用callback.onResult();
//会直接将数据list返回到UI层,等下面接受到这个list数据的数据的时候我会提醒大家
//如果设置占位符需要调用三个参数的onResult()方法,最后一个参数为每页的总数据量
//我们没有设置占位符,因此调用两个参数的方法,注意position通过computeInitialLoadPosition(params, PAGE_SIZE)获取
callback.onResult(listData,position);
}
@Override
public void onFailure(Call<MusicResponeBean> call, Throwable t) {
}
});
}
/**
* 当用户滑动recyclerView到下一屏的时候自动调用,这里我们自动加载下一页的数据
*
* @param params 包含当前加载的位置position、下一页加载的长度count
* @param callback 将数据回调给UI界面使用callback.onResult
*/
@Override
public void loadRange(@NonNull LoadRangeParams params, @NonNull final LoadRangeCallback<MusicBean> callback) {
//step11、请求网络加载更多数据,执行在子线程
pageCount++;
LogUtils.i(this,"loadRange "+pageCount);
Map<String, String> paramsGet = new HashMap<>();
paramsGet.put("page", pageCount + "");
paramsGet.put("count", PAGE_SIZE + "");
paramsGet.put("type", "video");
HttpUtils.getAllApiInCompany().getVideoBean(paramsGet)
.enqueue(new Callback<MusicResponeBean>() {
@Override
public void onResponse(Call<MusicResponeBean> call, Response<MusicResponeBean> response) {
List<MusicBean> tmpList = response.body().getResult();
listData.addAll(tmpList);
//step12、请求网络加载更多数据成功,调用callback的onResult()函数,传入总的list触发ui更新
callback.onResult(listData);
}
@Override
public void onFailure(Call<MusicResponeBean> call, Throwable t) {
}
});
}
}
2、DataSource.Factory,构建PagedList所需
/**
* 自定义DataSource.Factory
* //step7、定义一个DataSource.Facotry的子类,因为后台给的接口是那种有pageSize和pageCount的那种,
* 所以这里选择返回的DataSource应该是PositionalDataSource<T>这种,别问我为什么,google就是这么说的。其他的情况可以参考
* https://www.jianshu.com/p/ad040aab0e66
*/
class PageDataSourceFacotry extends DataSource.Factory {
PositionalDataSource<MusicBean> positionalDataSource;
public PageDataSourceFacotry(PositionalDataSource<MusicBean> positionalDataSource) {
this.positionalDataSource = positionalDataSource;
}
@Override
public DataSource create() {
return positionalDataSource;
}
}
3、构建LiveData<PagedList<T>>(使用Paging组件时,不用用List<T>在装载数据了,需要使用PagedList<T>),通过LivePagedListBuilder.builder方法生成,需要传入的参分别是前面的的DataSource.Factory和PagedList.Config。
private void initPagedList() {
//step13、生成自定义的DataSource对象
PositionalDataSource<MusicBean> positionalDataSource = new MyDataSource();
//step14、通过PagedList.Config.Builder()生成配置对象,传入的到LivePagedListBuilder中
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)//每次加载的数据数量15
//距离本页数据几个时候开始加载下一页数据(例如现在加载10个数据,设置prefetchDistance为2,则滑到第八个数据时候开始加载下一页数据).
.setPrefetchDistance(5)//15
//这里设置是否设置PagedList中的占位符,如果设置为true,我们的数据数量必须固定,由于网络数据数量不固定,所以设置false.
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(PAGE_SIZE)//15
.build();
/*
//step6、新建LiveData<PagedList<T>>,需要通过LivePagedListBuilder来完成,里面需要两个参数。
一个是:DataSource.Factor,作用就是返回一个DataSource,有几种类型,(PositionalDataSource适合接口是带pageCount,pageSize)
一个是:PagedList.Config,配置主要分页加载参数,如每页加载多少条数据等
*/
// 构建LiveData
pagedListLiveData = new LivePagedListBuilder(new PageDataSourceFacotry(positionalDataSource)//自己定义
, config).build();
}
绑定UI部分
1、原来的adapter需要实现PagedListAdapter,注意获取itemBean的时候需要用getItem()
2、给LiveData注册observer的时候,通过adapter.submitList(PagedList)绑定
public class SamplePagingActivity extends AppCompatActivity {
RecyclerView recyclerView;
private PagingViewModel pagingViewModel;
private MyPagingAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample_paging);
pagingViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(PagingViewModel.class);
//step3、获取ViewModel中的LiveData(LiveData<T> ,T必须要是一个PagedList<>类型),注册观察者
pagingViewModel.getPagedListLiveData().observe(this, new Observer<PagedList<MusicBean>>() {
@Override
public void onChanged(@Nullable PagedList<MusicBean> musicBeans) {
//step4、通过submitList()函数触发adapter数据更新
adapter.submitList(musicBeans);
}
});
recyclerView = findViewById(R.id.rv);
//step1、adapter换成PagingAdapter
adapter = new MyPagingAdapter( new DiffUtil.ItemCallback<MusicBean>() {
@Override
public boolean areItemsTheSame(@NonNull MusicBean musicBean, @NonNull MusicBean t1) {
return musicBean.text.equals(t1.text);//比对唯一标识码
// return false;
}
@SuppressLint("DiffUtilEquals")
@Override
public boolean areContentsTheSame(@NonNull MusicBean musicBean, @NonNull MusicBean t1) {
return musicBean.equals(t1);//比对对象
// return false;
}
});
recyclerView.setAdapter(adapter);
}
class MyPagingAdapter extends PagedListAdapter<MusicBean, RecyclerView.ViewHolder> {
protected MyPagingAdapter(@NonNull DiffUtil.ItemCallback<MusicBean> diffCallback) {
super(diffCallback);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(SamplePagingActivity.this).inflate(android.R.layout.simple_list_item_1, null);
RecyclerView.ViewHolder viewholder = new RecyclerView.ViewHolder(view) {
};
return viewholder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
//step2、获取list的方法必须换成PagingAdapter的getItem()函数
String str = getItem(i).text;
((TextView) viewHolder.itemView.findViewById(android.R.id.text1)).setText(str);
}
}
}
这里先简述一下流程:
- 首先我们需要一个DataSource:根据后台借口,应用不同的DataSource,Android api封装了3种
- 然后创建一个DataSource.Factory:实现抽象方法,返回一个DataSource
- 创建一个PagedList.Config:设置加载配置,如每页加载多少条数据
- 在ViewModel中创建LiveData<PagedList<T>>,通过LivePagedListBuilder创建,需要用到上面3个数据
- adapter换成PagingAdapter,bindView中获取list的方法必须换成PagingAdapter的getItem()函数
- 获取ViewModel中的LiveData(LiveData<T> ,T必须要是一个PagedList<>类型),注册观察者,在onChanged()内通过submitList()函数触发adapter数据更新
对于Android Jetpack的套件,我正在整理一个比较简单,适合初学者的Demo,特别适合快速入门,有兴趣的童鞋们可以去下载看看:https://github.com/KubyWong/JetpackSample