1. 程式人生 > >Kotlin recipes for Android (I): OnGlobalLayoutListener

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 question because of a couple of things, let’s see it a little more deeply.

What is OnGlobalLayoutListener for?

This listener is available for any view’s ViewTreeObserver

and it’s quite often used to get a callback when the view is inflated and measured, and we already have a width and height available to do any kind of calculations, animations, etc.

If you’re going to use this method, make sure it’s really what you need. Check this tweet from Chris Banes

Thanks to the awesome Java interoperability that Kotlin provides, we can do this on a very clean way using its simulated properties and lambdas for single-method interfaces:

recycler.viewTreeObserver.addOnGlobalLayoutListener {
    // do whatever
}

What’s the issue here? To prevent leaks, a recommended practice is to remove the listener once you’ve finished using it. But we don’t have a reference to the object because we used a lambda, and a lambda is not exactly the same as an object.

Want to learn Kotlin?

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

We could still use the old-fashioned style, but a kitten dies every time we use an anonymous object directly in Kotlin. We are not changing to a nicer language if we still need to do things like this:

recycler.viewTreeObserver.addOnGlobalLayoutListener(
        object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                recycler.viewTreeObserver.removeOnGlobalLayoutListener(this);
                // do whatever
            }
        });

Finding a better alternative

Ok, we know we don’t want that. But what can we do to make it better? We are forced to use the not-so-good-looking way, but a good alternative would be to hide this behind an extension function.

We will then create a new function for views that receives another function and creates and removes the listener by itself. Something like:

inline fun View.waitForLayout(crossinline f: () -> Unit) = with(viewTreeObserver) {
    addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            removeOnGlobalLayoutListener(this)
            f()
        }
    })
}

You can now just call the function and be sure that the listener is added and removed by itself. Besides, you’ll never forget about removing it anymore:

recycler.waitForLayout { 
    // do whatever
}

If you prefer, you could apply the extension function to the ViewTreeObserver instead of directly to the View. That’s up to you.

But we can improve it

This layout listener is usually used to do something after a view is measured, so you typically would need to wait until width and height are greater than 0. And we probably want to do something with the view that called it, so why don’t we convert the parameter function into an extension function too?

I also generified the function so that it can be used by any object that extends View and also be able to access to all its specific functions and properties from the function we’ll write.

inline fun <T: View> T.afterMeasured(crossinline f: T.() -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            if (measuredWidth > 0 && measuredHeight > 0) {
                viewTreeObserver.removeOnGlobalLayoutListener(this)
                f()
            }
        }
    })
}

This afterMeasured function is very similar to the previous one, but you can use the properties and public methods of the view directly inside the lambda. We can, for instance, get the width of the recycler and set a layout with a dynamic number of columns depending on it.

recycler.afterMeasured {
    val columnCount = width / columnWidth
    layoutManager = GridLayoutManager(context, columnCount)
}

Conclusion

It’s true that there are still some things that don’t look nice when working with Android, even moving to Kotlin, but we can always find an alternative that improves readability and avoids boilerplate, by hiding this boilerplate behind other structures. At least you’ll have to only write it once and the rest of the code will look awesome!

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 (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

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 for Android (I): Introduction

Kotlin is one of the many JVM based languages that are starting to emerge as a possible Java successor in Android development. Java is one of the mos

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模式

前言 關於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

Codelab for Android Design Support Library used in I/O Rewind Bangkok session

At the moment I believe that there is no any Android Developer who doesn't know about Material Design anymore since it officially becomes

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

Kotlin for Android(九)Kotlin集合

一、結構 集合在我們實際開發中用的還是比較頻繁的,Kotlin中的集合不同於Java中的集合, Kotlin中的集合根據“是否可變”,分為兩派:可變集合與不可變集合(可變集合可以在初始化後add新資料,不可變集合只能get資料,不能add資料),而後者是 在前

一步步學習kotlin for android(三) kotlin省略findviewById

findViewById      今天的內容涉及到findViewByID,android語言原來這個特別繁瑣,現在好了,kotlin語言,直接拿來佈局裡面的id用,省去好多重複工作量啊 在使用kotlin的id之前,需要先在builde.gradle裡引入這個 app

Kotlin for Android Developers

If at any time you have tried to investigate on your own, then you are aware of the amount of time we sometimes spend to find the solution we are loo

What do 17 Google Developers Experts for Android think about Kotlin?

I started developing for Android as a natural evolution from being a Java guy. The language was shared, so I decided to give it a try. And it was a c

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