下拉即可重整畫面

下拉更新元件可讓使用者在應用程式內容的開頭向下滑動,重新整理資料。

API 介面

使用 PullToRefreshBox 可組合項實作下拉更新,做為可捲動內容的容器。下列重要參數可控制重新整理行為和外觀:

  • isRefreshing:布林值,指出是否正在執行重新整理動作。
  • onRefresh:使用者啟動重新整理時執行的 lambda 函式。
  • 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) })
            }
        }
    }
}

程式碼重點

  • PullToRefreshBox 會包裝 LazyColumn,後者會顯示字串清單。
  • 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.pullToRefresh(
            state = state,
            isRefreshing = isRefreshing,
            threshold = PositionalThreshold,
            onRefresh = {

            }
        ),
        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(
                    painter = rememberVectorPainter(AppIcons.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 例項,以便將相同例項傳遞至 PullToRefreshBox 和 pullToRefreshModifier。
  • 這個範例使用 PullToRefreshDefaults 類別的容器顏色和位置門檻。這樣一來,您就可以重複使用 Material 程式庫的預設行為和樣式,同時只自訂您感興趣的元素。
  • MyCustomIndicator 使用 Crossfade 在雲端圖示和 CircularProgressIndicator 之間轉換。雲朵圖示會隨著使用者下拉而放大,並在重新整理動作開始時轉換為 CircularProgressIndicator。
    • targetState 會使用 isRefreshing 判斷要顯示的狀態 (雲端圖示或圓形進度指標)。
    • animationSpec 定義轉換的 tween 動畫,並指定持續時間為 CROSSFADE_DURATION_MILLIS。
    • state.distanceFraction 代表使用者下拉的距離,範圍從 0f (未下拉) 到 1f (完全下拉)。
    • graphicsLayer 修飾符會修改縮放比例和透明度。

結果

這部影片顯示上述程式碼的自訂指標:

圖 3. 使用自訂指標實作下拉更新。

其他資源