Paging library trong Android

Paging là 1 chức năng rất phổ biến đối với 1 ứng dụng Andorid. Bây giờ bạn có thể hoàn thiện chức năng này rất dễ dàng với thư viện Paging.

Bước 1: Add thư viện vào app

implementation 'android.arch.paging:runtime:1.0.0'
   
implementation 'android.arch.lifecycle:extensions:1.1.1'

implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'

Bước 2: Setup viewmodel

ViewModel sẽ chịu trách nhiệm tạo PagedList cùng với các cấu hình của nó và gửi đến activity để có thể observe các thay đổi dữ liệu và chuyển nó tới adapter.

Vậy PagedList là gì? PagedList là một danh sách các wrapprt chứa các data item và gọi DataSource để load các item. Nó thường bao gồm một background executor (để lấy dữ liệu) và foreground executor (cập nhật giao diện người dùng với dữ liệu).

Một PageList có 4 tham số quan trọng: a. setEnablePlaceholders (boolean enablePlaceholders) - Enabling placeholders có nghĩa là có một placeholder hiển thị cho người dùng đến khi dữ liệu được tải đầy đủ.

b. setInitialLoadSizeHint (int initialLoadSizeHint) - Số lượng các item cần tải ban đầu. c. setPageSize (int pageSize) - Số lượng các item cần tải trong PagedList. d. setPrefetchDistance (int prefetchDistance) - Số lần tải trước.

Dưới đây là class ViewModel:

    public class FeedViewModel extends ViewModel {

    private Executor executor;
    private LiveData<NetworkState> networkState;
    private LiveData<PagedList<Article>> articleLiveData;

    public FeedViewModel() {
        init();
    }

    private void init() {
        executor = Executors.newFixedThreadPool(5);

        FeedDataFactory feedDataFactory = new FeedDataFactory();
        networkState = Transformations.switchMap(feedDataFactory.getMutableLiveData(),
                dataSource -> dataSource.getNetworkState());

        PagedList.Config pagedListConfig =
                (new PagedList.Config.Builder())
                        .setEnablePlaceholders(false)
                        .setInitialLoadSizeHint(10)
                        .setPageSize(20).build();

        articleLiveData = (new LivePagedListBuilder(feedDataFactory, pagedListConfig))
                .setFetchExecutor(executor)
                .build();
    }

    /* 
     * Getter method for the network state
     */
    public LiveData<NetworkState> getNetworkState() {
        return networkState;
    }

    /* 
     * Getter method for the pageList
     */  
    public LiveData<PagedList<Article>> getArticleLiveData() {
        return articleLiveData;
    }
}

Step 3: Setup PagedListAdapter

PagedListAdapter là implementaiton của RecyclerView.Adapter, present dữ liệu từ một PagedList. Nó sử dụng DiffUtil như một tham số để tính toán sự khác biệt dữ liệu và thực hiện tất cả các bản cập nhật cho bạn. DiffUtil được định nghĩa trong Class Model:

    public class Article implements Parcelable {

    public static DiffUtil.ItemCallback<Article> DIFF_CALLBACK = new DiffUtil.ItemCallback<Article>() {
        @Override
        public boolean areItemsTheSame(@NonNull Article oldItem, @NonNull Article newItem) {
            return oldItem.id == newItem.id;
        }

        @Override
        public boolean areContentsTheSame(@NonNull Article oldItem, @NonNull Article newItem) {
            return oldItem.equals(newItem);
        }
    };

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;

        Article article = (Article) obj;
        return article.id == this.id;
    }

Class PagedListAdapter:

    public class FeedListAdapter extends PagedListAdapter<Article, RecyclerView.ViewHolder> {

    private static final int TYPE_PROGRESS = 0;
    private static final int TYPE_ITEM = 1;

    private Context context;
    private NetworkState networkState;
  
    /* 
     * The DiffUtil is defined in the constructor
     */  
    public FeedListAdapter(Context context) {
        super(Article.DIFF_CALLBACK);
        this.context = context;
    }
  
     private boolean hasExtraRow() {
        if (networkState != null && networkState != NetworkState.LOADED) {
            return true;
        } else {
            return false;
        }
    }

    public void setNetworkState(NetworkState newNetworkState) {
        NetworkState previousState = this.networkState;
        boolean previousExtraRow = hasExtraRow();
        this.networkState = newNetworkState;
        boolean newExtraRow = hasExtraRow();
        if (previousExtraRow != newExtraRow) {
            if (previousExtraRow) {
                notifyItemRemoved(getItemCount());
            } else {
                notifyItemInserted(getItemCount());
            }
        } else if (newExtraRow && previousState != newNetworkState) {
            notifyItemChanged(getItemCount() - 1);
        }
    }
}

Step 4: Setup Activity

    public class FeedActivity extends AppCompatActivity {

    private FeedListAdapter adapter;
    private FeedViewModel feedViewModel;
    private FeedActivityBinding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        feedViewModel = ViewModelProviders.of(this).get(FeedViewModel.class);

        binding.listFeed.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        adapter = new FeedListAdapter(getApplicationContext());

        feedViewModel.getArticleLiveData().observe(this, pagedList -> {
            adapter.submitList(pagedList);
        });
     
        feedViewModel.getNetworkState().observe(this, networkState -> {
            adapter.setNetworkState(networkState);
        });

        binding.listFeed.setAdapter(adapter);
    }
}

Nguồn:https://proandroiddev.com/8-steps-to-implement-paging-library-in-android-d02500f7fffe