怎么使用Jetpack paging

发布时间:2021-11-15 14:20:14 作者:iii
来源:亿速云 阅读:167

本篇内容主要讲解“怎么使用Jetpack paging”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用Jetpack paging”吧!

1. Demo概述

在正式介绍paging 的使用之前,我们先来看看最终的效果是怎么样的。

怎么使用Jetpack paging

效果是非常的简单,就是有一个RecyclerView在显示数据,然后我们向下滑动,滑动到一定位置,paging会自动请求数据,这也就是分页请求的效果.paging的目的就是实现分页加载,之前我们都是自己实现分页,这次是Google爸爸为我们实现。不得不说,jetpack真香!

2. 准备components

要想使用paging,我们得准备几个东西(当然,导依赖这种基本操作肯定是必须的,我就不介绍了哈)。

  1. DataSource:顾名思义,数据源,获取数据是通过它实现的。

  2. PagedList: 数据源获取的数据最终靠PagedList来承载。对于PagedList,我们可以这样来理解,它就是一页数据的集合。每请求一页,就是新的一个PagedList对象。

  3. PagedListAdapter:这个Adapter就是一个RecyclerViewAdapter。不过我们在使用paging实现RecyclerView的分页加载效果,不能直接继承RecyclerViewAdapter,而是需要继承PagedListAdapter

3. 自定义DataSource

说实话,自定义DataSource没有什么难度,但是我为什么要单独提出来呢?首先官方文档上,实现的是ItemKeyedDataSource,而我这里实现的是PositionalDataSource。虽然差别不是很大,但是还是有点区别,因为在这里我踩过两个坑。我相信,通过这两个坑,以后遇到类似的问题,会找到解决思路。
  在贴代码之前,我们先来看看实现PositionalDataSource需要注意的地方。
  首先,我们得了解一下需要实现的两个方法:

方法名含义
loadInitial加载初始化数据,可以这么来理解,加载的是第一页的数据。形象的说,当我们第一次打开页面,需要回调此方法来获取数据。
loadRange当有了初始化数据之后,滑动的时候如果需要加载数据的话,会调用此方法。

现在,我们来看看最终实现的代码。

  public class ConcertDataSource extends PositionalDataSource<Concert> {
      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<Concert> 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<Concert> 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<Concert> fetchItems(int startPosition, int pageSize) {
          List<Concert> 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<Bean>() {
              @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 LoadInitialCallback<Concert> 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);
      }

我们通过LoadInitialCallbackonResult方法一共传递了三个参数,这三个参数分别表示什么意思?我们先来LoadInitialCallback这个类吧。

    public abstract static class LoadInitialCallback<T> {
        public abstract void onResult(@NonNull List<T> data, int position, int totalCount);
        public abstract void onResult(@NonNull List<T> data, int position);
    }

LoadInitialCallback一共有两个onResult方法。而我这里出现了RecyclerView不显示任何数据,是因为我调用得是两个参数的onResult方法,导致了totalCount为0,所以显示不出任何数据。
  第二个问题就是,数据显示出来了,但是始终显示一页的数据,也就是说loadRange方法不会调用。其实也是totalCount导致的,当时我解决了第一个的问题时,直接给totalCount传递的是Listsize(假设为N条)。也就是说,PositionalDataSource最终一共只会获取N条的数据,因为第一页的大小恰好就等于N,所以不会调用loadRange方法去加载更多的数据。

3. 创建ViewModel

相信比较熟悉JetPack的同学都知道,在Jetpack的架构里面,推荐每个页面持有一个ViewModel对象,以保证数据的正确性和避免其他的问题。本文就不介绍ViewModel,不太了解的同学可以看一下的官方文档,这是链接:  ViewModel Overview Part of Android Jetpack。
  之前,我们已经成功的自定义一个DataSource,现在我们来看看怎么使用ViewModelDataSource连接起来。
  首先,我们得创建工厂类,用来创建DataSource对象,这在Jetpack架构里面是基本需要。

  public class ConcertFactory extends DataSource.Factory<Integer, Concert> {
      private MutableLiveData<ConcertDataSource> mSourceLiveData =
              new MutableLiveData<>();
      @Override
      public DataSource<Integer, Concert> create() {
          ConcertDataSource concertDataSource = new ConcertDataSource();
          mSourceLiveData.postValue(concertDataSource);
          return concertDataSource;
      }
  }

至于为什么需要一个MutableLiveDataDataSource包装一下,这个我也不是很了解,官方文档就是这么写的(可能是让DataSource监听Activity或者Fragment的生命周期,以保证只在页面活跃状态(onStart或者onResume)获取数据)。
  其次,就是创建对应的ViewModel:

  public class ConcertViewModel extends ViewModel {
      private final LiveData<PagedList<Concert>> convertList;
      private DataSource<Integer, Concert> concertDataSource;
      public ConcertViewModel() {
          ConcertFactory concertFactory = new ConcertFactory();
          concertDataSource = concertFactory.create();
          convertList = new LivePagedListBuilder<>(concertFactory, 20).build();
      }
      public void invalidateDataSource() {
          concertDataSource.invalidate();
      }
      public LiveData<PagedList<Concert>> getConvertList() {
          return convertList;
      }
  }

4. RecyclerView

首先,我们来看一下Adapter的定义:

  public class RecyclerAdapter extends PagedListAdapter<Concert, RecyclerAdapter.RecyclerViewHolder> {
      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<Concert> DIFF_CALLBACK =
              new DiffUtil.ItemCallback<Concert>() {
                  @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”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

推荐阅读:
  1. 使用MongoDB快速分页
  2. JS 插件dropload下拉刷新、上拉加载使用小结

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

jetpack paging

上一篇:EntityFramework.Extended如何用EF实现指定字段的更新

下一篇:RestFul API知识点有哪些

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》