Android的Kotlin秘方(II):RecyclerView 和 DiffUtil

來源:互聯網
上載者:User

標籤:

Antonio Leiva

時間:Sep 12, 2016

原文連結:http://antonioleiva.com/recyclerview-diffutil-kotlin/

 

如你所知,在【支援庫24(the Support Library 24)】中包括一個新的、適用、方便的類:DiffUtil,這使你擺脫對單元改變和更新它們的無聊和易出錯。

 

如果你還不瞭解它,可以閱讀Nicola Despotoski的這篇好文章瞭解它。這篇文章解釋怎樣容易處理它。

 

實際上,Java語言引入許多模板,而我決定研究是用Kotlin怎樣實現。

 

例子

我建立一個小APP(可以在GitHub下載)作為例子,它從一個有10項的列表中選擇項目,用於下一次RecycerView。

 

這樣,從一次迭代到下次,有些被顯示,有些消失,而有時整個全部更新。

 

如果你知道RecyclerView是怎樣工作的,你就知道在它的適配器怎樣通知那些改變,這就需要這三個方法:

  • notifyItemChanged
  • notifyItemInserted
  • notifyItemRemoved

以及它們對應的Range變化。

 

DiffUtil類將我們做所有的計算,且調用要求的notify方法。

 

原始實現方法

首次迭代,我們是從“提供者”那裡獲得這些項目,讓適配器通知變化(這即使不是最好的代碼,而可以很快的理解為什麼這樣做):

1 private fun fillAdapter() {2     val oldItems = adapter.items3     adapter.items = provider.generate()4     adapter.notifyChanges(oldItems, adapter.items)5 }

 

簡單:我儲存前面項目,產生新的項目,對適配器說notifyChanges,而用DiffUtil方法是這樣:

 1 fun notifyChanges(old: List<Content>, new: List<Content>) { 2     val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() { 3         override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 4             return old[oldItemPosition].id == new[newItemPosition].id 5         } 6     7         override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 8             return old[oldItemPosition] == new[newItemPosition] 9         }10 11         override fun getOldListSize() = old.size12        13         override fun getNewListSize() = new.size14     })15 16     diff.dispatchUpdatesTo(this)17 }

 

由於大部分代碼都是基於模板,這實在是令人討厭,但是稍後還是要返回了。

 

現在,如你所見在設定新的一組項目後我調用notifyChanges。那我們為什麼不委託那些行為?

 

用委託使通知更簡單

如果我們知道每次一組項目改變時進行通知,那麼僅需要用Delegates.observer,那麼代碼就非常棒:

 

這個activity就非常簡單了:

1 private fun fillAdapter() {2     adapter.items = provider.generate()3 }

 

“觀察者”看上去就非常不錯:

1 class ContentAdapter():RecyclerView.Adapter<ContentAdapter.ViewHolder>() {2 3     var items: List<Content> by Delegates.observable(emptyList()) {4         prop, old, new ->5                 notifyChanges(old, new)6     }7      ...8 }

 

太棒了!但是,這還可以更好。

 

用擴充函數提升適配器的能力

 

NotityChanges的大多數代碼都是模式化的。如果用資料類,我們就需要實現判斷兩個項目是否相同的方法,即使它們的內容不同。

 

在這個例子中,識別的方法是id。

 

這樣,我為這個適配器建立一個擴充函數,它將為我們做大部分困難的工作:

 1 fun <T> RecyclerView.Adapter<*>.autoNotify(old: List<T>, new: List<T>, compare: (T, T) -> Boolean) { 2     val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() { 3  4         override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 5             return compare(old[oldItemPosition], new[newItemPosition]) 6         } 7  8         override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { 9             return old[oldItemPosition] == new[newItemPosition]10         }11 12         override fun getOldListSize() = old.size13 14         override fun getNewListSize() = new.size15     })16 17     diff.dispatchUpdatesTo(this)18 }

 

這個函數接收兩組項目,和另一個函數。這最後參數將在areItemsTheSame中使用,以確定兩組項目是否相同。

 

現在調用是這樣了:

1 var items: List<Content> by Delegates.observable(emptyList()) {2     prop, old, new ->3             autoNotify(old, new) { o, n -> o.id == n.id }4 }

 

組合使用

我能理解你非常不喜歡前面的解決方案。而在特定情況下,你不要所有適配器都使用它。

 

但是,有一個替換方法:介面。

 

悲哀的是,在Kotlin預覽上的某些位置上,介面不能擴充類(我非常希望在將來能夠增加它)。這讓你用擴充類的方法,強制類實現介面類型。

 

但是,我們將擴充函數移入介面內部,也能夠獲得類似的結果:

1 interface AutoUpdatableAdapter {2 3     fun <T> RecyclerView.Adapter<*>.autoNotify(old: List<T>, new: List<T>, compare: (T, T) -> Boolean) {4         ...5     }6 }

 

適配器只需要實現這個介面:

1 class ContentAdapter() : RecyclerView.Adapter<ContentAdapter.ViewHolder>(), AutoUpdatableAdapter {2     ....3 }

 

這就是所有代碼,其它保持不變。

 

結論

有幾個方法用DiffUtls使代碼看起比Java更好、更簡單。

 

如果你需要嘗試其它解決方案,從程式碼程式庫擷取,並刪除一些特殊注釋。

 

Android的Kotlin秘方(II):RecyclerView 和 DiffUtil

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.