下方向にスワイプして更新

プルして更新コンポーネントを使用すると、ユーザーはアプリのコンテンツの先頭を下にドラッグしてデータを更新できます。

API サーフェス

PullToRefreshBox コンポーザブルを使用して、スクロール可能なコンテンツのコンテナとして機能するプルトゥ リフレッシュを実装します。更新の動作と外観は、次の主なパラメータで制御します。

  • isRefreshing: 更新アクションが現在進行中かどうかを示すブール値。
  • onRefresh: ユーザーが更新を開始したときに実行されるラムダ関数。
  • indicator: プルして更新するときに描画されるインジケーターをカスタマイズします。

基本的な例

次のスニペットは、PullToRefreshBox の基本的な使用方法を示しています。

@Composable
fun PullToRefreshBasicSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

コードに関する主なポイント

  • PullToRefreshBoxLazyColumn をラップし、文字列のリストを表示します。
  • PullToRefreshBox には isRefreshing パラメータと onRefresh パラメータが必要です。
  • PullToRefreshBox ブロック内のコンテンツは、スクロール可能なコンテンツを表します。

結果

この動画は、上記のコードによる基本的なプルトゥー リフレッシュの実装を示しています。

図 1: アイテムのリストに対する基本的なプルトゥー リフレッシュの実装。

高度な例: インジケーターの色をカスタマイズする

@Composable
fun PullToRefreshCustomStyleSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            Indicator(
                modifier = Modifier.align(Alignment.TopCenter),
                isRefreshing = isRefreshing,
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                color = MaterialTheme.colorScheme.onPrimaryContainer,
                state = state
            )
        },
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

コードに関する主なポイント

  • インジケーターの色は、indicator パラメータの containerColor プロパティと color プロパティでカスタマイズできます。
  • rememberPullToRefreshState() は、更新アクションの状態を管理します。この状態は、indicator パラメータと組み合わせて使用します。

結果

この動画では、色付きのインジケータを使用したプルして更新の実装を示しています。

図 2. カスタム スタイルを使用したプルして更新の実装。

高度な例: 完全にカスタマイズされたインジケーターを作成する

既存のコンポーザブルとアニメーションを活用することで、複雑なカスタム インジケーターを作成できます。このスニペットは、プルして更新の実装で完全にカスタムのインジケーターを作成する方法を示しています。

@Composable
fun PullToRefreshCustomIndicatorSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            MyCustomIndicator(
                state = state,
                isRefreshing = isRefreshing,
                modifier = Modifier.align(Alignment.TopCenter)
            )
        }
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

// ...
@Composable
fun MyCustomIndicator(
    state: PullToRefreshState,
    isRefreshing: Boolean,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier.pullToRefreshIndicator(
            state = state,
            isRefreshing = isRefreshing,
            containerColor = PullToRefreshDefaults.containerColor,
            threshold = PositionalThreshold
        ),
        contentAlignment = Alignment.Center
    ) {
        Crossfade(
            targetState = isRefreshing,
            animationSpec = tween(durationMillis = CROSSFADE_DURATION_MILLIS),
            modifier = Modifier.align(Alignment.Center)
        ) { refreshing ->
            if (refreshing) {
                CircularProgressIndicator(Modifier.size(SPINNER_SIZE))
            } else {
                val distanceFraction = { state.distanceFraction.coerceIn(0f, 1f) }
                Icon(
                    imageVector = Icons.Filled.CloudDownload,
                    contentDescription = "Refresh",
                    modifier = Modifier
                        .size(18.dp)
                        .graphicsLayer {
                            val progress = distanceFraction()
                            this.alpha = progress
                            this.scaleX = progress
                            this.scaleY = progress
                        }
                )
            }
        }
    }
}

コードに関する主なポイント

  • 前のスニペットでは、ライブラリから提供された Indicator を使用しました。このスニペットは、MyCustomIndicator というカスタム インジケーター コンポーザブルを作成します。このコンポーザブルでは、pullToRefreshIndicator 修飾子が配置と更新のトリガーを処理します。
  • 前のスニペットと同様に、PullToRefreshState インスタンスが抽出されるため、同じインスタンスを PullToRefreshBoxpullToRefreshModifier の両方に渡すことができます。
  • コンテナの色と位置しきい値は、PullToRefreshDefaults クラスから使用されます。これにより、マテリアル ライブラリのデフォルトの動作とスタイルを再利用しながら、必要な要素のみをカスタマイズできます。
  • MyCustomIndicatorCrossfade を使用して、クラウド アイコンと CircularProgressIndicator を切り替えます。ユーザーがプルするとクラウド アイコンが拡大し、更新アクションが開始されると CircularProgressIndicator に遷移します。
    • targetState は、isRefreshing を使用して、表示する状態(クラウド アイコンまたは円形の進行状況インジケーター)を決定します。
    • animationSpec は、遷移の tween アニメーションを定義し、CROSSFADE_DURATION_MILLIS の継続時間を指定します。
    • state.distanceFraction は、ユーザーがプルダウンした距離を表します。値は 0f(プルダウンなし)から 1f(完全にプルダウン)の範囲です。
    • graphicsLayer 修飾子は、スケールと透明度を変更します。

結果

この動画は、上記のコードにあるカスタム インジケーターを示しています。

図 3. カスタム インジケータを使用したプルして更新の実装。

参考情報