1. 程式人生 > >Kotlin Recipes for Android (II): RecyclerView and DiffUtil

Kotlin Recipes for Android (II): RecyclerView and DiffUtil

As you may know, the Support Library 24 included a new really handy class called DiffUtil, which will let you get rid of the boring and error prone of detecting which cells changed and update them

If you haven’t heard about it, you can take a look at this nice article from Nicola Despotoski, which explains quite easily how to deal with it.

As usual, Java language introduces a lot of boilerplate, and I decided to investigate what we can get when implementing this using Kotlin.

The example

For the example, I’m creating a small App (which you can download from Github) that randomly selects the set of items that will be next included in the RecyclerView

from a list of 10.

diffutil-recyclerview

So, from one iteration to the next one, some of them will be repeated, some of them will have disappeared, and some other will enter as new.

If you know how RecyclerView works, you may know that to notify those changes to its adapter, you have these three methods:

  • notifyItemChanged
  • notifyItemInserted
  • notifyItemRemoved

And their corresponding Range variations.

Want to learn Kotlin?

Check my free guide to create your first project in 15 minutes!

The DiffUtil class will do all the calculations for us, and will call the required notify methods.

The raw implementation

For a first iteration, we could just get the items from a provider, and let the adapter notify the difference (not the best code ever, but you’ll understand soon why it’s done like that):

   private fun fillAdapter() {
        val oldItems = adapter.items
        adapter.items = provider.generate()
        adapter.notifyChanges(oldItems, adapter.items)
    }

Easy: I save the previous items, generate the new ones, and say the adapter to notifyChanges, which is a method that uses DiffUtil:

	    fun notifyChanges(oldList: List<Content>, newList: List<Content>) {

        val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {

            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                return oldList[oldItemPosition].id == newList[newItemPosition].id
            }

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                return oldList[oldItemPosition] == newList[newItemPosition]
            }

            override fun getOldListSize() = oldList.size

            override fun getNewListSize() = newList.size
        })

        diff.dispatchUpdatesTo(this)
    }

This is really annoying, because most part of the code is basically boilerplate, but we’ll get back to it later.

Now, you saw that I’m calling notifyChanges just after setting the new set of items.

So why don’t we delegate that behaviour?

Using delegation to make notifications simpler

If we know that we are notifying every time the set of items change, let’s just use Delegates.observer, which leaves an awesome code:

The activity is much simpler:

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

And the observer looks quite nice:

class ContentAdapter() : RecyclerView.Adapter<ContentAdapter.ViewHolder>() {

    var items: List<Content> by Delegates.observable(emptyList()) {
        prop, oldList, newList ->
        notifyChanges(oldList, newList)
    }
    ...
}

Awesome! But this can be better.

Using extension functions to improve adapter capabilities

Most of the code in notifyChanges is boilerplate. If we use data clases, we just need to implement a way to detect that two items are the same, even when their content isn’t.

In the example, the way to identify this is the id.

So I’ll create an extension function for the adapter that will do most of the hard work for us:

fun <T> RecyclerView.Adapter<*>.autoNotify(oldList: List<T>, newList: List<T>, compare: (T, T) -> Boolean) {
    val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return compare(old[oldItemPosition], newList[newItemPosition])
        }

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return oldList[oldItemPosition] == newList[newItemPosition]
        }

        override fun getOldListSize() = oldList.size

        override fun getNewListSize() = newList.size
    })

    diff.dispatchUpdatesTo(this)
}

The function receives the two sets of items, and another function. This last parameter will be used in areItemsTheSame to decide whether two items are the same or not.

And now to the call:

	    var items: List<Content> by Delegates.observable(emptyList()) {
        prop, oldList, newList ->
        autoNotify(oldList, newList) { o, n -> o.id == n.id }
    }

Using composition

I can understand that you don’t like the previous solution very much. It’s rather specific to a particular case, and you don’t want that all adapters can use it.

But there’s an alternative: an interface.

Sadly, interfaces can’t extend classes as they did at some point of Kotlin preview (I really hope it’s re-added in the future). This let you use the methods of the extended class, and forced classes that implement the interface to be of that type.

But we can get a similar result by moving the extension function into an interface:

interface AutoUpdatableAdapter {

    fun <T> RecyclerView.Adapter<*>.autoNotify(oldList: List<T>, newList: List<T>, compare: (T, T) -> Boolean) {
    ...
}

And the adapter just needs to implement this interface:

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

And that’s all! The rest of the code remains unchanged.

Conclusion

There are several ways to use DiffUtils that make code look great and simpler than in Java. You can choose any of them.

If you want to try any particular solution, go to the repository and move to the specific commit.

I’m in love with Kotlin. I’ve been learning about it for a couple of years, applying it to Android and digesting all this knowledge so that you can learn it with no effort.

Shares

Like this:

Like Loading...

相關推薦

Kotlin Recipes for Android (II): RecyclerView and DiffUtil

As you may know, the Support Library 24 included a new really handy class called DiffUtil, which will let you get rid of the boring and error prone o

Kotlin recipes for Android (I): OnGlobalLayoutListener

Today a mate asked me how he could do an OnGlobalLayoutListener properly without incurring in the need of too much boilerplate. This was a tricky que

Android Developers Blog: Kotlin Momentum for Android and Beyond

Posted by James Lau (@jmslau), Product Manager Today marks the beginning of KotlinConf 2018 - the largest in-person gathering of the Kotlin community a

Kotlin for Android (II): Create a new project

After getting a light idea of, it´s time to configure Android Studio to help us develop Android apps using Kotlin. It requires some steps that only n

AndroidKotlin的學習(點選事件、跳轉、for迴圈、RecyclerView、回撥)

剛剛學習Kotlin,有什麼不對的地方請見諒。 首先屬性的宣告中有兩個很重要的關鍵字: var  和  val。 例如: var a = "hello" val b = "hello"區別是什麼呢? var可多次分配; val只能一次初始化。 點選事件: xml中View定

Kotlin for Android (III): Extension functions and default values

Now that you know the basics about Kotlin and how to configure your project, it´s time to talk about some interesting things that Kotlin can do for u

Kotlin for Android (IV): Custom Views and Android Extensions

After reading what can do for you, you might be wondering what´s next. As we talked in , this language makes Android development much simpler and th

Who is Open Source? Android O Preview 3. Kotlin for Android. And Vaadin 8!

GitHub Survey: Open Source is Raining Men! A 2017 GitHub survey of 5500 randomly selected participants, most of whom work on GitHub.com open source pro

更新 是 可用的 針對 安卓 軟件開發包和工具 Updates are available for android software development packages and tools

安卓 模擬器 軟件 ide software ava -m android 設置 作者:韓夢飛沙 Author:han_meng_fei_sha 郵箱:[email protected]/* */ E-mail: 313134555 @qq.com

Android dependency 'com.android.support:recyclerview-v7' has different version for the compile

Error:Execution failed for task ':app:preDebugBuild'. > Android dependency 'com.android.support:recyclerview-v7' has different version for the co

快速切換至Kotlin for Android模式

前言 關於Kotlin的文章,已經構思了很久。一直不知道該怎麼寫。文件式文章?那不如直接看文件,何必需要我再多“嗶嗶”呢。思來想後,決定寫一篇快速在Android開發中感受Kotlin的其妙的文章。 說實話,最開始搞Kotlin我是拒絕的。為啥?因為完全沒有感覺到用它替換Java開發有什麼實質性的改變;而

極簡Kotlin-For-Android(一)

安裝 Kotlin 外掛 Android Studio 3.+ 已經有了 Kotlin 外掛,如果是更早的版本,點選 Android Studio | File | Settings | Plugins,搜尋 Kotlin ,安裝,重啟 Android Studio . 建立工程 點選 Android

極簡Kotlin-For-Android(二)

上一篇我們做到了從網路獲取資料,並寫好了實體類.接著我們需要建立domain層,這一層為app執行任務. 構建domain層 首先需要建立command: public interface Command<T> { fun execute() : T } 複製程式碼 建立DataMap

Clean architecture for Android with Kotlin: a pragmatic approach

Clean architecture is a topic that never gets old in the Android world, and from the comments and questions I receive, I feel it’s still not very cle

Blog-08-《一週快速上手Kotlin For Android》-之ArrayList

在 Kotlin 中沒有實現和 Java 一樣的 List 集合,而是使用了和 Java 一樣的 ArrayList 集合。Kotlin 中提供了以下四種函式方法來使用 ArrayList,分別是 1、listOf()2、listOfNotNull()3、mutableListOf()4、arraylistO

How Kotlin became our primary language for Android

Keeping the Kotlin reference open, we started to develop the Voter application. Kotlin is a JVM language with 100% Java interoperability, and if you’re fam

Blog-06-《一週快速上手Kotlin For Android》-之When分支

—《一週快速上手Kotlin For Android》簡介目前Kotlin已正式成為Android的官方語言,作為Android開發者來說,學習和了解Kotlin也是屬於理所當然的事情,興許你覺得Jav

Blog-04-《一週快速上手Kotlin For Android》-之Activity詳細用法

—《一週快速上手Kotlin For Android》簡介 目前Kotlin已正式成為Android的官方語言,作為Android開發者來說,學習和了解Kotlin也是屬於理所當然的事情,興許你覺得Java對於你來說才是真正的開發”利器”,使用Java你能發揮

Kotlin for Android

在Google IO 2017 大會上,Google將 Kotlin列為 Android官方開發語言,Android Studio 3.0 也預設集成了Kotlin外掛。 如果您是更早的版本,點選Android Studio File->Settin

Blog-07-《一週快速上手Kotlin For Android》-之陣列

—《一週快速上手Kotlin For Android》簡介目前Kotlin已正式成為Android的官方語言,作為Android開發者來說,學習和了解Kotlin也是屬於理所當然的事情,興許你覺得Java對於你來說才是真正的開發”利器”,使用Java你能發揮更高的效率,當然,如果如此你還是可以繼續使用Java