0Pricing
Android Academy · Lekcja

Animacje gestów i sprężynowe

Naturalny ruch oparty na fizyce

Animacje gestów i sprężynowe to bezpłatna lekcja Android Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Android Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Android Academy zawiera 4 lekcji w sumie.

Interaktywny ruch oparty na fizyce

Najbardziej satysfakcjonujące animacje reagują na palec użytkownika i zachowują się jak prawdziwe obiekty — mają bezwładność i odbicie. Aby je tworzyć, należy połączyć gesty z animacją opartą na fizyce.

W tej lekcji omówiono Animatable, sprężyny, wygaszanie ruchu typu fling oraz łączenie ich z gestami przeciągania w celu uzyskania naturalnego, interaktywnego interfejsu użytkownika.

Animatable: kontrola niskiego poziomu

Podczas gdy animate*AsState reaguje na stan, Animatable zapewnia kontrolę imperatywną. Należy przechowywać go w remember i sterować nim z poziomu korutyny za pomocą wywołań takich jak animateTo lub snapTo.

To podstawa ruchu sterowanego gestami.

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

Sprężyny: dampingRatio i stiffness

Specyfikację spring definiują dwie wartości:

  • dampingRatio — określa sprężystość. Niższa wartość oznacza silniejsze odbijanie; 1f (tłumienie krytyczne) nie powoduje przekroczenia celu.
  • stiffness — określa szybkość. Wyższa wartość oznacza szybsze osiągnięcie celu.

Compose udostępnia stałe takie jak Spring.DampingRatioMediumBouncy i Spring.StiffnessLow, dlatego rzadko trzeba korzystać z surowych liczb.

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

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

Wykrywanie gestów przeciągania

Aby nadać ruchowi interaktywny charakter, należy odczytywać dane wejściowe dotyku. Modifier.pointerInput wraz z detectDragGestures zgłasza przesunięcia podczas poruszania palcem.

Przesunięcia te są sumowane w wartości offsetu, a następnie stosowane do 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
                }
            }
    )
}

Powrót sprężysty

Połącz przeciąganie z Animatable, aby uzyskać efekt „powrotu na miejsce”. Podczas przeciągania użyj snapTo, aby natychmiast ustawić pozycję palca, a po jego zwolnieniu wywołaj animateTo(0f) ze sprężyną, dzięki czemu element odbije się i wróci na miejsce.

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

Rzut z prędkością

Rzeczywiste rzuty nie kończą się w chwili oderwania palca od ekranu. Śledź prędkość za pomocą VelocityTracker, a następnie przekaż ją do animateDecay, aby element płynnie wyhamował w naturalny sposób, korzystając z opartego na tarciu DecayAnimationSpec.

val decay = rememberSplineBasedDecay<Float>()

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

Przesunięcie w celu zamknięcia z punktami zakotwiczenia

W przypadku gestów przesunięcia ze stałymi pozycjami spoczynkowymi, takich jak przesunięcie w celu zamknięcia, anchoredDraggable przyciąga element do zdefiniowanych punktów zakotwiczenia. Deklarujesz stany i odpowiadające im pozycje w pikselach, a Compose obsługuje przejście sprężyste między nimi.

enum class DragValue { Start, End }

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

Nieskończone i powtarzające się animacje

W przypadku ciągłego ruchu, takiego jak pulsująca kropka lub animowany efekt shimmer podczas ładowania, użyj rememberInfiniteTransition wraz z infiniteRepeatable. Ustaw RepeatMode na Reverse, aby uzyskać ruch tam i z powrotem, lub na Restart, aby zapętlić animację.

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

Łączenie gestu ze sprężyną

Charakterystyczny wzorzec interakcji wygląda tak: przeciągnij, aby przesunąć, a następnie zwolnij, aby element osiadł na miejscu. Śledź prędkość podczas przeciągania, a potem, zależnie od odległości lub szybkości, wykonaj rzut za pomocą animateDecay albo zastosuj spring, aby osadzić element przy punkcie zakotwiczenia.

Właśnie dlatego dolne arkusze, karuzele i karty możliwe do zamknięcia działają tak naturalnie.

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

Osiadanie sprężyny w czystym Kotlinie

Aby zobaczyć, jak sprężyna osiada, przeanalizuj tę minimalną pętlę napisaną w czystym Kotlinie. W każdym kroku stosuje ona siłę przywracającą w kierunku celu i tłumi prędkość — to ta sama idea, którą Compose realizuje w każdej klatce.

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

Dotknięcia, naciśnięcia i wiele gestów

Przeciąganie nie jest jedynym gestem. pointerInput oferuje również detectTapGestures do obsługi dotknięć, dwukrotnych dotknięć i długich naciśnięć, a także detectTransformGestures do powiększania gestem uszczypnięcia i obracania.

W tym przykładzie długie naciśnięcie uruchamia sprężyste skalowanie, łącząc wykrywanie gestu z 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()) }
                    }
                )
            }
    )
}

Szybkie sprawdzenie

Tworzysz kartę, którą można przeciągać i która po zwolnieniu powinna odbić się z powrotem do pozycji początkowej. Które połączenie jest idiomatycznym wyborem w Compose?

Podsumowanie: gesty i sprężyny

Możesz już tworzyć naturalny, interaktywny ruch:

  • Animatable zapewnia imperatywną kontrolę sterowaną przez korutyny za pomocą animateTo / snapTo.
  • spring można dostrajać parametrami dampingRatio (odbicie) i stiffness (szybkość).
  • pointerInput + detectDragGestures zamieniają dotyk w ruch, a animateDecay dodaje rzut.
  • anchoredDraggable przyciąga element do stałych pozycji podczas interakcji opartych na przesunięciu.
  • rememberInfiniteTransition steruje zapętlonymi efektami, takimi jak pulsowanie i shimmer.

To kończy temat animacji w Jetpack Compose. Możesz teraz z pewnością animować wartości, widoczność, zawartość i gesty.

Często zadawane pytania

Czy lekcja „Animacje gestów i sprężynowe” jest bezpłatna?

Tak — pełny tekst „Animacje gestów i sprężynowe” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Android Academy, przejdź na CoddyKit PRO. Kurs Android Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Animacje gestów i sprężynowe”?

Naturalny ruch oparty na fizyce Ćwiczysz Android Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Android Academy?

Nie wymagamy żadnego doświadczenia. Android Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „Animacje gestów i sprężynowe”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Android Academy?

Tak. Każda lekcja Android Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Dlaczego animacje mają znaczenie
  2. Podstawy animate*AsState
  3. AnimatedVisibility i przejścia
  4. Animacje gestów i sprężynowe
← Powrót do Android Academy