Von „Swipeable“ zu „AnchoredDraggable“ migrieren

Swipeable ist eine Compose Material-API, mit der Sie Komponenten erstellen können, zwischen denen durch Wischen gewechselt werden kann, z. B. Bottom Sheets, Drawers oder Swipe-to-Dismiss. Um erweiterte Anwendungsfälle besser zu unterstützen, z. B. Anker, die von der Größe einer Komponente abhängen, wurde in Compose-Foundation 1.6.0-alpha01 ein Nachfolger veröffentlicht: AnchoredDraggable. AnchoredDraggable ist eine Foundation API zum Erstellen von ziehbaren Komponenten mit verankerten Status, z. B. Bottom Sheets, Drawers oder Swipe-to-Dismiss.

Die Swipeable-APIs von Material wurden zugunsten von AnchoredDraggable von Foundation eingestellt und werden in einer zukünftigen Version entfernt. In dieser Anleitung wird beschrieben, wie Sie von Swipeable-APIs zu AnchoredDraggable migrieren.

SwipeableState zu AnchoredDraggableState migrieren

Beginnen Sie damit, Änderungen an Ihrem State-Holder zu erkennen. AnchoredDraggableState kann nicht übernommen werden und der Offset wird als Float.NaN dargestellt, bevor er initialisiert wird.

Statusinhaber aktualisieren

AnchoredDraggableState ist eine endgültige Klasse, von der nicht abgeleitet werden kann. Wenn Ihre vorhandene Komponente von SwipeableState abgeleitet wird, aktualisieren Sie Ihren Status-Holder so, dass er eine Referenz auf AnchoredDraggableState enthält, anstatt davon abzuleiten:

Wischbar

class MySwitchState: SwipeableState()

AnchoredDraggable

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

Da Ihr Statusinhaber nicht mehr von SwipeableState erbt, müssen Sie möglicherweise selbst APIs bereitstellen. Die am häufigsten verwendeten APIs sind offset, progress, currentValue und targetValue.

Auf den Versatz zugreifen

Anders als in Swipeable ist AnchoredDraggableStates offset vor der Initialisierung Float.NaN. In AnchoredDraggable können die Anker an den Konstruktor von AnchoredDraggableState übergeben oder über AnchoredDraggableState#updateAnchors aktualisiert werden. Durch Übergeben der Anker an den Konstruktor von AnchoredDraggableState wird der Offset sofort initialisiert.

Wenn Ihre Anker vom Layout abhängen oder sich ändern können, verwenden Sie AnchoredDraggableState#updateAnchors, um zu vermeiden, dass der Status neu erstellt wird, wenn sich die Anker ändern.

Wenn Sie updateAnchors verwenden, ist der Offset Float.NaN, bevor die Anker an updateAnchors übergeben werden. Um zu vermeiden, dass Float.NaN versehentlich an Komponenten übergeben wird, verwenden Sie AnchoredDraggableState#requireOffset, um zu erzwingen, dass der Offset beim Lesen initialisiert wurde. So können Sie Unstimmigkeiten oder mögliche Fehler frühzeitig erkennen.

@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 zu Modifier.anchoredDraggable migrieren

Modifier.anchoredDraggable() ersetzt Modifier.swipeable. Einige Parameter von Modifier.swipeable() wurden direkt zu AnchoredDraggableState verschoben, wie in den folgenden Abschnitten beschrieben.

Anker definieren

Definieren Sie die Anker mit der Builder-Methode DraggableAnchors. Übergeben Sie sie dann an den Konstruktor von AnchoredDraggableState#updateAnchors oder AnchoredDraggableState:

Konstruktor

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) }
    )
}

Wenn die Anker statisch sind, übergeben Sie sie an den Konstruktor. Wenn sie vom Layout abhängen oder nicht statisch sind, verwenden Sie updateAnchors.

Positionsschwellenwerte definieren

Der Typ und der Name des Parameters „thresholds“ haben sich geändert. Anstelle einer separaten ThresholdConfig-Schnittstelle hat AnchoredDraggableState einen positionalThreshold-Parameter, der eine Lambda-Funktion akzeptiert, die die Position des Grenzwerts zurückgibt. Ein Beispiel für einen Positionsgrenzwert von 50% wäre:

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

Ein Positionsgrenzwert von 56dp könnte so ausgedrückt werden:

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

Grenzwerte für die Geschwindigkeit definieren

Geschwindigkeitsschwellenwerte werden auch an den Konstruktor von AnchoredDraggableState übergeben und ebenfalls als Lambda ausgedrückt:

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

Änderungen an der API-Oberfläche

Unten finden Sie eine Übersicht über die Änderungen an der API-Oberfläche.

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)

An den AnchoredDraggableState-Konstruktor als positionalThreshold übergeben

resistance: ResistanceConfig? = …

Wird bisher nicht unterstützt. Den aktuellen Status finden Sie unter b/288084801.

velocityThreshold: Dp = 125.dp

An den Konstruktor von AnchoredDraggable übergeben