מעבר מניתנת להחלקה לגרירה במעוגנות

Swipeable הוא ממשק API של Compose Material API שעוזר ליצור רכיבים לעבור בין מצבים נפרדים, כגון גיליונות תחתונים, מגירות או החלקה כדי לסגור. כדי לספק תמיכה טובה יותר בתרחישים מתקדמים לדוגמה, כמו עוגנים בהתאם לגודל של רכיב, פורסם רצף המרות הגדרת פיתוח נייטיב 1.6.0-alpha01: AnchoredDraggable. AnchoredDraggable הוא Fundamentals API שמשמש לפיתוח רכיבים שניתנים לגרירה עם מצבים מעוגנים, כגיליונות תחתונים, חלונית הזזה או החלקה לסגירה.

ממשקי ה-API מסוג Swipeable של Material הוצאו משימוש לטובת AnchoredDraggable ותוסר בגרסה עתידית. המדריך הזה מוסבר איך לעבור מממשקי 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, ניתן להעביר את העוגנים אל ה-constructor של AnchoredDraggableState או עודכן באמצעות AnchoredDraggableState#updateAnchors. העברת העוגנים אל ה-constructor של 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 ישירות, כפי שמתואר בסעיפים הבאים.

הגדרת עוגנים

מגדירים את העוגנים באמצעות שיטת ה-builder DraggableAnchors. לאחר מכן, מעבירים אותם אל AnchoredDraggableState#updateAnchors או אל AnchoredDraggableState constructor:

בונה

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

אם העוגנים סטטיים, מעבירים אותם ל-constructor. אם הם תלויים פריסה או שהן לא סטטיות, צריך להשתמש ב-updateAnchors.

הגדרת ערכי סף לפי מיקום

הסוג והשם של פרמטר הסף השתנו. במקום בממשק ThresholdConfig נפרד, ב-AnchoredDraggableState יש פרמטר positionalThreshold שלוקח פונקציית lambda שמחזירה את במיקום של הסף. לדוגמה, סף מיקום של 50% יכול להיות מבוטאת כך:

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

אפשר לבטא את סף המיקום של 56dp כך:

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

הגדרת ערכי סף של מהירות

ערכי הסף של המהירות מועברים גם הם ל-constructor של 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)

הועברה ל-constructor של AnchoredDraggableState בתור positionalThreshold

resistance: ResistanceConfig? = …

החשבון עדיין לא נתמך. אפשר לראות את הסטטוס העדכני ביותר בכתובת b/288084801.

velocityThreshold: Dp = 125.dp

הועבר אל constructor של AnchoredDraggable