Android Academy · Lektion

Gesture- og spring-animationer

Naturlig, fysikbaseret bevægelse.

Lektion 4 af 413 trin

Gesture- og spring-animationer er en gratis Android Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Android Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Android Academy-kurset indeholder 4 lektioner i alt.

Interaktiv, fysisk bevægelse

De mest tilfredsstillende animationer reagerer på brugerens finger og føles som virkelige objekter med fremdrift og hop. For at bygge dem kombinerer du gestus med fysikbaseret animation.

Denne lektion dækker Animatable, fjedre, kastehenfald og kobling til trækgestus for en naturlig, interaktiv brugergrænseflade.

Animatable: Styring på lavt niveau

Mens animate*AsState reagerer på tilstand, giver Animatable dig imperativ kontrol. Du gemmer en sådan i remember og driver den fra en coroutine med kald som animateTo eller snapTo.

Det er grundlaget for bevægelse styret af gestus.

@Composable
fun BounceOnClick() {
    val scale = remember { Animatable(1f) }
    val scope = rememberCoroutineScope()
    Box(
        Modifier
            .graphicsLayer { scaleX = scale.value; scaleY = scale.value }
            .clickable {
                scope.launch {
                    scale.animateTo(1.3f, spring())
                    scale.animateTo(1f, spring())
                }
            }
    )
}

Fjedre: dampingRatio og stiffness

En spring-specifikation defineres af to værdier:

  • dampingRatio – hvor meget den hopper. En lavere værdi giver flere hop; 1f (kritisk dæmpet) overskyder ikke.
  • stiffness – hvor hurtigt den bevæger sig. En højere værdi når hurtigere frem til målet.

Compose tilbyder konstanter som Spring.DampingRatioMediumBouncy og Spring.StiffnessLow, så du sjældent behøver rå tal.

val spec = spring<Float>(
    dampingRatio = Spring.DampingRatioLowBouncy,
    stiffness = Spring.StiffnessMedium
)

// later, inside a coroutine:
// offset.animateTo(targetValue = 0f, animationSpec = spec)

Registrering af trækbevægelser

For at gøre bevægelse interaktiv læser du berøringsinput. Modifier.pointerInput med detectDragGestures rapporterer trækforskydninger, mens brugeren bevæger fingeren.

Du akkumulerer disse forskydninger i en offset og anvender den på den composable.

@Composable
fun Draggable() {
    var offsetX by remember { mutableStateOf(0f) }
    Box(
        Modifier
            .offset { IntOffset(offsetX.roundToInt(), 0) }
            .size(80.dp)
            .background(Color.Red)
            .pointerInput(Unit) {
                detectDragGestures { change, drag ->
                    change.consume()
                    offsetX += drag.x
                }
            }
    )
}

Spring tilbage med en fjeder

Kombinér træk med Animatable for at skabe en effekt, hvor elementet "springer tilbage". Mens du trækker, bruger du snapTo til øjeblikkeligt at sætte elementet ved fingerens position. Når fingeren slippes, bruger du animateTo(0f) med en fjeder, så elementet hopper tilbage på plads.

@Composable
fun SnapBack() {
    val offsetX = remember { Animatable(0f) }
    val scope = rememberCoroutineScope()
    Box(
        Modifier
            .offset { IntOffset(offsetX.value.roundToInt(), 0) }
            .size(80.dp)
            .background(Color.Blue)
            .pointerInput(Unit) {
                detectDragGestures(
                    onDrag = { change, drag ->
                        change.consume()
                        scope.launch { offsetX.snapTo(offsetX.value + drag.x) }
                    },
                    onDragEnd = {
                        scope.launch { offsetX.animateTo(0f, spring()) }
                    }
                )
            }
    )
}

Slyngning med hastighed

Ved en ægte slyngning fortsætter elementet med at bevæge sig, efter fingeren løftes. Spor hastigheden med en VelocityTracker, og giv den derefter til animateDecay, så elementet glider naturligt til standsning ved hjælp af en friktionsbaseret DecayAnimationSpec.

val decay = rememberSplineBasedDecay<Float>()

// on drag end, with measured velocity:
// scope.launch {
//     offsetX.animateDecay(
//         initialVelocity = velocity,
//         animationSpec = decay
//     )
// }

Swipe for at afvise med ankre

Ved swipebevægelser med faste hvilepositioner, f.eks. swipe for at afvise, springer anchoredDraggable til de definerede ankre. Du erklærer tilstandene og deres positioner i pixels, og Compose håndterer fjederen mellem dem.

enum class DragValue { Start, End }

val state = remember {
    AnchoredDraggableState(
        initialValue = DragValue.Start,
        anchors = DraggableAnchors {
            DragValue.Start at 0f
            DragValue.End at 300f
        }
    )
}

Uendelige og gentagne animationer

Til kontinuerlig bevægelse, f.eks. en pulserende prik eller en indlæsningsglans, skal du bruge rememberInfiniteTransition sammen med infiniteRepeatable. Angiv RepeatMode som Reverse for at køre frem og tilbage eller som Restart for at gentage fra begyndelsen.

@Composable
fun Pulse() {
    val transition = rememberInfiniteTransition(label = "pulse")
    val scale by transition.animateFloat(
        initialValue = 1f,
        targetValue = 1.2f,
        animationSpec = infiniteRepeatable(
            animation = tween(700),
            repeatMode = RepeatMode.Reverse
        ),
        label = "scale"
    )
    Box(Modifier.graphicsLayer { scaleX = scale; scaleY = scale })
}

Kombination af gestus og fjeder

Det klassiske interaktive mønster: træk for at flytte, og slip for at få elementet til at falde på plads. Spor hastigheden under trækket, og brug derefter enten animateDecay til en slyngning eller en spring mod et anker, afhængigt af afstanden eller hastigheden.

Det er præcis sådan, bundpaneler, karruseller og kort, der kan afvises, føles så naturlige.

// pattern:
// 1) onDrag    -> animatable.snapTo(current + delta)
// 2) onDragEnd -> if (fast) animatable.animateDecay(velocity, decay)
//                 else      animatable.animateTo(nearestAnchor, spring())

Fjederens stabilisering i ren Kotlin

For at mærke, hvordan en fjeder stabiliserer sig, får du her en minimal løkke i ren Kotlin. Den anvender en gendannende kraft mod målet og dæmper hastigheden for hvert trin – det er den samme idé, som Compose kører for hver frame.

fun main() {
    var pos = 0f
    var vel = 0f
    val target = 200f
    val stiffness = 0.2f
    val damping = 0.75f
    repeat(8) { step ->
        vel = (vel + (target - pos) * stiffness) * damping
        pos += vel
        println("step $step pos=${"%.1f".format(pos)}")
    }
}

Tryk, hold, og brug flere gestus

Træk er ikke den eneste gestus. pointerInput tilbyder også detectTapGestures til tryk, dobbelttryk og lange tryk samt detectTransformGestures til knib for at zoome og rotation.

Her udløser et langt tryk en fjederbaseret skalering, hvor registrering af gestus kombineres med en Animatable.

@Composable
fun LongPressScale() {
    val scale = remember { Animatable(1f) }
    val scope = rememberCoroutineScope()
    Box(
        Modifier
            .graphicsLayer { scaleX = scale.value; scaleY = scale.value }
            .pointerInput(Unit) {
                detectTapGestures(
                    onLongPress = {
                        scope.launch { scale.animateTo(1.4f, spring()) }
                    },
                    onPress = {
                        tryAwaitRelease()
                        scope.launch { scale.animateTo(1f, spring()) }
                    }
                )
            }
    )
}

Hurtig kontrol

Du bygger et kort, der kan trækkes, og som skal hoppe tilbage til sin oprindelige position, når det slippes. Hvilken kombination er det idiomatiske valg i Compose?

Opsummering: gestus og fjedre

Du kan nu bygge naturlig, interaktiv bevægelse:

  • Animatable giver imperativ, coroutine-drevet kontrol via animateTo / snapTo.
  • spring justeres med dampingRatio (hop) og stiffness (hastighed).
  • pointerInput + detectDragGestures omdanner berøring til bevægelse; animateDecay tilføjer slyngning.
  • anchoredDraggable springer til faste positioner ved swipeinteraktioner.
  • rememberInfiniteTransition driver gentagne effekter som pulser og glanseffekter.

Det afslutter animationer i Jetpack Compose. Du kan nu animere værdier, synlighed, indhold og gestus med sikkerhed.

Gratis at komme i gang

Lær Kotlin med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
36
Lektioner
152

Ofte stillede spørgsmål

Er lektionen “Gesture- og spring-animationer” gratis?

Ja — alle 3 lektioner i læringssporet Android Academy, inklusive “Gesture- og spring-animationer”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Android Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Gesture- og spring-animationer”?

Naturlig, fysikbaseret bevægelse. Du øver dig i Android Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Android Academy?

Der kræves ingen tidligere erfaring. Android Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Gesture- og spring-animationer”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Android Academy-lektion?

Ja. Alle Android Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Hvorfor animationer er vigtige
  2. Grundlæggende animate*AsState
  3. AnimatedVisibility og transitions
  4. Gesture- og spring-animationer
← Tilbage til Android Academy