其实很多这种文章了,但是很多都是Kotlin甚至都是一些理论?还有一些是用VM模式(比如官方)。作为老骨头,我们来看看Java最基础版的。
我的主张是:先给我看代码,我再考虑后面的。
2019-08-08更新
GoogleSamples有完整的Demo:PagingWithNetworkSample
简单说下吧,这个Demo比较完善,但是我觉得不够精简。等我空了再重新分析整理一份,不过我这个老骨头也已经转kotlin了,以后的代码也分享为kotlin了。
Paging的功能无非是控制页面的加载过程(什么时候加载上一页/下一页?),优点自行搜索引擎。
时间仓促,先直接上代码了。后面再来完善文章:
0 引入paging库
def pagingCommonVersion = '2.0.0-rc01'
def pagingNewestVersion = '2.1.0-beta01'
dependencies {
api 'androidx.recyclerview:recyclerview:1.0.0'
api "androidx.paging:paging-common:$pagingCommonVersion"
api "androidx.paging:paging-runtime:$pagingNewestVersion"
}
1 构建本地模拟数据
1.1 模拟实体:StudentModel
class StudentModel {
int id;
String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.2 模拟数据仓库CustomRepository
class CustomRepository {
List<StudentModel> studentModels = new ArrayList<>();
CustomRepository() {
for (int i = 0; i < 100; i++) {
StudentModel studentModel = new StudentModel();
studentModel.setId(i);
studentModel.setName("name" + i);
studentModels.add(studentModel);
}
}
List<StudentModel> getStudentsInit(@IntRange(from = 0) int initSize) {
return studentModels.subList(0, initSize);//包含~不包含
}
List<StudentModel> getStudentsByRange(@IntRange(from = 0) int from, int to) {
return studentModels.subList(from, to);
}
List<StudentModel> getStudentsByPage(@IntRange(from = 0) int page, int size) {
int totalPage = 0;
if (studentModels.size() % size == 0) {
totalPage = studentModels.size() / size;
} else {
totalPage = studentModels.size() / size + 1;
}
if (page >= totalPage || page < 0) {
return null;
}
if (page == totalPage - 1) {
return studentModels.subList(page * size, studentModels.size());
}
return studentModels.subList(page * size, (page + 1) * size);
}
}
2 继承于PageKeyedDataSource
的数据源类 CustomPageDataSource
class CustomPageDataSource extends PageKeyedDataSource<Integer, StudentModel> {
private CustomRepository mCustomRepository;
CustomPageDataSource(CustomRepository customRepository) {
this.mCustomRepository = customRepository;
}
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, StudentModel> callback) {
List<StudentModel> studentModels = mCustomRepository.getStudentsInit(params.requestedLoadSize);
callback.onResult(studentModels, null, 1);
}
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, StudentModel> callback) {
//模拟网络调用耗时操作
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<StudentModel> studentModels = mCustomRepository.getStudentsByPage(params.key, params.requestedLoadSize);
callback.onResult(studentModels, params.key - 1);
}
}).start();
}
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, StudentModel> callback) {
//模拟网络调用耗时操作
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<StudentModel> studentModels = mCustomRepository.getStudentsByPage(params.key, params.requestedLoadSize);
if (studentModels != null) {
callback.onResult(studentModels, params.key + 1);
}
}
}).start();
}
}
3 继承于DataSource.Factory
的数据工厂类 CustomPageDataSourceFactory
class CustomPageDataSourceFactory extends DataSource.Factory<Integer, StudentModel> {
private CustomRepository mCustomRepository;
CustomPageDataSourceFactory(CustomRepository customRepository) {
this.mCustomRepository = customRepository;
}
@NonNull
@Override
public DataSource<Integer, StudentModel> create() {
return new CustomPageDataSource(mCustomRepository);
}
}
4 DiffUtil.ItemCallback
private static DiffUtil.ItemCallback<StudentModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<StudentModel>() {
// Concert details may have changed if reloaded from the database,
// but ID is fixed.
@Override
public boolean areItemsTheSame(StudentModel oldConcert, StudentModel newConcert) {
return oldConcert.getId() == newConcert.getId();
}
@Override
public boolean areContentsTheSame(StudentModel oldConcert,
StudentModel newConcert) {
return oldConcert.equals(newConcert);
}
};
5 继承于PagedListAdapter
的StudentAdapter
public class StudentAdapter extends PagedListAdapter<StudentModel, StudentAdapter.CustomViewHolder> {
protected StudentAdapter() {
super(DIFF_CALLBACK);
}
@NonNull
@Override
public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new CustomViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_test, parent, false));
}
@Override
public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) {
StudentModel studentModel = getItem(position);
holder.tvID.setText(String.valueOf(studentModel.getId()));
holder.tvName.setText(studentModel.getName());
}
class CustomViewHolder extends RecyclerView.ViewHolder {
TextView tvID, tvName;
public CustomViewHolder(@NonNull View itemView) {
super(itemView);
tvID = itemView.findViewById(R.id.tv_id);
tvName = itemView.findViewById(R.id.tv_name);
}
}
}
6 使用MainActivity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
StudentAdapter studentAdapter = new StudentAdapter();
recyclerView.setAdapter(studentAdapter);
PagedList.Config config = new PagedList.Config.Builder()
.setInitialLoadSizeHint(10)//设置首次加载的数量;
.setPageSize(15)//设置每一页加载的数量;
.setMaxSize(15 + 2 * 3)
.setPrefetchDistance(3)//设置距离最后还有多少个item时,即自动加载下一页的数据;
.setEnablePlaceholders(true)//表示是否设置null占位符;
.build();
CustomRepository repository = new CustomRepository();
CustomPageDataSourceFactory factory = new CustomPageDataSourceFactory(repository);
LiveData<PagedList<StudentModel>> liveData = new LivePagedListBuilder<>(factory, config).build();
//这里的this是LifecycleOwner接口,而我们常用的AppCompatActivity已经实现啦
liveData.observe(this, studentAdapter::submitList);
}
以上代码只需要创建一个Activity
和对应的Adapter
所需的item_test.xml
,然后把上面的类全部copy到Activity
作为内部类就可以直接跑起来了。当然,Activity
里需要一个装有RecyclerView
的activity_main.xml
。。。