您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Paging庫怎么在Android 中使用,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
添加分頁依賴
按照如下代碼添加依賴:
dependencies { def paging_version = "1.0.0" implementation "android.arch.paging:runtime:$paging_version" // alternatively - without Android dependencies for testing testImplementation "android.arch.paging:common:$paging_version" // optional - RxJava support, currently in release candidate implementation "android.arch.paging:rxjava2:1.0.0-rc1" }
備注: 分頁包幫助開發者在UI的列表容器中順暢地展示數據, 而不管是使用設備內部的數據庫還是從應用后端拉取數據.
庫架構
分頁庫的核心構件是PagedList類, 它是一個集合, 用于異步加載應用數據塊或者數據頁. 該類在應用的其它架構之間充當中介.
Data
每一個PagedList實例從DataSource中加載最新的應用數據. 數據從應用后端或者數據庫流入PagedList對象. 分頁包支持多樣的應用架構, 包括脫機數據庫和與后臺服務器通訊的數據庫.
UI
PagedList類通過PagedListAdapter加載數據項到RecyclerView里面. 在加載數據的時候, 這些類協同工作, 拉取數據并展示內容, 包括預取看不見的內容并在內容改變時加載動畫.
支持不同的數據架構
分頁包支持應用架構, 包括應用拉取數據的地方是從后臺服務器, 還是本機數據庫, 還是兩者的結合.
只有網絡
要展示后臺數據, 需要使用Retrofit的同步版本, 加載信息到自定義的DataSource對象中.
備注: 分頁包的DataSource對象并沒有提供任何錯誤處理機制, 因為不同的應用需要用不同的方式處理和展示UI錯誤. 如果錯誤發生了, 順從結果的回調, 然后稍后重試.
只有數據庫
要設置RecyclerView觀測本地存儲, 偏向于使用Room持久化庫. 用這種方式, 無論任何時候數據庫數據插入或者修改, 這些改變會自動地在負責展示這些數據的RecyclerView展示出來.
網絡+數據庫
在開始觀測數據庫之后, 你能夠通過使用PagedList.BoundaryCallback來監聽數據庫什么時候過期. 之后, 你可能從網絡拉取更多的數據, 并把它們插入到數據庫中. 如果UI正在展示數據庫, 以上就是你所需要做的全部.
下面的代碼片斷展示了BoundaryCallback的使用實例:
class ConcertViewModel { fun search(query: String): ConcertSearchResult { val boundaryCallback = ConcertBoundaryCallback(query, myService, myCache) // Error-handling not shown in this snippet. val networkErrors = boundaryCallback.networkErrors } } class ConcertBoundaryCallback( private val query: String, private val service: MyService, private val cache: MyLocalCache ) : PagedList.BoundaryCallback<Concert>() { override fun onZeroItemsLoaded() { requestAndSaveData(query) } override fun onItemAtEndLoaded(itemAtEnd: Concert) { requestAndSaveData(query) } }
處理網絡錯誤
在使用網絡拉取或者分頁的數據, 而這些數據正在使用分頁包展示的時候, 不總是把網絡分為要么"可用"要么"不可能"是很重要的, 因為許多連接是間歇性或者成片的:
特定的服務器可能不能響應網絡請求;
設備可能聯接了慢的或者弱的網絡;
應用應該檢查每一個請求是否成功, 并且在網絡不可用的情形下, 盡可能快地恢復. 比如, 你可以為用戶提供一個"重試"按鈕, 如果數據沒有刷新成功的話. 如果在數據分頁期間發生錯誤, 最好自動地重新分頁請求.
更新已有應用
如果應用已經從網絡或者數據庫消費數據, 很大可能可以直接升級到分頁庫提供的功能.
自定義分頁解決方案
如果你使用了自定義功能加載數據源中的小的數據集, 你可以使用PagedList類取代這個邏輯. PagedList類實例提供了內建的連接, 到通用的數據源. 這些實例也提供了在應用中引用的RecyclerView的適配器.
使用列表而非分頁加載的數據
如果你使用內存里的列表作為UI適配器的后備數據結構, 考慮使用PagedList類觀測數據更新, 如果列表中數據項變得很多的話. PagedList實例既可以使用LiveData<PagedList>也可以使用Observable<List>對UI傳遞數據更新, 同時最小化了加載時間和內存使用. 然而, 應用中使用PagedList對象代替List并不要求對UI結構和數據更新邏輯作任何改變.
使用CursorAdapter將數據cursor與列表視圖聯系起來
應用也許會使用CursorAdapter將數據從Cursor跟ListView連接起來. 在這種情況下, 通常需要從ListView遷移到RecyclerView, 然后使用Room或者PositionalDataSource構件代替Cursor, 當然, 這主要依據于Cursor實例能否訪問SQLite數據庫.
在一些情況下, 比如使用Spinner實例的時候, 你僅僅提供了Adapter本身. 然后一個庫使用了加載進adapter中的數據, 并展示了數據. 在這些情況下, 把adapter數據類型轉化為LiveData<PagedList>, 之后在嘗試使用將這些數據項在UI中填充起來之前, 將這個列表在ArrayAdapter對象中包裹起來.
使用AsyncListUtil異步加載內容
如果你在使用AsyncListUtil對象異步地加載和展示分組信息的話, 分頁包將會使得加載數據更加方便:
數據并不需要定位. 分頁包讓你直接從后臺使用網絡提供的鍵加載數據.
數據量太大. 使用分頁包可以將數據加載分頁直到沒有任何數據留下.
更方便地觀測數據. 分頁包能夠展示應用在可觀測數據結構中持有的ViewModel.
數據庫例子
使用LiveData觀測分頁數據
下面的示例代碼展示了所有一起工作的碎片. 當演唱會事件在數據庫中添加, 刪除或者修改的修改的時候, RecyclerView中的內容自動且高效地更新:
@Dao interface ConcertDao { // The Integer type parameter tells Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM user ORDER BY concert DESC") fun concertsByDate(): DataSource.Factory<Int, Concert> } class MyViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: LiveData<PagedList<Concert>> = LivePagedListBuilder( concertDao.concertsByDate(), /* page size */ 20 ).build() } class MyActivity : AppCompatActivity() { public override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) val viewModel = ViewModelProviders.of(this) .get(MyViewModel::class.java!!) val recyclerView = findViewById(R.id.concert_list) val adapter = ConcertAdapter() viewModel.concertList.observe(this, { pagedList -> adapter.submitList(pagedList) }) recyclerView.setAdapter(adapter) } } class ConcertAdapter() : PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) { fun onBindViewHolder(holder: ConcertViewHolder, position: Int) { val concert = getItem(position) if (concert != null) { holder.bindTo(concert) } else { // Null defines a placeholder item - PagedListAdapter automatically // invalidates this row when the actual object is loaded from the // database. holder.clear() } } companion object { private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() { // Concert details may have changed if reloaded from the database, // but ID is fixed. override fun areItemsTheSame(oldConcert: Concert, newConcert: Concert): Boolean = oldConcert.id == newConcert.id override fun areContentsTheSame(oldConcert: Concert, newConcert: Concert): Boolean = oldConcert == newConcert } } }
使用RxJava2觀測分頁數據
如果你偏愛使用RxJava2而非LiveData, 那么你可以創建Observable或者Flowable對象:
class MyViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: Flowable<PagedList<Concert>> = RxPagedListBuilder( concertDao.concertsByDate(), /* page size */ 50 ).buildFlowable(BackpressureStrategy.LATEST) }
之后你可以按照如下代碼開始和停止觀測數據:
class MyActivity : AppCompatActivity() { private lateinit var adapter: ConcertAdapter<Concert> private lateinit var viewModel: MyViewModel private val disposable = CompositeDisposable() public override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) val recyclerView = findViewById(R.id.concert_list) viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java!!) adapter = ConcertAdapter() recyclerView.setAdapter(adapter) } override fun onStart() { super.onStart() disposable.add(viewModel.concertList.subscribe({ flowableList -> adapter.submitList(flowableList) })) } override fun onStop() { super.onStop() disposable.clear() } }
基于RxJava2解決方案的ConcertDao和ConcertAdapter代碼, 和基于LiveData解決方案的代碼是一樣的.
UI構件及其出發點
將UI和視圖模型聯接起來
你可以按照如下方式, 將LiveData<PagedList>實例跟PagedListAdapter聯系起來:
private val adapter = ConcertPagedListAdapter() private lateinit var viewModel: ConcertViewModel override fun onCreate(savedInstanceState: Bundle?) { viewModel = ViewModelProviders.of(this) .get(ConcertViewModel::class.java) viewModel.concerts.observe(this, adapter::submitList) }
當數據源提供一個新PagedList實例的時候, activity會將這些對象改善給adapter. PagedListAdapter實現, 定義了更新如何計算, 自動地處理分頁和列表不同. 由此, 你的ViewHolder只需要綁定到特定的提供項:
class ConcertPagedListAdapter() : PagedListAdapter<Concert, ConcertViewHolder>( object : DiffUtil.ItemCallback<Concert>() { // The ID property identifies when items are the same. override fun areItemsTheSame(oldItem: Concert, newItem: Concert) = oldItem.id = newItem.id // Use the "==" operator (or Object.equals() in Java-based code) to know // when an item's content changes. Implement equals(), or write custom // data comparison logic here. override fun areContentsTheSame(oldItem: Concert, newItem: Concert) = oldItem.name == newItem.name && oldItem.date == newItem.date } ) { override fun onBindViewHolder(holder: ConcertViewHolder, position: Int) { val concert: Concert? = getItem(position) // Note that "concert" is a placeholder if it's null holder.bind(concert) } }
PagedListAdapter使用PagedList.Callback對象處理分頁加載事件. 當用戶滑動時, PagedListAdapter調用PagedList.loadAround()方法將從DataSource中拉聚攏數據項提示提供給基本的PagedList.
備注: PageList是內容不可變的. 這意味著, 盡管新內容能夠被加載到PagedList實例中, 但已加載項一旦加載完成便不能發生改變. 由此, 如果PagedList中的內容發生改變, PagedListAdapter對象將會接收到一個包含已更新信息的全新的PagedList.
實現diffing回調
先前的代碼展示了areContentsTheSame()的手動實現, 它比較了對象的相關的域. 你也可以使用Java中的Object.equals()方法或者Kotlin中的==操作符. 但是要確保要么實現了對象中的equals()方法或者使用了kotlin中的數據對象.
使用不同的adapter類型進行diffing
如果你選擇不從PagedListAdapter繼承--比如你在使用一個提供了自己的adapter的庫的時候--你依然可以通過直接使用AsyncPagedListDiffer對象使用分頁包adapter的diffing功能.
在UI中提供占位符
在應用完成拉取數據之前, 如果你想UI展示一個列表, 你可以向用戶展示占位符列表項. RecyclerView通過將列表項臨時地設置為null來處理這個情況.
備注: 默認情況下, 分頁包開啟了占位符行為.
占位符有如下好處:
支持scrollbar. PagedList向PagedListAdapter提供了大量的列表項. 這個信息允許adapter繪制一個表示列表已滿的scrollbar. 當新的頁加載時, scrollbar并不會跳動, 因為列表是并不沒有改變它的size.
不需要"正在加載"旋轉指針. 因為列表大小已知, 沒必要提醒用戶有更多的數據項正在加載. 占位符本身表達了這個信息.
在添加占位符的支持之前, 請牢記以下先置條件:
要求集合中數據可數. 來自Room持久化庫的DataSource實例能夠高效地計算數據項. 然而, 如果你在用自定義本地存儲方案或者只有網絡的數據架構, 想了解數據集中有多少數據項可能代價很高, 甚至不可能.
要求adapter負責未加載數據項. 你正在使用的adapter或者展示機制來準備填充列表, 需要處理null列表項. 比如, 當將數據綁定到ViewHolder的時候, 你需要提供默認值表示未加載數據.
要求數據相同數量的item view. 如果列表項數目能夠基于內容發生改變, 比如, 社交網絡更新, 交叉淡入淡出看起來并不好. 在這種情況下, 強烈推薦禁掉占位符.
數據構件及其出發點
構建可觀測列表
通常情況下, UI代碼觀測LiveData<PagedList>對象(或者, 如果你在使用RxJava2, 是Flowable<PagedList>/Observable<PagedList>對象), 這個對象存在于應用的ViewModel中. 這個可觀測對象形成了應用列表數據內容和展示的連接.
要創建這么一個可觀測PagedList對象, 需要將DataSource.Factory實例傳給LivePageListBuilder/RxPagedListBuilder對象. 一個DataSource對象對單個PagedList加載分頁. 這個工廠類為內容更新創建PagedList實例, 比如數據庫表驗證, 網絡刷新等. Room持久化庫能夠提供DataSource.Factory, 或者自定義.
如下代碼展示了如何在應用的ViewModel類中使用Room的DataSource.Factory構建能力創建新的LiveData<PagedaList>實例:
ConcertDao.kt:
interface ConcertDao { // The Integer type parameter tells Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM concerts ORDER BY date DESC") public abstract DataSource.Factory<Integer, Concert> concertsByDate() }
ConcertViewModel.kt:
// The Integer type argument corresponds to a PositionalDataSource object. val myConcertDataSource : DataSource.Factory<Integer, Concert> = concertDao.concertsByDate() val myPagedList = LivePagedListBuilder(myConcertDataSource, /* page size */ 20) .build()
定義分頁配置
要想為復雜情形更深入地配置LiveData<PagedList>, 你也可以定義自己的分頁配置. 尤其是, 你可以定義如下屬性:
頁大小: 每一頁的數據量.
預取距離: 給定UI中最后可見項, 超過該項之后多少項, 分頁包要嘗試提前提取數據. 這個值應該比page size大幾倍.
占位符展示: 決定了UI是否會為還沒有完成加載的數據項展示占位符.
如果你想要對分布包從數據庫加載中設置更多的控件, 要像下面的代碼一樣, 傳遞自定義的Executor對象給LivePagedListBuilder:
EventViewModel.kt:
val myPagingConfig = PagedList.Config.Builder() .setPageSize(50) .setPrefetchDistance(150) .setEnablePlaceholders(true) .build() // The Integer type argument corresponds to a PositionalDataSource object. val myConcertDataSource : DataSource.Factory<Integer, Concert> = concertDao.concertsByDate() val myPagedList = LivePagedListBuilder(myConcertDataSource, myPagingConfig) .setFetchExecutor(myExecutor) .build()
選擇正確的數據源類型
連接更最好地處理源數據結構的數據源很重要:
如果加載的頁嵌套了之前/之后頁的key的話, 使用PageKeyDataSource. 比如, 比如你正在從網絡中拉取社交媒體博客, 你也許需要傳遞從一次加載向下一次加載的nextPage token.
如果需要使用每N項數據項的數據拉取每N+1項的話, 使用ItemKeyedDataSource. 比如, 你在為一個討論型應用拉取螺紋評論, 你可能需要傳遞最后一條評論的ID來獲取下一條評論的內容.
如果你需要從數據商店中的任意位置拉取分頁數據的話, 使用PositionalDataSource. 這個類支持請求任意位置開始的數據集. 比如, 請求也許返回從位置1200開始的20條數據.
通知數據非法
在使用分頁包時, 在表或者行數據變得陳腐時, 取決于數據層來通知應用的其它層. 要想這么做的話, 需要從DataSource類中調用invalidate()方法.
備注: UI也可以使用"滑動刷新"模式來觸發數據非法功能.
構建自己的數據源
如果你使用了自定義的數據解決方案, 或者直接從網絡加載數據, 你可以實現一個DataSource子類. 下面的代碼展示了數據源從給定的concert起始時間切斷:
class ConcertTimeDataSource(private val concertStartTime: Date) : ItemKeyedDataSource<Date, Concert>() { override fun getKey(item: Concert) = item.startTime override fun loadInitial( params: LoadInitialParams<Date>, callback: LoadInitialCallback<Concert>) { val items = fetchItems(concertStartTime, params.requestedLoadSize) callback.onResult(items) } override fun loadAfter( params: LoadParams<Date>, callback: LoadCallback<Concert>) { val items = fetchItemsAfter( date = params.key, limit = params.requestedLoadSize) callback.onResult(items) } }
通過創建真實的DataSource.Factory子類, 你之后能夠加載自定義的數據到PagedList對象. 下面的代碼展示了如何創建在之前代碼中定義的自定義數據源:
class ConcertTimeDataSourceFactory(private val concertStartTime: Date) : DataSource.Factory<Date, Concert>() { val sourceLiveData = MutableLiveData<ConcertTimeDataSource>() override fun create(): DataSource<Date, Concert> { val source = ConcertTimeDataSource(concertStartTime) sourceLiveData.postValue(source) return source } }
考慮內容更新
當你構建可觀測PagedList對象的時候, 考慮一下內容是如何更新的. 如果你直接從Room數據庫中加載數據, 更新會自動地推送到UI上面.
如果你在使用分頁的網絡API, 通常你會有用戶交互, 比如"滑動刷新", 把它作為信號去驗證當前DataSource非法并請求一個新的. 這個行為出行在下面的代碼中:
class ConcertActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { ... concertViewModel.refreshState.observe(this, Observer { swipeRefreshLayout.isRefreshing = it == NetworkState.LOADING }) swipeRefreshLayout.setOnRefreshListener { concertViewModel.invalidateDataSource() } } }
提供數據表現之間的映射
對于DataSource加載的數據, 分頁包支持基于數據項和基于頁的轉換.
下面的代碼中, concert名和日期的聯合被映射成包含姓名和日期的字符串:
class ConcertViewModel : ViewModel() { val concertDescriptions : LiveData<PagedList<String>> init { val factory = database.allConcertsFactory() .map { concert -> concert.name + " - " + concert.date } concerts = LivePagedListBuilder(factory, 30).build() } } }
如果在數據加載之后, 想要包裹, 轉換或者準備item, 這將非常有用. 因為這個工作是在獲取執行器中完成的, 你可以在其中執行花銷巨大的工作, 比如, 從硬盤中讀取, 查詢數據庫等.
關于Paging庫怎么在Android 中使用就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。