Sange散華 - 一個快速實現(xiàn)RecyclerView分頁加載的輕量級庫

image

Sange(散華)

github地址

Read this in other languages: 中文, English

一個快速實現(xiàn)RecyclerView分頁加載的輕量級庫.

物品介紹:

散華是一件異常精準的武器。它具有不可思議的靈性,就好像它會自己尋找對手的弱點進行攻擊。

增加16點的力量。

增加10點的攻擊力。

殘廢(被動):在攻擊中有15%的幾率使目標殘廢。殘廢效果降低目標20%的移動速度,持續(xù)4秒。

Prepare

  1. 添加jitpack到build.gradle
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  1. 添加依賴
dependencies {
    // 替換 xyz 為具體的版本號, 例如 1.0.0
    implementation 'com.github.ssseasonnn:Sange:xyz'
}

Start

First Blood

  • 散華核心的功能是DataSource, 利用它,只需幾個簡單的步驟,即可輕松實現(xiàn)數(shù)據(jù)的初始化及分頁加載.

    在此之前, 我們得先定義好我們的數(shù)據(jù)類型,記得實現(xiàn)SangeItem接口 例如:

    class NormalItem(val number: Int): SangeItem
    
  • 接下來創(chuàng)建你自己的DataSource.

    如下所示,我們繼承了MultiDataSource, 并把SangeItem當作泛型參數(shù), 然后實現(xiàn)loadInitialloadAfter方法:

    class DemoDataSource : MultiDataSource<SangeItem>() {
    
        override fun loadInitial(loadCallback: LoadCallback<SangeItem>) {
    
            //loadInitial 將會在子線程中調(diào)用, 因此無需擔心任何耗時操作
            Thread.sleep(2000)
    
            // 加載數(shù)據(jù)
            val items = mutableListOf<SangeItem>()
            for (i in 0 until 10) {
                items.add(NormalItem(i))
            }
    
            //將加載之后的數(shù)據(jù)傳遞給 LoadCallback, 即可輕松更新RecyclerView
            loadCallback.setResult(items)
        }
    
        override fun loadAfter(loadCallback: LoadCallback<SangeItem>) {
            //loadAfter 將會在子線程中調(diào)用, 因此無需擔心任何耗時操作
            Thread.sleep(2000)
    
            val items = mutableListOf<SangeItem>()
            for (i in page * 10 until (page + 1) * 10) {
                items.add(NormalItem(i))
            }
    
            loadCallback.setResult(items)
        }
    }
    
    

    loadInitial和loadAfter方法都將在子線程中調(diào)用, 因此無需擔心在這兩個方法中做的任何耗時操作.

    數(shù)據(jù)加載完成后, 只需調(diào)用LoadCallback的setResult(list)方法即可, 散華會替你做好其他的一切工作,
    包括線程切換,通知界面更新等, 你需要做的, 僅僅只是關注于數(shù)據(jù)的加載.

  • 接下來創(chuàng)建一個用于展示的ViewHolder吧, 通過繼承散華提供的SangeViewHolder, 你可以省略很多其他繁瑣的工作.

    例如:

    class NormalViewHolder(containerView: View) :
            SangeViewHolder<SangeItem>(containerView) {
    
        override fun onBind(t: SangeItem) {
            t as NormalItem
            tv_normal_content.text = t.toString()
        }
    }
    
  • 下一步就是創(chuàng)建一個你自己的Adapter,通過繼承散華提供的SangeMultiAdapter, 你可以輕松的將DataSource結合起來.

    例如:

    class DemoAdapter(dataSource: DataSource<SangeItem>) :
            SangeMultiAdapter<SangeItem, SangeViewHolder<SangeItem>>(dataSource) {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SangeViewHolder<SangeItem> {
            return NormalViewHolder(inflate(parent, R.layout.view_holder_normal))
        }
    }
    
  • 最后, 將RecyclerView和Adapter關聯(lián)起來:

    recycler_view.layoutManager = LinearLayoutManager(this)
    recycler_view.adapter = DemoAdapter(DemoDataSource())
    

    就是這樣, 你無需關心分頁的邏輯, 你只需要專注于你真正應該關注的東西: 加載數(shù)據(jù), 其他的就交給散華吧!

Double Kill

到目前為止我們一切進展很順利, 可是似乎缺少了分頁加載的狀態(tài)顯示, 下面來實現(xiàn)它吧.

  • 為了顯示加載的狀態(tài),我們先創(chuàng)建一個表示狀態(tài)的數(shù)據(jù)類型:

    class StateItem(val state: Int, val retry: () -> Unit) : SangeItem {
        override fun viewType() = STATE
    }
    

    如你所見, 我們同樣實現(xiàn)了SangeItem接口, 并且實現(xiàn)了viewType方法, 在該方法中返回了一個新的Type類型

  • 接著我們稍微改造一下DataSource,我們實現(xiàn)一個額外的方法: onStateChanged(newState).

    class DemoDataSource : MultiDataSource<SangeItem>() {
    
        override fun loadInitial(loadCallback: LoadCallback<SangeItem>) {
            //...
        }
    
        override fun loadAfter(loadCallback: LoadCallback<SangeItem>) {
            //...
        }
    
        override fun onStateChanged(newState: Int) {
            //利用DataSource的setState方法, 添加一個額外的狀態(tài)Item
            setState(StateItem(state = newState, retry = ::retry))
        }
    }
    

    這個方法會在分頁加載的不同階段來調(diào)用,用來告訴我們目前DataSource的狀態(tài), 例如加載中, 加載失敗, 加載成功等.
    通過實現(xiàn)這個方法, 我們便可以自行控制加載狀態(tài)的顯示與否, 以及對顯示的樣式進行定制.

    如上所示, 我們添加了一個用來表示狀態(tài)的數(shù)據(jù)Item項.

  • 同樣的, 我們需要一個渲染State的ViewHolder:

    class StateViewHolder(containerView: View) :
            SangeViewHolder<SangeItem>(containerView) {
    
        override fun onBind(t: SangeItem) {
            super.onBind(t)
            t as StateItem
    
            tv_state_content.setOnClickListener {
                t.retry()
            }
    
            when {
                t.state == FetchingState.FETCHING -> {
                    state_loading.visibility = View.VISIBLE
                    tv_state_content.visibility = View.GONE
                }
                t.state == FetchingState.FETCHING_ERROR -> {
                    state_loading.visibility = View.GONE
                    tv_state_content.visibility = View.VISIBLE
                }
                t.state == FetchingState.DONE_FETCHING -> {
                    state_loading.visibility = View.GONE
                    tv_state_content.visibility = View.GONE
                }
                else -> {
                    state_loading.visibility = View.GONE
                    tv_state_content.visibility = View.GONE
                }
            }
        }
    }
    
  • 最后,顯示加載狀態(tài)

    class DemoAdapter(dataSource: DataSource<SangeItem>) :
            SangeMultiAdapter<SangeItem, SangeViewHolder<SangeItem>>(dataSource) {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SangeViewHolder<SangeItem> {
            return when (viewType) {
                STATE -> StateViewHolder(inflate(parent, R.layout.view_holder_state))
                else -> NormalViewHolder(inflate(parent, R.layout.view_holder_normal))
            }
        }
    }
    
    
    

Triple Kill

  • 刷新與重試

    散華的DataSource提供了 invalidate()retry() 方法, 當需要刷新數(shù)據(jù)時, 調(diào)用 invalidate() 方法即可,
    當加載失敗需要重試時, 調(diào)用 retry() 方法即可

  • 定制DiffCallback

    散華使用了DiffUtil來高效的更新RecyclerView,你可以根據(jù)你的實際情況來改變比較邏輯:

    class NormalItem(val i: Int) : SangeItem {
    
        override fun areContentsTheSame(other: Differ): Boolean {
            //use your own diff logic
            //...
        }
    
        override fun areItemsTheSame(other: Differ): Boolean {
            //use your own diff logic
            //...
        }
    
        override fun getChangePayload(other: Differ): Any? {
            //...
        }
    }
    

END

image

github地址

License

Copyright 2019 Season.Zlc

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容