Android分頁元件Paging簡單使用
title: Android分頁元件Paging簡單使用
date: 2018-10-10 17:03:23
tags: Paging
2018-12.21更新
發現loadRange()方法似乎是線上程中執行的,所以這個方法內通過同步網路請求獲取資料,重新修改了程式碼,提供了一些之前忽略的程式碼。
1.簡介:
Paging元件是Google新推出的分頁元件,可以輕鬆幫助開發者實現RecyclerView中分頁載入功能。
本文先開坑,等以後用到在詳細寫明。
推薦部落格:https://www.jianshu.com/p/1bfec9b9612c
2.Paging庫引入:
implementation 'android.arch.paging:runtime:1.0.0'
剛引入就遇到了錯誤,真是一個不友好的開始:
Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.
> java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex
一查資料,是匯入重複的包,這個出錯的地方還是google自己的東西,這豈不是
“大水衝了龍王廟,自家人打自家人”?compile 'com.android.support:design:26.1.0'
google了一下,在stackoverflow中找到了解決辦法:
https://stackoverflow.com/questions/49028119/multiple-dex-files-define-landroid-support-design-widget-coordinatorlayoutlayou
即用編譯版本改成27,並且把對應的庫版本也改為27implementation 'com.android.support:appcompat-v7:27.1.1'
compile 'com.android.support:design:27.1.1'
問題了解決了,接下來我們看看他的工作原理
3.工作原理
這是官方提供的原理圖,寫的很清楚從DataSource到PagedList, PagedListAdapter最後是我們的recyclerview,我們先眼熟這幾個名字,下文會常常出現。
3.1 先來看Adapter
public class AdapterPaging extends PagedListAdapter<VideoInfo, AdapterPaging.ViewHolder> {
private Context mContext;
public static final DiffUtil.ItemCallback<VideoInfo> mDiffCallback = new AdapterPaging.VideoInfoItemCallback();
protected AdapterPaging() {
super(mDiffCallback);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
VideoInfo videoInfo = getItem(position);
}
static class ViewHolder extends RecyclerView.ViewHolder{
private LinearLayout ll_videoInfo;
public ViewHolder(View itemView) {
super(itemView);
ll_videoInfo = itemView.findViewById(R.id.ll_video_info);
}
}
private static class VideoInfoItemCallback extends DiffUtil.ItemCallback<VideoInfo>{
@Override
public boolean areItemsTheSame(VideoInfo oldItem, VideoInfo newItem) {
return oldItem.getUrl() == newItem.getUrl();
}
@Override
public boolean areContentsTheSame(VideoInfo oldItem, VideoInfo newItem) {
return (oldItem == newItem);
}
}
}
大致的4個不同如下:
- Adapter不再繼承自RecyclerView.Adapter,改為繼承自PagedListAdapter,因為PagedListAdapter就是RecyclerView.Adapter的一個子類。
- 定義內部回撥介面VideoInfoItemCallback繼承自DiffUtil.ItemCallback<VideoInfo>,並且例項化一個父類引用指向子類(VideoInfoItemCallback)物件
public static final DiffUtil.ItemCallback<VideoInfo> mDiffCallback = new AdapterPaging.VideoInfoItemCallback();
- 重寫構造方法,無需引數傳入,呼叫父類構造方法將mDiffCallback傳入。
- 通onBindViewHolder中過呼叫getItem(position);獲得指定位置的資料物件。
因為Adapter中不再需要維護一個資料List了,PagedListAdapter中已經維護有,並且提供getItem()方法訪問。
3.2 在Activity中的使用
在使用Paging後,我們無需向Adapter中在傳入資料來源List,我們需要構造LiveData。
LiveData需要DataSource.Factory物件和PagedList.Config物件,只是例項化DataSource.Factory物件需要額外兩個步驟
DataSource.Factory是一個抽象類,例項化時需要實現create()函式,這個函式返回值是一個DataSource類物件
DataSource是一個抽象類,他有三個實現子類:(詳細參考原部落格:https://www.jianshu.com/p/95d44c5338fd)(1)PageKeyedDataSource 按頁載入,如請求資料時傳入page頁碼。
(2)ItemKeyedDataSource 按條目載入,即請求資料需要傳入其它item的資訊,如載入第n+1項的資料需傳入第n項的id。
(3)PositionalDataSource 按位置載入,如載入指定從第n條到n+20條。
所以我們捋一下思路,首先要定義一個MyDataSource繼承自DataSource的三個子類之一,
再定義一個MyDataSourceFactory繼承自DataSource.Factory,返回值是MyDataSource
然後例項化PagedList.Config,這個類提供有Builder(),比較簡單。
最後將MyDataSourceFactory物件和PagedList.Config物件傳入new LivePagedListBuilder()中得到liveData資料來源。
將liveData資料來源和Adapter繫結是通過觀察者模式實現,呼叫liveData.observe()。
//定義MyDataSource類,繼承自DataSource三個子類之一
private class MyDataSource extends PositionalDataSource<Movie.SubjectsBean>{
@Override
public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Movie.SubjectsBean> callback) {
callback.onResult(initData(), 0, 10);//initData()是我封裝的載入資料方法
}
@Override
public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Movie.SubjectsBean> callback) {
callback.onResult(syncRequestData().getSubjects());
}
}
//定義MyDataSourceFactory,是DataSource.Factory的實現類
private class MyDataSourceFactory extends DataSource.Factory<Integer, Movie.SubjectsBean>{
@Override
public DataSource<Integer, Movie.SubjectsBean> create() {
return new MyDataSource();
}
}
//將生產config和liveData的程式碼封裝在這個方法中
private void initPaging(){
PagedList.Config config = new PagedList.Config.Builder()
.setPageSize(10) //每頁顯示的詞條數
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(10) //首次載入的資料量
.setPrefetchDistance(5) //距離底部還有多少條資料時開始預載入
.build();
/**
* LiveData用LivePagedListBuilder生成
* LivePagedListBuilder 構造方法需要 DataSource.Factory和PagedList.Config
*/
LiveData<PagedList<Movie.SubjectsBean>> liveData = new LivePagedListBuilder(new MyDataSourceFactory(), config)
.build();
//觀察者模式,將Adapter註冊進去,當liveData發生改變事通知Adapter
liveData.observe(this, new Observer<PagedList<Movie.SubjectsBean>>() {
@Override
public void onChanged(@Nullable PagedList<Movie.SubjectsBean> subjectsBeans) {
adapterHomeInfo.submitList(subjectsBeans);
}
});
}
從上面的程式碼我們可以看到,資料的入口在MyDataSource的兩個重寫方法裡:
initData():就是用於資料載入的方法,可以按自己想需求來封裝。
syncRequestData():用於"載入更多"的方法,loadRange()似乎本身就處在子執行緒中,所以此處可以用同步網路請求。
initData()在初始化時呼叫,所以要求我們初始化的時候使用的是本地資料,而來不及進行網路請求。
4.載入網路資料
這麼好的控制元件,我無論如何都想用於純網路載入。想這麼做也很簡單,因為我們已經把與初始化相關的程式碼封裝到initPaging()中了。
我們只需要在合適的時候進行初始化,就能達到延遲載入的目的了。比如,我們可以用handler和網路請求實現訊息非同步處理,在handler的中呼叫initPaging(),這樣就可以不需要本地資料進行初始化了。
public static final String MOVIE_REQUEST= 1;
private Movie movie;
private MyHandler handler = new MyHandler(this);
//定義一個MyHandler,用弱引用防止記憶體洩漏
private static class MyHandler<T> extends Handler {
WeakReference<ActivityHome> weakReference;
public MyHandler(ActivityHome activityHome){
weakReference = new WeakReference<ActivityHome>(activityHome);
}
@Override
public void handleMessage(Message msg) {
ActivityHome activity = weakReference.get();
//網路狀況不好時,返回obj為null
if(null == msg.obj){
Toast.makeText(activity, "獲取資料失敗,請檢查網路", Toast.LENGTH_SHORT).show();
return;
}
switch (msg.what){
case MOVIE_REQUEST:
activity.movie = NetWorkUtil.parseJsonWithGson(msg.obj.toString(), Movie.class);
activity.initPaging();
break;
default:
break;
}
}
}
/**
* 初始化資料
* @return
*/
private List<Movie.SubjectsBean> initData(){
List<Movie.SubjectsBean> subjectsBeanList = new ArrayList<>();
subjectsBeanList = movie.getSubjects();
return subjectsBeanList;
}
/**
* 進行網路請求,同步
* @return
*/
private Movie syncRequestData(){
//將uri中pageindex對應的引數+1,然後進行同步網路請求
int currentPageNumber = Integer.parseInt(uri.getQueryParameter("pageindex"));
String url = replace(uri.toString(), "pageindex", currentPageNumber+1+"");
return NetWorkUtil.syncRequest(url, Movie.class);
}
/**
* 進行網路請求,非同步
* @return
*/
private void asynRequestData(MyHandler handler){
//將uri中pageindex對應的引數+1,然後進行非同步網路請求
int currentPageNumber = Integer.parseInt(uri.getQueryParameter("pageindex"));
String url = replace(uri.toString(), "pageindex", currentPageNumber+1+"");
NetWorkUtil.sendRequestWithOkHttp(url, MOVIE_REQUEST, handler);
}
//將Uri中的引數重新賦值
public static String replace(String url, String key, String value) {
if (!TextUtils.isEmpty(url) && !TextUtils.isEmpty(key)) {
url = url.replaceAll("(" + key + "=[^&]*)", key + "=" + value);
}
return url;
}
程式碼完成了,思路很簡單。
我們只需要在onCreate()中呼叫asynRequestData()方法,開始請求資料,資料得到後開始初始化Paging元件,之後通過同步網路請求獲得後續的資料。
關於同步網路請求syncRequest()和非同步網路請求sendRequestWithOkHttp()我就不在這裡給出了,大家可以用不同的庫去實現。
相關文章
- Android官方架構元件Paging:分頁庫的設計美學Android架構元件
- Android官方架構元件Paging-Ex:為分頁列表新增Header和FooterAndroid架構元件Header
- Android_Jetpack:Paging元件之BoundaryCallback的使用AndroidJetpack元件
- Android Jetpack: 分頁庫 (Paging Library) | 中文教學視訊AndroidJetpack
- 反思|Android 列表分頁元件Paging的設計與實現:架構設計與原理解析Android元件架構
- Android Paging分頁庫的學習(一)—— 結合本地資料進行分頁載入Android
- 如何寫一個簡單的分頁元件(原理)元件
- Android Paging分頁庫的學習(二)—— 結合Room資料庫進行分頁載入AndroidOOM資料庫
- MybatisPlus的分頁外掛簡單使用MyBatis
- Android 簡單控制元件Android控制元件
- php 分頁 分頁類 簡單實用PHP
- 簡單使用Knockout.js和Datatables.js 分頁JS
- Flutter元件之ClipRRect簡單使用Flutter元件
- django 網站實現簡單分頁Django網站
- Android事件分發機制簡單理解Android事件
- Android中Lottie的簡單使用Android
- 一次簡單的分頁優化優化
- Paging 3.0 簡介 | MAD Skills
- vue_分頁元件Vue元件
- Android 簡單瀏覽器例項-webview控制元件Android瀏覽器WebView控制元件
- Paging Library使用及原理
- 分頁元件原始碼分享元件原始碼
- 簡單網頁網頁
- [Android開源]:EasyGuideLayer: 這可能是最簡單、靈活、強大的頁面蒙層元件了!AndroidGUIIDE元件
- 原生 JS實現一個簡單分頁外掛JS
- Android最簡單的側劃選單,DrawerLayout的使用Android
- C/C++ Qt Tree與Tab元件實現分頁選單C++QT元件
- NLP——史丹佛分詞工具簡單使用分詞
- C++ Qt開發:Tab與Tree元件實現分頁選單C++QT元件
- uniapp使用z-paging外掛APP
- 告別單調,Django後臺主頁改造 - 使用AdminLTE元件Django元件
- 基於元件化開發,一個簡單的Android專案框架元件化Android框架
- 在Vue3中使用Element-Plus分頁(Pagination )元件Vue元件
- [需求建議]希望加入單頁模型(單頁分類)模型
- 直播系統搭建,簡單實現Android應用的啟動頁Android
- 簡簡單單的Vue2(簡單語法,生命週期,元件)Vue元件
- 簡單的網頁登入頁面網頁
- 使用WebSocket實現一個簡單的頁面聊天Web