当前位置: 代码迷 >> 综合 >> JetPack--Paging2
  详细解决方案

JetPack--Paging2

热度:63   发布时间:2023-09-14 05:34:00.0

Paging是一个用于分页加载的组件,对于一些列表数据,以前大家都使用过的一种方式是定义一个page,当达到加载更多条件时,page加一再去请求数据,为此我们要写很多重复的代码,Paging就对分页进行了一个封装

Paging由分为三个模块
1.DataSource:数据从该模块中获取,数据可以来源于网络、本地数据库等
2.PagedList:负责具体获取数据的逻辑,何时获取、加载下一页、预加载等
3.PagedListAdapter:RecyclerView的adapter需要继承它,内部做了一系列处理
JetPack--Paging2

一、Paging上手

1.PositionalDataSource
PositionalDataSource适合于从任意位置获取数据的情况,入参为开始点和数据量大小

首先我们要获取网络数据、使用LiveData、Paging等,需要添加依赖

implementation 'com.squareup.retrofit2:retrofit:2.9.0'implementation 'com.squareup.retrofit2:converter-gson:2.9.0'implementation 'com.squareup.picasso:picasso:2.71828'implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'implementation 'androidx.paging:paging-runtime:2.1.2'

根据服务器返回数据,创建实体类
服务器数据:

{"count":5,"start":0,"total":100,"subjects":[{"id":35076714,"title":"扎克·施奈德版正义联盟","cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2634360594.webp","rate":"8.9"},{"id":26935283,"title":"侍神令","cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2629260713.webp","rate":"5.8"},{"id":35145068,"title":"双层肉排","cover":"https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2633977758.webp","rate":"6.7"},{"id":33433405,"title":"大地","cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2628845704.webp","rate":"6.6"},{"id":35167535,"title":"租来的朋友","cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2616903233.webp","rate":"6.1"}]
}

实体类:

package com.aruba.paging.entity;import java.util.Objects;/*** Created by aruba on 2021/9/17.*/
public class Movie {//唯一idpublic int id;//标题public String title;//图片地址public String cover;//评分public String rate;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Movie movie = (Movie) o;return id == movie.id && title.equals(movie.title) && cover.equals(movie.cover) && rate.equals(movie.rate);}@Overridepublic int hashCode() {return Objects.hash(id, title, cover, rate);}@Overridepublic String toString() {return "Movie{" +"id=" + id +", title='" + title + '\'' +", cover='" + cover + '\'' +", rate='" + rate + '\'' +'}';}
}
package com.aruba.paging.entity;import com.google.gson.annotations.SerializedName;import java.util.List;
import java.util.Objects;/*** Created by aruba on 2021/9/17.*/
public class Movies {//当前有多少条数据public int count;//从哪条开始的public int start;//一共多少条public int total;@SerializedName("subjects")public List<Movie> movies;@Overridepublic String toString() {return "Movies{" +"count=" + count +", start=" + start +", total=" + total +", movies=" + movies +'}';}
}

定义接口相关:

package com.aruba.paging.api;import android.annotation.TargetApi;import com.aruba.paging.entity.Movies;import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;/*** Created by aruba on 2021/9/17.*/
public interface Api {@GET("pds.do")Call<Movies> getMovies(@Query("start") int start, @Query("count") int count);
}
package com.aruba.paging.api;import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;/*** Created by aruba on 2021/9/17.*/
public class RetrofitClient {private static final String BASE_URL = "http://192.168.17.114:8080/pagingserver_war/";private static RetrofitClient instance = new RetrofitClient();private Retrofit retrofit;static RetrofitClient getInstance() {if (instance == null) {instance = new RetrofitClient();}return instance;}private RetrofitClient() {retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).client(getOkhttpClient()).build();}private OkHttpClient getOkhttpClient() {return new OkHttpClient.Builder().build();}public Api getApi() {return retrofit.create(Api.class);}
}

定义DataSource,继承至PositionalDataSource

package com.aruba.paging.paging.model;import androidx.annotation.NonNull;
import androidx.paging.PositionalDataSource;import com.aruba.paging.api.RetrofitClient;
import com.aruba.paging.entity.Movie;
import com.aruba.paging.entity.Movies;import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;/*** Created by aruba on 2021/9/17.*/
public class MovieDataSource extends PositionalDataSource<Movie> {//一次取8条数据public static final int PER_PAGE = 8;//第一次加载@Overridepublic void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Movie> callback) {int startPosition = 0;RetrofitClient.getInstance().getApi()//入参:开始点,一次获取的数据量大小.getMovies(startPosition, PER_PAGE).enqueue(new Callback<Movies>() {@Overridepublic void onResponse(Call<Movies> call, Response<Movies> response) {if (response.body() != null) {//数据回调给pagedlistcallback.onResult(response.body().movies,response.body().start,response.body().total);}}@Overridepublic void onFailure(Call<Movies> call, Throwable t) {}});}//加载更多@Overridepublic void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Movie> callback) {RetrofitClient.getInstance().getApi()//入参:开始点,一次获取的数据量大小//加载更多时,params中的startPosition参数会自动加PER_PAGE.getMovies(params.startPosition, PER_PAGE).enqueue(new Callback<Movies>() {@Overridepublic void onResponse(Call<Movies> call, Response<Movies> response) {if (response.body() != null) {//数据回调给pagedlistcallback.onResult(response.body().movies);}}@Overridepublic void onFailure(Call<Movies> call, Throwable t) {}});}
}

接下来在定义PagedList之前,需要定义一个DataSource的Factory,PagedList需要通过这个Factory来拿到DataSource对象

package com.aruba.paging.paging.factory;import androidx.annotation.NonNull;
import androidx.paging.DataSource;import com.aruba.paging.entity.Movie;
import com.aruba.paging.paging.model.MovieDataSource;/*** Created by aruba on 2021/9/17.*/
public class MovieDataSourceFactory extends DataSource.Factory<Integer, Movie> {@NonNull@Overridepublic DataSource<Integer, Movie> create() {return new MovieDataSource();}}

定义一个ViewModel,把PagedList作为LiveData对象

package com.aruba.paging.paging.viewmodel;import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;import com.aruba.paging.entity.Movie;
import com.aruba.paging.paging.factory.MovieDataSourceFactory;
import com.aruba.paging.paging.model.MovieDataSource;/*** Created by aruba on 2021/9/17.*/
public class MovieViewModel extends ViewModel {public LiveData<PagedList<Movie>> pagedListLiveData;public MovieViewModel() {//配置项PagedList.Config config = new PagedList.Config.Builder()//最大加载多少条数据.setMaxSize(1000 * MovieDataSource.PER_PAGE)//首次加载多少条数据.setInitialLoadSizeHint(MovieDataSource.PER_PAGE * 2)//设置距离底部还有多少条数据时开始加载下一页的数据.setPrefetchDistance(2)//一页显示多少条.setPageSize(MovieDataSource.PER_PAGE)//设置控件占位.setEnablePlaceholders(false).build();//通过Factory和Config配置pagedListLiveData = new LivePagedListBuilder<>(new MovieDataSourceFactory(), config).build();}
}

接下来就是UI展示了,定义RecyclerView的Adapter,继承于PagedListAdapter,内部还需要需要一个diffCallback,用来刷新数据用:

package com.aruba.paging.adapter;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.paging.PagedListAdapter;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;import com.aruba.paging.R;
import com.aruba.paging.entity.Movie;
import com.squareup.picasso.Picasso;/*** Created by aruba on 2021/9/17.*/
public class MovieListAdapter extends PagedListAdapter<Movie, MovieListAdapter.ViewHolder> {//数据源更新时,只会更新不一样的,而不是整个adapter刷新private static final DiffUtil.ItemCallback<Movie> diffCallback = new DiffUtil.ItemCallback<Movie>() {@Overridepublic boolean areItemsTheSame(@NonNull Movie oldItem, @NonNull Movie newItem) {return oldItem == newItem;}@Overridepublic boolean areContentsTheSame(@NonNull Movie oldItem, @NonNull Movie newItem) {return oldItem.equals(newItem);}};public MovieListAdapter() {super(diffCallback);}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View item = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);return new ViewHolder(item);}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {Movie movie = getItem(position);holder.textViewTitle.setText(movie.title);holder.textViewRate.setText(movie.rate);Picasso.get().load(movie.cover).placeholder(R.drawable.ic_launcher_foreground).error(R.drawable.ic_launcher_foreground).into(holder.imageView);}public class ViewHolder extends RecyclerView.ViewHolder {private ImageView imageView;private TextView textViewTitle;private TextView textViewRate;public ViewHolder(@NonNull View itemView) {super(itemView);this.imageView = itemView.findViewById(R.id.imageView);this.textViewTitle = itemView.findViewById(R.id.textViewTitle);this.textViewRate = itemView.findViewById(R.id.textViewRate);}}
}

Activity中配置RecyclerView并实例化ViewModel进行数据观测:

package com.aruba.paging;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import android.content.Context;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.BaseKeyListener;
import android.text.method.DateKeyListener;
import android.text.method.KeyListener;
import android.text.method.NumberKeyListener;
import android.text.method.TextKeyListener;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;import com.aruba.paging.adapter.MovieListAdapter;
import com.aruba.paging.entity.Movie;
import com.aruba.paging.paging.viewmodel.MovieViewModel;public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MovieListAdapter movieListAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);recyclerView = findViewById(R.id.recyclerView);movieListAdapter = new MovieListAdapter();recyclerView.setAdapter(movieListAdapter);recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));//实例化ViewModelMovieViewModel movieViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MovieViewModel.class);//设置pagedList的数据变化监听movieViewModel.pagedListLiveData.observe(this, new Observer<PagedList<Movie>>() {@Overridepublic void onChanged(PagedList<Movie> movies) {//将数据放入adaptermovieListAdapter.submitList(movies);}});}}

效果:

JetPack--Paging2
2.PageKeyedDataSource
PageKeyedDataSource适合于按页分页的情况,需要一个页数和一页数据量大小

服务器返回数据:

{"has_more":true,"subjects":[{"id":35076714,"title":"扎克·施奈德版正义联盟","cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2634360594.webp","rate":"8.9"},{"id":26935283,"title":"侍神令","cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2629260713.webp","rate":"5.8"},{"id":35145068,"title":"双层肉排","cover":"https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2633977758.webp","rate":"6.7"},{"id":33433405,"title":"大地","cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2628845704.webp","rate":"6.6"},{"id":35167535,"title":"租来的朋友","cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2616903233.webp","rate":"6.1"}]
}

有了上面的基础,我们再修改下返回的Movies实体类:

package com.aruba.paging2.entity;import com.google.gson.annotations.SerializedName;import java.util.List;/*** Created by aruba on 2021/9/17.*/
public class Movies {//是否还有下一页@SerializedName("has_more")public boolean hasMore;@SerializedName("subjects")public List<Movie> movies;
}

修改下api

package com.aruba.paging2.api;import com.aruba.paging2.entity.Movies;import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;/*** Created by aruba on 2021/9/17.*/
public interface Api {@GET("pkds.do")Call<Movies> getMovies(@Query("page") int page,@Query("pagesize") int pagesize);
}

定义DataSource继承于PageKeyedDataSource,在初次加载和下一页加载中调用网络请求

package com.aruba.paging2.paging.model;import androidx.annotation.NonNull;
import androidx.paging.PageKeyedDataSource;
import androidx.paging.PositionalDataSource;import com.aruba.paging2.api.RetrofitClient;
import com.aruba.paging2.entity.Movie;
import com.aruba.paging2.entity.Movies;import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;/*** Created by aruba on 2021/9/17.*/
public class MovieDataSource extends PageKeyedDataSource<Integer, Movie> {public static final int PER_PAGE = 8;public static final int FIRST_PAGE = 1;@Overridepublic void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Movie> callback) {RetrofitClient.getInstance().getApi().getMovies(FIRST_PAGE, PER_PAGE).enqueue(new Callback<Movies>() {@Overridepublic void onResponse(Call<Movies> call, Response<Movies> response) {//第一次加载,上一页page为null,下一页传入page+1callback.onResult(response.body().movies, null, FIRST_PAGE + 1);}@Overridepublic void onFailure(Call<Movies> call, Throwable t) {}});}@Overridepublic void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Movie> callback) {}@Overridepublic void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Movie> callback) {RetrofitClient.getInstance().getApi().getMovies(params.key, PER_PAGE).enqueue(new Callback<Movies>() {@Overridepublic void onResponse(Call<Movies> call, Response<Movies> response) {if(response.body() == null){return;}//把数据传递给PagedListInteger nextKey = response.body().hasMore ? params.key + 1 : null;callback.onResult(response.body().movies, nextKey);}@Overridepublic void onFailure(Call<Movies> call, Throwable t) {}});}
}

其他的就不用改动了,效果和上面是一样的

3.ItemKeyedDataSource
ItemKeyedDataSource使用于不固定的数据列表,如帖子,因为新增帖子比较频繁,使用上面两种可能会出现重复数据,需要参数为实体类唯一值和数据量大小

服务器返回数据:

[{"id":35076714,"title":"扎克·施奈德版正义联盟","cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2634360594.webp","rate":"8.9"},{"id":26935283,"title":"侍神令","cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2629260713.webp","rate":"5.8"},{"id":35145068,"title":"双层肉排","cover":"https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2633977758.webp","rate":"6.7"},{"id":33433405,"title":"大地","cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2628845704.webp","rate":"6.6"},{"id":35167535,"title":"租来的朋友","cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2616903233.webp","rate":"6.1"}
]

直接返回一个列表,我们就不需要Movies类了,修改下api:

package com.aruba.paging3.api;import com.aruba.paging3.entity.Movie;
import com.aruba.paging3.entity.Movies;import java.util.List;import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;/*** Created by aruba on 2021/9/17.*/
public interface Api {@GET("ikds.do")Call<List<Movie>> getMovies(@Query("since") int since,@Query("pagesize") int pagesize);
}

定义DataSource继承于ItemKeyedDataSourcegetKey方法中返回实体类的唯一值,内部会使用最后一个数据的唯一值作为下次查询的参数

package com.aruba.paging2.paging.model;import androidx.annotation.NonNull;
import androidx.paging.ItemKeyedDataSource;import com.aruba.paging2.api.RetrofitClient;
import com.aruba.paging2.entity.Movie;import java.util.List;import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;/*** Created by aruba on 2021/9/17.*/
public class MovieDataSource extends ItemKeyedDataSource<Integer, Movie> {public static final int PER_PAGE = 8;@Overridepublic void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Movie> callback) {RetrofitClient.getInstance().getApi().getMovies(0, PER_PAGE).enqueue(new Callback<List<Movie>>() {@Overridepublic void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {if (response.body() != null)callback.onResult(response.body());}@Overridepublic void onFailure(Call<List<Movie>> call, Throwable t) {}});}@Overridepublic void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Movie> callback) {RetrofitClient.getInstance().getApi().getMovies(params.key, PER_PAGE).enqueue(new Callback<List<Movie>>() {@Overridepublic void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {if (response.body() != null)callback.onResult(response.body());}@Overridepublic void onFailure(Call<List<Movie>> call, Throwable t) {}});}@Overridepublic void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Movie> callback) {}@NonNull@Overridepublic Integer getKey(@NonNull Movie item) {return item.id;}
}

效果和上面也是一样的

二、本地数据缓存

BoundaryCallback

有时我们想要把数据缓存到本地,然后无网络时就可以加载本地数据,那么可以用BoundaryCallback

依赖Room数据库和刷新控件

implementation 'androidx.room:room-runtime:2.3.0-rc01'annotationProcessor 'androidx.room:room-compiler:2.3.0-rc01'implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01'

Movie类改造成Entity,由于服务器id为随机的,我们内置一个自增长的主键

package com.aruba.paging4.entity;import androidx.room.Entity;
import androidx.room.PrimaryKey;import java.util.Objects;/*** Created by aruba on 2021/9/17.*/
@Entity
public class Movie {//作为本地表的主键@PrimaryKey(autoGenerate = true)public int key;//唯一idpublic int id;//标题public String title;//图片地址public String cover;//评分public String rate;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Movie movie = (Movie) o;return id == movie.id && title.equals(movie.title) && cover.equals(movie.cover) && rate.equals(movie.rate);}@Overridepublic int hashCode() {return Objects.hash(key, id, title, cover, rate);}
}

定义DaoDatabase相关:

package com.aruba.paging4.database.dao;import androidx.paging.DataSource;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;import com.aruba.paging4.entity.Movie;import java.util.List;/*** Created by aruba on 2021/9/18.*/
@Dao
public interface MovieDao {@Insertvoid insertMovies(List<Movie> movies);@Query("DELETE FROM movie")void clear();@Query("SELECT * FROM movie")DataSource.Factory<Integer, Movie> getMovieList();
}
package com.aruba.paging4.database;import android.content.Context;import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.DatabaseConfiguration;
import androidx.room.InvalidationTracker;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;import com.aruba.paging4.database.dao.MovieDao;
import com.aruba.paging4.entity.Movie;/*** Created by aruba on 2021/9/18.*/
@Database(entities = {Movie.class}, version = 1, exportSchema = true)
public abstract class MyDataBase extends RoomDatabase {private static final String DBNAME = "my.db";private static MyDataBase instance;public static MyDataBase getInstance() {if (instance == null) throw new NullPointerException("database not init!!");return instance;}public static synchronized MyDataBase init(Context context) {if (instance == null)instance = Room.databaseBuilder(context.getApplicationContext(), MyDataBase.class, DBNAME).fallbackToDestructiveMigration().build();return instance;}public abstract MovieDao getMovieDao();
}

定义BoundaryCallback

package com.aruba.paging4.paging.boundarycallback;import android.os.AsyncTask;import androidx.annotation.NonNull;
import androidx.paging.PagedList;import com.aruba.paging4.api.RetrofitClient;
import com.aruba.paging4.database.MyDataBase;
import com.aruba.paging4.entity.Movie;import java.util.List;import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;/*** Created by aruba on 2021/9/18.*/
public class MovieBoundaryCallback extends PagedList.BoundaryCallback<Movie> {public static final int PER_PAGE = 10;@Overridepublic void onZeroItemsLoaded() {super.onZeroItemsLoaded();//加载第一页getTopData();}private void getTopData() {RetrofitClient.getInstance().getApi().getMovies(0, PER_PAGE).enqueue(new Callback<List<Movie>>() {@Overridepublic void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {if (response.body() != null)insertMovies(response.body());}@Overridepublic void onFailure(Call<List<Movie>> call, Throwable t) {}});}private void insertMovies(List<Movie> body) {new AsyncTask<Void, Void, Void>() {@Overrideprotected Void doInBackground(Void... voids) {MyDataBase.getInstance().getMovieDao().insertMovies(body);return null;}}.execute();}@Overridepublic void onItemAtEndLoaded(@NonNull Movie itemAtEnd) {super.onItemAtEndLoaded(itemAtEnd);//加载之后的页getAfterData(itemAtEnd);}private void getAfterData(Movie movie) {RetrofitClient.getInstance().getApi().getMovies(movie.id, PER_PAGE).enqueue(new Callback<List<Movie>>() {@Overridepublic void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {if (response.body() != null)insertMovies(response.body());}@Overridepublic void onFailure(Call<List<Movie>> call, Throwable t) {}});}
}

修改ViewModel,这时我们不需要DataSource和Factory了

package com.aruba.paging4.paging.viewmodel;import android.os.AsyncTask;import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList;import com.aruba.paging4.database.MyDataBase;
import com.aruba.paging4.entity.Movie;
import com.aruba.paging4.paging.boundarycallback.MovieBoundaryCallback;/*** Created by aruba on 2021/9/17.*/
public class MovieViewModel extends ViewModel {public LiveData<PagedList<Movie>> pagedListLiveData;public MovieViewModel() {//通过Dao获取FactorypagedListLiveData = new LivePagedListBuilder<>(MyDataBase.getInstance().getMovieDao().getMovieList(),MovieBoundaryCallback.PER_PAGE).setBoundaryCallback(new MovieBoundaryCallback()).build();}//刷新数据public void refresh() {//数据库清空后,会自动重新加载new AsyncTask<Void, Void, Void>() {@Overrideprotected Void doInBackground(Void... voids) {MyDataBase.getInstance().getMovieDao().clear();return null;}}.execute();}
}

Activity中做一个下拉刷新,数据清空后,BoundaryCallback会自动加载第一次数据

package com.aruba.paging4;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;import com.aruba.paging4.adapter.MovieListAdapter;
import com.aruba.paging4.database.MyDataBase;
import com.aruba.paging4.entity.Movie;
import com.aruba.paging4.paging.viewmodel.MovieViewModel;public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MovieListAdapter movieListAdapter;private MovieViewModel movieViewModel;private SwipeRefreshLayout swipeRefreshLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyDataBase.init(this);swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);recyclerView = findViewById(R.id.recyclerView);movieListAdapter = new MovieListAdapter();recyclerView.setAdapter(movieListAdapter);recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));//实例化ViewModelmovieViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MovieViewModel.class);//设置pagedList的数据变化监听movieViewModel.pagedListLiveData.observe(this, new Observer<PagedList<Movie>>() {@Overridepublic void onChanged(PagedList<Movie> movies) {//将数据放入adaptermovieListAdapter.submitList(movies);}});swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {@Overridepublic void onRefresh() {movieViewModel.refresh();swipeRefreshLayout.setRefreshing(false);}});}}

无网络效果:

JetPack--Paging2
Demo地址:https://gitee.com/aruba/my-jetpack-application.git