重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
本篇内容主要讲解“怎么使用Jetpack paging”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用Jetpack paging”吧!
为江西等地区用户提供了全套网页设计制作服务,及江西网站建设行业解决方案。主营业务为网站制作、网站设计、江西网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
在正式介绍paging 的使用之前,我们先来看看最终的效果是怎么样的。
效果是非常的简单,就是有一个RecyclerView
在显示数据,然后我们向下滑动,滑动到一定位置,paging
会自动请求数据,这也就是分页请求的效果.paging
的目的就是实现分页加载,之前我们都是自己实现分页,这次是Google爸爸为我们实现。不得不说,jetpack
真香!
要想使用paging
,我们得准备几个东西(当然,导依赖这种基本操作肯定是必须的,我就不介绍了哈)。
DataSource:顾名思义,数据源,获取数据是通过它实现的。
PagedList: 数据源获取的数据最终靠
PagedList
来承载。对于PagedList
,我们可以这样来理解,它就是一页数据的集合。每请求一页,就是新的一个PagedList
对象。PagedListAdapter:这个Adapter就是一个
RecyclerView
的Adapter
。不过我们在使用paging
实现RecyclerView
的分页加载效果,不能直接继承RecyclerView
的Adapter
,而是需要继承PagedListAdapter
。
说实话,自定义DataSource
没有什么难度,但是我为什么要单独提出来呢?首先官方文档上,实现的是ItemKeyedDataSource
,而我这里实现的是PositionalDataSource
。虽然差别不是很大,但是还是有点区别,因为在这里我踩过两个坑。我相信,通过这两个坑,以后遇到类似的问题,会找到解决思路。
在贴代码之前,我们先来看看实现PositionalDataSource
需要注意的地方。
首先,我们得了解一下需要实现的两个方法:
方法名 | 含义 |
---|---|
loadInitial | 加载初始化数据,可以这么来理解,加载的是第一页的数据。形象的说,当我们第一次打开页面,需要回调此方法来获取数据。 |
loadRange | 当有了初始化数据之后,滑动的时候如果需要加载数据的话,会调用此方法。 |
现在,我们来看看最终实现的代码。
public class ConcertDataSource extends PositionalDataSource{ private final Retrofit mRetrofit = new Retrofit.Builder() .baseUrl("http://api.apiopen.top/") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); private final Service mService; public ConcertDataSource() { mService = mRetrofit.create(Service.class); } @Override public void loadInitial(@NonNull LoadInitialParams params, @NonNull final LoadInitialCallback callback) { // fetchItem(0, params.pageSize, new Callback() { // @Override // public void onResult(Bean value) { // callback.onResult(value.getResult(), 0, 2000); // } // }); callback.onResult(fetchItems(0, 20), 0, 2000); } @Override public void loadRange(@NonNull LoadRangeParams params, @NonNull final LoadRangeCallback callback) { // fetchItem(params.startPosition, params.loadSize, new Callback() { // @Override // public void onResult(Bean value) { // callback.onResult(value.getResult()); // } // }); callback.onResult(fetchItems(params.startPosition, params.loadSize)); } private List fetchItems(int startPosition, int pageSize) { List list = new ArrayList<>(); for (int i = startPosition; i < startPosition + pageSize; i++) { Concert concert = new Concert(); concert.setAuthor("author = " + i); concert.setContent("content = " + i); concert.setTitle("title = " + i); list.add(concert); } return list; } private void fetchItem(final int startPosition, int size, final Callback callback) { mService.getCall(startPosition, size).subscribe(new Observer () { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Bean value) { callback.onResult(value); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); } private interface Callback { void onResult(Bean value); } }
然后,我结合上面的代码来介绍一下我踩过的两个坑(其实这两个坑可以归并为一个坑,因为都是一个原因导致)。
我先来描述我之前我遇到的问题,在第一次打开界面时,我发现,RecyclerView
没有显示任何的数据,同时经过debug,发现loadInitial
方法被回调到的,但是最终Adapter
里面的itemCount
为0。这是什么原因导致的呢?我们来看看现在的loadInitial
方法的实现:
@Override public void loadInitial(@NonNull LoadInitialParams params, @NonNull final LoadInitialCallbackcallback) { // fetchItem(0, params.pageSize, new Callback() { // @Override // public void onResult(Bean value) { // callback.onResult(value.getResult(), 0, 2000); // } // }); callback.onResult(fetchItems(0, 20), 0, 2000); }
我们通过LoadInitialCallback
的onResult
方法一共传递了三个参数,这三个参数分别表示什么意思?我们先来LoadInitialCallback
这个类吧。
public abstract static class LoadInitialCallback{ public abstract void onResult(@NonNull List data, int position, int totalCount); public abstract void onResult(@NonNull List data, int position); }
LoadInitialCallback
一共有两个onResult
方法。而我这里出现了RecyclerView
不显示任何数据,是因为我调用得是两个参数的onResult
方法,导致了totalCount
为0,所以显示不出任何数据。
第二个问题就是,数据显示出来了,但是始终显示一页的数据,也就是说loadRange
方法不会调用。其实也是totalCount
导致的,当时我解决了第一个的问题时,直接给totalCount
传递的是List
的size
(假设为N条)。也就是说,PositionalDataSource
最终一共只会获取N条的数据,因为第一页的大小恰好就等于N,所以不会调用loadRange
方法去加载更多的数据。
相信比较熟悉JetPack
的同学都知道,在Jetpack
的架构里面,推荐每个页面持有一个ViewModel
对象,以保证数据的正确性和避免其他的问题。本文就不介绍ViewModel
,不太了解的同学可以看一下的官方文档,这是链接:
ViewModel Overview Part of Android Jetpack。
之前,我们已经成功的自定义一个DataSource
,现在我们来看看怎么使用ViewModel
跟DataSource
连接起来。
首先,我们得创建工厂类,用来创建DataSource
对象,这在Jetpack
架构里面是基本需要。
public class ConcertFactory extends DataSource.Factory{ private MutableLiveData mSourceLiveData = new MutableLiveData<>(); @Override public DataSource create() { ConcertDataSource concertDataSource = new ConcertDataSource(); mSourceLiveData.postValue(concertDataSource); return concertDataSource; } }
至于为什么需要一个MutableLiveData
将DataSource
包装一下,这个我也不是很了解,官方文档就是这么写的(可能是让DataSource
监听Activity
或者Fragment
的生命周期,以保证只在页面活跃状态(onStart
或者onResume
)获取数据)。
其次,就是创建对应的ViewModel
:
public class ConcertViewModel extends ViewModel { private final LiveData> convertList; private DataSource concertDataSource; public ConcertViewModel() { ConcertFactory concertFactory = new ConcertFactory(); concertDataSource = concertFactory.create(); convertList = new LivePagedListBuilder<>(concertFactory, 20).build(); } public void invalidateDataSource() { concertDataSource.invalidate(); } public LiveData > getConvertList() { return convertList; } }
首先,我们来看一下Adapter
的定义:
public class RecyclerAdapter extends PagedListAdapter{ protected RecyclerAdapter() { super(DIFF_CALLBACK); } @NonNull @Override public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview, parent, false); return new RecyclerViewHolder(view); } @Override public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) { Concert concert = getItem(position); if (concert != null) { holder.mTitleTextView.setText(concert.getTitle()); holder.mAuthorTextView.setText(concert.getAuthor()); holder.mContentTextView.setText(concert.getContent()); } } public static class RecyclerViewHolder extends RecyclerView.ViewHolder { TextView mTitleTextView; TextView mAuthorTextView; TextView mContentTextView; public RecyclerViewHolder(View itemView) { super(itemView); mTitleTextView = itemView.findViewById(R.id.title); mAuthorTextView = itemView.findViewById(R.id.author); mContentTextView = itemView.findViewById(R.id.content); } } private static DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback () { @Override public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) { return oldConcert.getTitle().equals(newConcert.getTitle()); } @Override public boolean areContentsTheSame(Concert oldConcert, Concert newConcert) { return oldConcert.equals(newConcert); } }; }
在使用PagedListAdapter
时,我们需要注意的是,PagedListAdapter
内部默认实现DiffUtil
来进行数据的差量计算,所以我们在构造方法里面传递一个DiffUtil.ItemCallback
。至于DiffUtil
是什么,以及它内部的算法实现,我们这里就不介绍,有兴趣的同学可以关注楼主的RecyclerView
源码分析系列文章,在不久的将来,我会详细的分析它的内部实现。
然后,我们再来看看Activity
里面是怎么使用的:
public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private RecyclerAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = findViewById(R.id.recyclerView); adapter = new RecyclerAdapter(); ConcertViewModel viewModel = ViewModelProviders.of(this).get(ConcertViewModel.class); viewModel.getConvertList().observe(this, concerts -> adapter.submitList(concerts)); mRecyclerView.setAdapter(adapter); mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); mRecyclerView.addItemDecoration(new SimpleDividerDecoration(this)); } }
整个结构就这样的,是不是非常简单?不过,到这里可能有人可能有疑问,我就实现了一个RecyclerView
加载更多的数据,你就让我定义这么多的类,是不是有点小题大做呢?
其实不然,Jetpack
是Google官方推荐的一种App架构方案,在这种结构下,会避免很多的问题,比如说内存泄漏和空指针异常等等。之所以使用这种方案来架构App,不是我们吃饱撑的没事干,而是为了解决传统架构方案下不能解决的问题。当然可能还有其他的第三方解决方案,但是肯定不比Jetpack
权威,毕竟Google爸爸亲自设计的。
到此,相信大家对“怎么使用Jetpack paging”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!