Миграция с Swipeable на AnchoredDraggable

Swipeable — это API Compose Material, который помогает создавать компоненты, которые можно перемещать между отдельными состояниями, например нижние листы, ящики или смахивание для закрытия. Для лучшей поддержки расширенных вариантов использования, таких как привязки, зависящие от размера компонента, в Compose-Foundation 1.6.0-alpha01 был опубликован преемник: AnchoredDraggable . AnchoredDraggable — это базовый API для создания перетаскиваемых компонентов с привязанными состояниями, таких как нижние листы, ящики или пролистывание для закрытия.

API-интерфейсы Swipeable Material устарели в пользу AnchoredDraggable Foundation и будут удалены в будущем выпуске. В этом руководстве описывается, как перейти с API Swipeable на AnchoredDraggable .

Перенос SwipeableState в AnchoredDraggableState

Начните с выявления изменений в вашем держателе штата. AnchoredDraggableState не может быть унаследован, а смещение представлено как Float.NaN до его инициализации.

Обновите своего владельца штата

AnchoredDraggableState — это финальный класс, то есть от него нельзя унаследоваться. Если ваш существующий компонент наследует от SwipeableState , обновите свой держатель состояния, чтобы он содержал ссылку на AnchoredDraggableState вместо наследования от него:

Перелистываемый

class MySwitchState: SwipeableState()

ПрикрепленныйПеретаскиваемый

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

Поскольку ваш держатель состояния больше не наследуется от SwipeableState , вам, возможно, придется предоставить API самостоятельно. Наиболее распространенные API, которые вы можете использовать, — это offset , progress , currentValue и targetValue .

Доступ к смещению

В отличие от Swipeable , offset AnchoredDraggableState равно Float.NaN до его инициализации. В AnchoredDraggable привязки можно передать конструктору AnchoredDraggableState или обновить с помощью AnchoredDraggableState#updateAnchors . Передача привязок конструктору AnchoredDraggableState немедленно инициализирует смещение.

Если ваши привязки зависят от макета или могут измениться, используйте AnchoredDraggableState#updateAnchors чтобы избежать повторного создания состояния при изменении привязок.

Если вы используете updateAnchors , перед передачей привязок в updateAnchors смещение будет Float.NaN . Чтобы избежать случайной передачи 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#updateAnchors или AnchoredDraggableState :

Конструктор

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

обновитьякоря

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 и также выражаются в виде лямбды:

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 = …) open class SwipeableState (initialValue: T, animationSpec: AnimationSpec = …, confirmStateChange: (T) -> Boolean = …) 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 = …) class AnchoredDraggableState ( initialValue: T, animationSpec: AnimationSpec = …, confirmValueChange: (T) -> Boolean = …, positionalThreshold: Density.(Float) -> Float = …, velocityThreshold: Dp = …) 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(
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)

Передается в конструктор AnchoredDraggableState как positionalThreshold

resistance: ResistanceConfig? = …

Еще не поддерживается. Последний статус см. в b/288084801 .

velocityThreshold: Dp = 125.dp

Передано конструктору AnchoredDraggable