doOnNextLayout、doOnLayout、doOnPreDrawの違いと、Coroutineでこれらを動かしてみる

タイトルにあるメソッドはJetpack core-ktxに定義されています。

これらを雰囲気で使っていたので、軽く調べてみました。

doOnNextLayout

これは、指定したViewがレイアウトされたときに実行されます。 なので、measure、layoutの後にコールバックされます。

注意としては、「既にレイアウト済み かつ 再レイアウトが行われない時」はコールバックされません。

doOnLayout

doOnNextLayoutと似ているのですが、異なる点は、「レイアウト済み かつ 再レイアウトの要求がない」場合には、即時実行されます。

inline fun View.doOnLayout(crossinline action: (view: View) -> Unit) {
    if (ViewCompat.isLaidOut(this) && !isLayoutRequested) {
        action(this)
    } else {
        doOnNextLayout {
            action(it)
        }
    }
}

doOnPreDraw

描画される前に実行されます。よって、このタイミングではmeasure、layoutは完了していて、描画するぞっていうタイミングでコールバックされます。

doOnNextLayoutと違うところは、goneでもコールされる点なのかなと思います。 Viewがgoneの場合、doOnNextLayoutはコールされないですが、doOnPreDrawではコールされます。

コルーチンと一緒に使う

chrisbanes/tiviが凄く参考になります。

PreDrawをCoroutineと協調して動くようにしたいなら、次のようになります。

/*
 * Copyright 2019 Google LLC
 *
 * 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.
 */
suspend fun View.awaitPreDraw() = suspendCancellableCoroutine<Unit> { cont ->
    val listener = OneShotPreDrawListener.add(this) {
        cont.resume(Unit)
    }
    // If the coroutine is cancelled, remove the listener
    cont.invokeOnCancellation {
        listener.removeListener()
    }
}

View周りのコードってどうしてもコールバック地獄になりがちだと思うんですが、Coroutineを使うことでフラットに保つことが期待できます。

他にもいろいろ定義してあります。ViewExtensions.kt

まとめ

参考

Written by
あんどろいどでぃべろっぱぁー🍎