Migracja z przesuwnego do AnchoredDraggable

Swipeable to interfejs API Compose Material, który ułatwia tworzenie komponentów, które można przełączać między stanami, takimi jak dolne arkusze, szuflady czy funkcja przesuwania w celu zamknięcia. Aby lepiej obsługiwać zaawansowane przypadki użycia, takie jak kotwy zależne od rozmiaru komponentu, opublikowaliśmy nową wersję Compose-Foundation 1.6.0-alpha01: AnchoredDraggable. AnchoredDraggable to interfejs Foundation API do tworzenia przeciąganych komponentów z zakotwiczonymi stanami, takich jak dolne panele, szuflady czy gest przesunięcia w bok, aby zamknąć.

Interfejsy API Material Swipeable zostały wycofane na rzecz interfejsów API Foundation AnchoredDraggable i w przyszłej wersji zostaną usunięte. Ten przewodnik opisuje, jak przejść z interfejsów API Swipeable na AnchoredDraggable.

Przenoszenie danych z SwipeableState do AnchoredDraggableState

Najpierw sprawdź, czy zmienił się stan posiadania. Nie można dziedziczyć funkcji AnchoredDraggableState, a przed zainicjowaniem przesunięcie jest reprezentowane jako Float.NaN.

Zmień stan posiadacza

AnchoredDraggableState to klasa ostateczna, co oznacza, że nie można jej dziedziczyć. Jeśli istniejący komponent dziedziczy z poziomu SwipeableState, zamiast dziedziczenia z poziomu AnchoredDraggableState zaktualizuj holder stanu, aby zawierał odwołanie do AnchoredDraggableState:

Przesuwany

class MySwitchState: SwipeableState()

AnchoredDraggable

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

Twój stan nie dziedziczy już z poziomu SwipeableState, więc musisz samodzielnie udostępnić interfejsy API. Najczęściej używane interfejsy API to offset, progress, currentValue i targetValue.

Dostęp do przesunięcia

W przeciwieństwie do Swipeable zmienna offset w klasie AnchoredDraggableState ma wartość Float.NaN przed zainicjowaniem. W AnchoredDraggable kotwy można przekazywać do konstruktora AnchoredDraggableState lub aktualizować za pomocą AnchoredDraggableState#updateAnchors. Przekazanie elementów zaczepowych do konstruktora AnchoredDraggableState powoduje natychmiastowe zainicjowanie przesunięcia.

Jeśli punkty zaczepienia zależą od układu lub mogą się zmienić, użyj opcji AnchoredDraggableState#updateAnchors, aby uniknąć ponownego tworzenia stanu po zmianie punktów zaczepienia.

Jeśli użyjesz updateAnchors, przesunięcie będzie wynosić Float.NaN, zanim przekaziesz kotwy do updateAnchors. Aby uniknąć przypadkowego przekazania Float.NaN do komponentów, użyj wartości AnchoredDraggableState#requireOffset, aby wymagać inicjalizacji przesunięcia podczas odczytu. Pomoże Ci to w wczesnej fazie wykryć niespójności lub możliwe błędy.

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

Przenoszenie danych z Modifier.swipeable do Modifier.anchoredDraggable

Modifier.anchoredDraggable() zastępuje Modifier.swipeable. Niektóre parametry Modifier.swipeable() zostały przeniesione bezpośrednio do AnchoredDraggableState, jak opisano w następnych sekcjach.

Zdefiniuj kotwy

Określ kotwy za pomocą metody konstruktora DraggableAnchors. Następnie prześlij je do konstruktora AnchoredDraggableState#updateAnchors lub 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) }
    )
}

Jeśli kotwy są statyczne, prześlij je do konstruktora. Jeśli są zależne od układu lub nie są statyczne, użyj updateAnchors.

Definiowanie progów pozycji

Zmieniono typ i nazwę parametru progi. Zamiast osobnego interfejsu ThresholdConfig funkcja AnchoredDraggableState ma parametr positionalThreshold, który przyjmuje funkcję lambda zwracającą pozycję progu. Na przykład próg pozycji 50% może być wyrażony w ten sposób:

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

Próg pozycyjny 56dp może być wyrażony w ten sposób:

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

Definiowanie progów prędkości

Próg prędkości jest również przekazywany do konstruktora AnchoredDraggableState, a także wyrażany jako lambda:

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

Zmiany w interfejsie API

Poniżej znajdziesz przegląd zmian w interfejsie 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]

Nie dotyczy

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)

przekazano do konstruktora AnchoredDraggableState jako positionalThreshold

resistance: ResistanceConfig? = …

Jeszcze nieobsługiwane. Najnowsze informacje znajdziesz w problemie b/288084801.

velocityThreshold: Dp = 125.dp

przekazany do konstruktora AnchoredDraggable,