Di chuyển từ Swipeable sang AnchoredDraggable

Swipeable là một API Material Compose giúp bạn tạo các thành phần có thể vuốt giữa các trạng thái riêng biệt, chẳng hạn như các trang dưới cùng, ngăn hoặc vuốt để đóng. Để hỗ trợ tốt hơn các trường hợp sử dụng nâng cao, chẳng hạn như neo phụ thuộc vào kích thước của một thành phần, một thành phần kế nhiệm đã được phát hành trong Compose-Foundation 1.6.0-alpha01: AnchoredDraggable. AnchoredDraggable là một API Foundation để tạo các thành phần có thể kéo với trạng thái liên kết, chẳng hạn như các trang dưới cùng, ngăn hoặc vuốt để đóng.

Các API Swipeable của Material không còn được dùng nữa mà thay vào đó là AnchoredDraggable của Foundation và sẽ bị xoá trong bản phát hành sau này. Hướng dẫn này mô tả cách di chuyển từ API Swipeable sang AnchoredDraggable.

Di chuyển SwipeableState sang AnchoredDraggableState

Bắt đầu bằng cách xác định các thay đổi đối với phần tử giữ trạng thái. Không thể kế thừa từ AnchoredDraggableState và độ dời được biểu thị dưới dạng Float.NaN trước khi được khởi tạo.

Cập nhật phần tử giữ trạng thái

AnchoredDraggableState là một lớp cuối cùng, nghĩa là không thể kế thừa lớp này. Nếu thành phần hiện có của bạn kế thừa từ SwipeableState, hãy cập nhật trình giữ trạng thái để giữ tham chiếu đến AnchoredDraggableState thay vì kế thừa từ thành phần đó:

Có thể vuốt

class MySwitchState: SwipeableState()

AnchoredDraggable

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

Vì phần tử giữ trạng thái không còn kế thừa từ SwipeableState nữa, nên bạn có thể phải tự hiển thị các API. Các API phổ biến nhất mà bạn có thể sử dụng là offset, progress, currentValuetargetValue.

Truy cập vào độ lệch

Không giống như trong Swipeable, offset của AnchoredDraggableStateFloat.NaN trước khi được khởi tạo. Trong AnchoredDraggable, bạn có thể truyền neo đến hàm khởi tạo của AnchoredDraggableState hoặc cập nhật thông qua AnchoredDraggableState#updateAnchors. Việc truyền neo đến hàm khởi tạo của AnchoredDraggableState sẽ khởi tạo độ dời ngay lập tức.

Nếu neo của bạn phụ thuộc vào bố cục hoặc có thể thay đổi, hãy sử dụng AnchoredDraggableState#updateAnchors để tránh tạo lại trạng thái khi neo thay đổi.

Nếu bạn sử dụng updateAnchors, độ dời sẽ là Float.NaN trước khi truyền các neo đến updateAnchors. Để tránh vô tình truyền Float.NaN đến các thành phần, hãy sử dụng AnchoredDraggableState#requireOffset để yêu cầu hệ thống đã khởi chạy độ dời khi đọc độ dời đó. Điều này giúp bạn phát hiện sớm các điểm không nhất quán hoặc lỗi có thể xảy ra.

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

Di chuyển Modifier.swipeable sang Modifier.anchoredDraggable

Modifier.anchoredDraggable() thay thế Modifier.swipeable. Một số tham số của Modifier.swipeable() đã được chuyển trực tiếp sang AnchoredDraggableState, như mô tả trong các phần sau.

Xác định neo

Xác định neo bằng phương thức trình tạo DraggableAnchors. Sau đó, truyền các giá trị này vào hàm khởi tạo của AnchoredDraggableState#updateAnchors hoặc AnchoredDraggableState:

Hàm dựng

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

Nếu neo là tĩnh, hãy truyền các neo đó vào hàm khởi tạo. Nếu các thành phần này phụ thuộc vào bố cục hoặc không tĩnh, hãy sử dụng updateAnchors.

Xác định ngưỡng vị trí

Loại và tên của tham số ngưỡng đã thay đổi. Thay vì có giao diện ThresholdConfig riêng biệt, AnchoredDraggableState có tham số positionalThreshold nhận một hàm lambda trả về vị trí của ngưỡng. Ví dụ: ngưỡng vị trí là 50% có thể được biểu thị như sau:

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

Bạn có thể biểu thị ngưỡng vị trí của 56dp như sau:

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

Xác định ngưỡng tốc độ

Các ngưỡng tốc độ cũng được truyền đến hàm khởi tạo của AnchoredDraggableState và cũng được biểu thị dưới dạng lambda:

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

Thay đổi đối với giao diện API

Hãy xem thông tin tổng quan về các thay đổi đối với giao diện API bên dưới.

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]

Không áp dụng

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)

Được truyền vào hàm khởi tạo AnchoredDraggableState dưới dạng positionalThreshold

resistance: ResistanceConfig? = …

Chưa được hỗ trợ. Xem b/288084801 để biết trạng thái mới nhất.

velocityThreshold: Dp = 125.dp

Được truyền vào hàm khởi tạo AnchoredDraggable