怎麼學習谷歌最新的元件--Jetpack?一文介紹paging的基本使用
樓主最近在學習Google爸爸最新的元件–
Jetpack
,說實話,看文件誰都看得懂,但是動起手來就徹底懵逼。就比如說,今天要介紹的
paging
的基本使用,樓主就踩了兩天的坑。這個過程中比較麻煩的一點就是,官方文件和網上大部分資料都是用kotlin語言寫的,可惜我不懂kotlin,只會寫一點小Java。
在閱讀本文之前,我預設大家都瞭解
jetpack
是什麼。因此本文不再介紹jetpack,注重介紹paging怎麼使用,本文會大量的貼出Demo程式碼,供大家理解和參考,同時也會介紹這其中我踩的坑。
本文參考資料:
在閱讀本文之前,最好是閱讀過上面兩篇官方文件。其中第一篇介紹的是
paging
的基本使用,第二篇是對第一篇的補充,主要介紹怎麼自定義
DataSource
。
1. Demo概述
在正式介紹paging 的使用之前,我們先來看看最終的效果是怎麼樣的。
效果是非常的簡單,就是有一個
RecyclerView
在顯示資料,然後我們向下滑動,滑動到一定位置,
paging
會自動請求資料,這也就是分頁請求的效果.
paging
的目的就是實現分頁載入,之前我們都是自己實現分頁,這次是Google爸爸為我們實現。不得不說,
jetpack
真香!
2. 準備components
要想使用
paging
,我們得準備幾個東西(當然,導依賴這種基本操作肯定是必須的,我就不介紹了哈)。
- DataSource:顧名思義,資料來源,獲取資料是透過它實現的。
- PagedList: 資料來源獲取的資料最終靠
PagedList
來承載。對於PagedList
,我們可以這樣來理解,它就是一頁資料的集合。每請求一頁,就是新的一個PagedList
物件。- PagedListAdapter:這個Adapter就是一個
RecyclerView
的Adapter
。不過我們在使用paging
實現RecyclerView
的分頁載入效果,不能直接繼承RecyclerView
的Adapter
,而是需要繼承PagedListAdapter
。
3. 自定義DataSource
說實話,自定義
DataSource
沒有什麼難度,但是我為什麼要單獨提出來呢?首先官方文件上,實現的是
ItemKeyedDataSource
,而我這裡實現的是
PositionalDataSource
。雖然差別不是很大,但是還是有點區別,因為在這裡我踩過兩個坑。我相信,透過這兩個坑,以後遇到類似的問題,會找到解決思路。
在貼程式碼之前,我們先來看看實現
PositionalDataSource
需要注意的地方。
首先,我們得了解一下需要實現的兩個方法:
方法名 | 含義 |
---|---|
loadInitial | 載入初始化資料,可以這麼來理解,載入的是第一頁的資料。形象的說,當我們第一次開啟頁面,需要回撥此方法來獲取資料。 |
loadRange | 當有了初始化資料之後,滑動的時候如果需要載入資料的話,會呼叫此方法。 |
現在,我們來看看最終實現的程式碼。
public class ConcertDataSource extends PositionalDataSource<Concert> { private final Retrofit mRetrofit = new Retrofit.Builder() .baseUrl(") .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); }
我們透過
LoadInitialCallback
的
onResult
方法一共傳遞了三個引數,這三個引數分別表示什麼意思?我們先來
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
傳遞的是
List
的
size
(假設為N條)。也就是說,
PositionalDataSource
最終一共只會獲取N條的資料,因為第一頁的大小恰好就等於N,所以不會呼叫
loadRange
方法去載入更多的資料。
3. 建立ViewModel
相信比較熟悉
JetPack
的同學都知道,在
Jetpack
的架構裡面,推薦每個頁面持有一個
ViewModel
物件,以保證資料的正確性和避免其他的問題。本文就不介紹
ViewModel
,不太瞭解的同學可以看一下的官方文件,這是連結:
ViewModel Overview Part of Android Jetpack。
之前,我們已經成功的自定義一個
DataSource
,現在我們來看看怎麼使用
ViewModel
跟
DataSource
連線起來。
首先,我們得建立工廠類,用來建立
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; } }
至於為什麼需要一個
MutableLiveData
將
DataSource
包裝一下,這個我也不是很瞭解,官方文件就是這麼寫的(可能是讓
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爸爸親自設計的。
5. 原始碼
為了讓大家更好理解
paging
的使用,我將我的程式碼上傳到github,供大家參考,連結:
。
6.最後
附上我的Android核心技術學習大綱,獲取相關內容來我的GitHub一起玩耍:
最近不是面試季嘛,再給大家一個福利:【2017-2019位元組跳動面試真題解析&簡歷製作PDF模板】
針對位元組跳動的面試題整理的,進行了分類,循序漸進,由基礎到深入,由易到簡。
將內容整理成了五個章節、計算機基礎面試題、資料結構和演算法面試題、Java面試題、Android面試題、其他擴充套件面試題、非技術面試題總共五個章節354頁。
獲取方式:關注我看個人介紹,或直接 免費領取
每個問題都附上1個標準參考答案,都是反覆摸索消化(真心花了很多時間),覺得寫的比較好的文章作為答案。
來節省大家自己去搜尋的時間,把時間用在正確的東西上。。
還整理了全套簡歷製作、春招困惑、HR面試等問題解析參考建議,都是福利分享給大家。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952849/viewspace-2682582/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android_Jetpack:Paging元件之BoundaryCallback的使用AndroidJetpack元件
- Jetpack架構元件學習(1)——LifeCycle的使用Jetpack架構元件
- layui 的基本使用介紹UI
- Jetpack架構元件學習(3)——Activity Results API使用Jetpack架構元件API
- 各位大俠,介紹基本學習struts的書啊?
- 學習MyBatis必知必會(2)~MyBatis基本介紹和MyBatis基本使用MyBatis
- VisionPro學習筆記(1)——軟體介紹和基本使用筆記
- Jetpack架構元件學習(2)——ViewModel和Livedata使用Jetpack架構元件ViewLiveData
- Android JetPack~ LiveData (一) 介紹與使用AndroidJetpackLiveData
- Android JetPack~ ViewModel (一) 介紹與使用AndroidJetpackView
- Jetpack Compose學習(2)——文字(Text)的使用Jetpack
- 學習變數的目的及基本資料型別介紹變數資料型別
- Qt學習第二篇(基本小元件的使用)QT元件
- hazelcast的基本介紹AST
- Android學習之 Scroller的介紹與使用Android
- Android Jetpack(2):DataBinding的基本使用AndroidJetpack
- Docker基本介紹及使用Docker
- ddddocr基本使用和介紹
- iOS開發-WKWebView的介紹與基本使用iOSWebView
- Wireshark基本介紹和學習TCP三次握手TCP
- Wireshark 基本介紹和學習 TCP 三次握手TCP
- 深度學習之遷移學習介紹與使用深度學習遷移學習
- 軟體測試學習教程—Jmeter元件介紹(二)JMeter元件
- 簡單介紹標準庫fmt的基本使用
- iOS開發- UILabel的基本介紹與使用iOSUI
- Android Jetpack: 分頁庫 (Paging Library) | 中文教學視訊AndroidJetpack
- 小米盒子怎麼看直播?最新自定義直播源教程介紹
- 表示學習介紹
- Android Jetpack Navigation基本使用AndroidJetpackNavigation
- Oracle 的基本函式介紹Oracle函式
- OC-NSArray的基本介紹
- [K8S 系列]k8s 學習一,Kubernetes 基本介紹及核心元件K8S元件
- webstorm簡單介紹,webstrom基本使用WebORM
- 陣列-在Shell指令碼中的基本使用介紹陣列指令碼
- iOS開發-列表檢視的基本介紹與使用iOS
- 『忘了再學』Shell基礎 — 27、AWK程式設計的介紹和基本使用程式設計
- C#學習 [型別系統] 基本型別介紹(10)C#型別
- NIO學習二、NIO的基本使用