Compose での可視性トラッキング

UI 要素が画面に表示されるタイミングをトラッキングすることは、ログ分析、UI 状態の管理、動画コンテンツの自動再生または一時停止によるリソースの最適化など、さまざまなユースケースに役立ちます。Compose には、UI 要素の可視性を追跡するための修飾子がいくつか用意されています。

  • onVisibilityChanged - この修飾子は、コンポーザブルの公開設定が変更されたときに通知します。コンポーザブルが表示されるたびにアクションや副作用をトリガーするのに最適です。
  • onLayoutRectChanged - この修飾子は、ルート、ウィンドウ、画面を基準としたコンポーザブルの境界に関する情報を提供します。低レベルの制御を提供し、onVisibilityChanged の基盤となる API です。この修飾子は onGloballyPositioned に似ていますが、パフォーマンスが向上し、柔軟性が高まります。

これらの API は、修飾子チェーンの一部として任意のコンポーザブルで使用できます。

onVisibilityChanged で公開設定の変更を追跡する

アイテムがユーザーに表示されているか、部分的に表示されているかを把握することで、分析の追跡(視聴回数など)、パフォーマンスの最適化(アイテムが表示されている場合にのみネットワークからデータを取得またはプリフェッチする)、イベントのトリガー(動画の再生または一時停止)が可能になります。

アイテムの可視性が変更されたときに通知を受け取るには、次の例に示すように onVisibilityChanged 修飾子を使用します。

Text(
    text = "Some text",
    modifier = Modifier
        .onVisibilityChanged { visible ->
            if (visible) {
                // Do something if visible.
            } else {
                // Do something if not visible.
            }
        }
        .padding(vertical = 8.dp)
)

onVisibilityChanged 修飾子は、コンポーザブルの現在の表示状態を反映するブール値を返します。また、minFractionVisible や minDurationMs などのパラメータも用意されており、可視性コールバックをトリガーするタイミングを細かく制御できます。

他の修飾子と同様に、onVisibilityChanged 修飾子でも順序が重要です。上記の例は、パディング付きのテキストをレンダリングするコンポーズ可能な関数を示しています。修飾子がパディングとともにコンポーザブル全体に影響するようにするには、padding 修飾子の前に onVisibilityChanged 修飾子を追加します。

可視性コールバックをトリガーする前にコンポーザブルに制限時間を設定する

場合によっては、アイテムが一定時間ユーザーに表示された後にのみアクションをトリガーしたいことがあります。たとえば、動画がユーザーに一定時間表示された場合に、動画を自動再生できます。

アイテムが一定期間表示された後にアクションをトリガーするには、onVisibilityChanged 修飾子の minDurationMs パラメータを使用します。このパラメータは、コールバックがトリガーされるためにコンポーザブルが継続的に表示される必要がある最小時間を指定します。期間が経過する前にコンポーザブルが表示されなくなった場合、タイマーはリセットされます。デフォルト値は 0 ミリ秒です。

次のスニペットでは、コンポーザブルがユーザーに 3 秒間表示された後、背景を紫色に変更します。

var background by remember { mutableStateOf(PalePink) }
Card(
    modifier = modifier
        // ...
        .onVisibilityChanged(minDurationMs = 3000) {
            if (it) {
                background = MutedPlum
            }
        }
) {

    Box(
        modifier = Modifier
            // ...
            .background(background),
        contentAlignment = Alignment.Center,
    ) {
        // ...
    }
}

図 1. コンポーザブルが画面に 3 秒間連続して表示されると、背景がピンクからプラムに変わります。

表示される最小割合を設定する

コンポーザブルの可視性コールバックに最小可視部分を設定すると、スクロール可能なコンテンツ(LazyColumn など)を操作する際に、画面サイズを超えるアイテムのデータ取得を最適化できます。

このような場合は、onVisibilityChanged 修飾子の minFractionVisible パラメータを使用して、コンポーザブルが表示可能としてマークされるために画面に表示される必要がある割合を定義します。0.0f から 1.0f までの浮動小数点値をサポートし、デフォルトでは 1.0f に設定されています。1.0f は、コンポーザブルが画面に完全に表示される必要があること、またはビューポートより大きい場合はビューポート全体を埋める必要があることを意味します。

コールバックは、表示可能な部分が minFractionVisible より大きい場合、またはコンポーザブルが完全に表示されているか、ビューポート全体に表示されている場合に、true を受け取ります。それ以外の場合は false を受け取ります。

LazyColumn(
    modifier = modifier.fillMaxSize()
) {
    item {
        Box(
            modifier = Modifier
                // ...
                // Here `visible` is `true` when more than 20% of the composable is visible.
                .onVisibilityChanged(
                    minFractionVisible = 0.2f,
                ) { visible ->
                    if (visible) {
                        // Call specific logic here.
                        // viewModel.fetchDataFromNetwork()
                    }
                }
                .padding(vertical = 16.dp)
        ) {
            Text(
                text = "Sample Text",
                modifier = Modifier.padding(horizontal = 16.dp)
            )
        }
    }
}

図 2. minFractionVisible が設定されていない場合。 図 3. minFractionVisible を 0.2f に設定。

前述の例では、コンポーザブルが完全に表示される前に、ネットワークから Androidify ボットをプリロードしています。図 2 では、コンポーザブルが完全に表示されていないため、3 番目のボットは読み込まれません。図 3 では、minFractionVisible が設定されており、3 番目のボットは画面に完全に表示される前に読み込まれます。

コンポーザブルを非表示にしてスペースを維持する

周囲のレイアウトを変更せずにコンポーザブルを非表示にするには、visible 修飾子を使用します。false を渡すと、コンポーザブルは測定されますが、配置や描画は行われません。alpha を 0f に設定する場合とは異なり、visible(false) はレイアウト スペースを保持しながら、ユーザー補助ツリーから要素を削除し、タッチ操作を無効にします。

次の例は、非表示の間もスペースを保持するチェックアイコンを示しています。これにより、横のテキストは移動しません。

Row(modifier, verticalAlignment = Alignment.CenterVertically) {
    Icon(
        painter = painterResource(R.drawable.check),
        contentDescription = "Saved",
        modifier = Modifier
            // The icon keeps its space while it's hidden, so the text doesn't move.
            .visible(isSaved)
    )
    Text("Draft", Modifier.padding(start = 8.dp))
}

コンポーザブルが表示されたり消えたりしても、コンポーザブルの周囲のレイアウトが移動しないようにする必要がある場合は、visible を使用します。コンポーザブルとそのスペースを削除するには、コンポーザブルを呼び出さないか、AnimatedVisibility を使用してアニメーションで表示と非表示を切り替えます。