從滑動式遷移至 AnchoredDraggable

Swipeable 是 Compose Material API,可協助您建構能 可在不同狀態之間滑動,例如底部功能表、導覽匣 滑動關閉為了更妥善支援進階用途,例如 取決於元件大小 Compose-Foundation 1.6.0-alpha01:AnchoredDraggableAnchoredDraggable 是一種基礎 API,可用來建構具有錨點狀態的可拖曳元件,如 例如底部功能表、導覽匣或滑動關閉

Material 的 Swipeable API 已淘汰,改用 Foundation 的 AnchoredDraggable,並將在日後推出的版本中移除。本指南 說明如何從 Swipeable API 遷移至 AnchoredDraggable

SwipeableState 遷移至 AnchoredDraggableState

首先,請確認狀態容器的變更。AnchoredDraggableState 無法繼承,而且偏移值會先以 Float.NaN 表示

更新狀態容器

AnchoredDraggableState 是最終類別,代表無法沿用 如果現有元件沿用自 SwipeableState,請更新 狀態容器保留 AnchoredDraggableState 的參照,而不是 從該物件繼承:

滑動

class MySwitchState: SwipeableState()

錨定可拖曳

class MySwitchState {
    private val anchoredDraggableState = AnchoredDraggableState(...)
}

由於狀態容器不再繼承 SwipeableState,因此您 您必須自行公開 API最常用的 API 是 offsetprogresscurrentValuetargetValue

存取偏移量

Swipeable 中的不同,AnchoredDraggableStateoffsetFloat.NaN之前 需要先初始化在 AnchoredDraggable 中,錨定標記可傳遞至 AnchoredDraggableState 的建構函式或透過以下方式更新: AnchoredDraggableState#updateAnchors。將錨定標記傳遞到 AnchoredDraggableState 的建構函式會立即初始化偏移值。

如果錨定標記取決於版面配置或可能會變動,請使用 AnchoredDraggableState#updateAnchors,以免在執行以下動作時重新建立狀態 錨點會改變。

如果使用 updateAnchors,則在傳遞Float.NaN 錨定標記為 updateAnchors。可避免不小心將 Float.NaN 傳遞至 使用 AnchoredDraggableState#requireOffset 要求 讀取時已初始化偏移值。這有助於您掌握 不一致或可能盡早可能發生錯誤

@Composable
fun AnchoredDraggableBox() {
    val state = remember { AnchoredDraggableState(...) }
    val density = LocalDensity.current
    val anchors = remember { DraggableAnchors { ... } }
    SideEffect {
        state.updateAnchors(anchors)
    }
    Box(
        Modifier.offset { IntOffset(x = state.requireOffset(), y = 0) }
    }
}

Modifier.swipeable 遷移至 Modifier.anchoredDraggable

Modifier.anchoredDraggable() 會取代 Modifier.swipeable。只有部分通知 的 Modifier.swipeable() 個參數已移至 AnchoredDraggableState ,詳情請參閱後續章節。

定義錨定標記

使用 DraggableAnchors 建構工具方法定義錨點。然後 傳送給 AnchoredDraggableState#updateAnchorsAnchoredDraggableState 的 建構函式:

建構函式

enum class DragValue { Start, Center, End }

@Composable
fun AnchoredDraggableBox() {
    val anchors = DraggableAnchors {
        Start at -100.dp.toPx()
        Center at 0f
        End at 100.dp.toPx()
    }
    val state = remember {
        AnchoredDraggableState(anchors = anchors)
    }
    Box(
        Modifier.offset { IntOffset(x = state.requireOffset(), y = 0) }
    )
}

updateAnchors

enum class DragValue { Start, Center, End }

@Composable
fun AnchoredDraggableBox() {
    val state = remember { AnchoredDraggableState(...) }
    val density = LocalDensity.current
    val anchors = with (density) {
        DraggableAnchors {
            Start at -100.dp.toPx()
            Center at 0f
            End at 100.dp.toPx()
        }
    }
    SideEffect {
        state.updateAnchors(anchors)
    }
    Box(
        Modifier.offset { IntOffset(x = state.requireOffset(), y = 0) }
    )
}

如果錨點是靜態,請將其傳遞至建構函式。如果依附元件 或者非靜態版面配置,請使用 updateAnchors

定義排名門檻

門檻參數的類型和名稱已變更。與其使用 獨立ThresholdConfig介面,AnchoredDraggableState則有 positionalThreshold 參數,採用會回傳 門檻。舉例來說,假設排名門檻是 50% 形式:

val anchoredDraggableState = AnchoredDraggableState(
    positionalThreshold = { distance -> distance * 0.5f },
    ...
)

56dp 的位置門檻可能如下所示:

val density = LocalDensity.current
val anchoredDraggableState = AnchoredDraggableState(
    positionalThreshold = { with(density) { 56.dp.toPx() } },
    ...
)

定義速率門檻

速率門檻也會傳遞至 AnchoredDraggableState 的建構函式。 並以 lambda 表示:

val density = LocalDensity.current
val anchoredDraggableState = AnchoredDraggableState(
    velocityThreshold = { with(density) { 125.dp.toPx() } },
    ...
)

API 介面異動

以下提供 API 介面的異動總覽。

AnchoredDraggableState

SwipeableState

AnchoredDraggableState

open class SwipeableState(initialValue: T, animationSpec: AnimationSpec = …, confirmStateChange: (T) -> Boolean = …)

class AnchoredDraggableState( initialValue: T, animationSpec: AnimationSpec = …, confirmValueChange: (T) -> Boolean = …, positionalThreshold: Density.(Float) -> Float = …, velocityThreshold: Dp = …)

offset: State

offset: Float
requireOffset()

progress: SwipeProgress

progress: Float [0f..1f]

currentValue: T

currentValue: T

targetValue: T

targetValue: T

direction: Float [-1f, 0f, 1f]

suspend animateTo(
targetValue: T,
anim: AnimationSpec = …)

suspend animateTo(
targetState: T,
velocity: Float =
lastVelocity)

suspend snapTo(targetValue: T)

suspend snapTo(targetValue: T)

performDrag(delta: Float)

dispatchRawDelta(delta: Float)

suspend performFling(velocity: Float)

suspend settle(velocity: Float)

isAnimationRunning: Boolean

isAnimationRunning: Boolean

lastVelocity: Float

Modifier.anchoredDraggable

Modifier.swipeable

Modifier.anchoredDraggable

state: SwipeableState

state: AnchoredDraggableState

anchors: Map

AnchoredDraggableState#updateAnchors
or

AnchoredDraggableState#constructor

orientation: Orientation

orientation: Orientation

enabled: Boolean = true

enabled: Boolean = true

reverseDirection: Boolean = false

reverseDirection: Boolean = false

interactionSource: MutableInteractionSource? = null

interactionSource: MutableInteractionSource? = null

thresholds: (from: T, to: T) -> ThresholdConfig = FixedThreshold(56.dp)

positionalThreshold 的形式傳遞至 AnchoredDraggableState 建構函式

resistance: ResistanceConfig? = …

尚不支援,如需最新狀態,請參閱 b/288084801

velocityThreshold: Dp = 125.dp

已傳遞至 AnchoredDraggable 建構函式