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:
Animatablezapewnia imperatywną kontrolę sterowaną przez korutyny za pomocąanimateTo/snapTo.springmożna dostrajać parametramidampingRatio(odbicie) istiffness(szybkość).pointerInput+detectDragGestureszamieniają dotyk w ruch, aanimateDecaydodaje rzut.anchoredDraggableprzyciąga element do stałych pozycji podczas interakcji opartych na przesunięciu.rememberInfiniteTransitionsteruje 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
- Dlaczego animacje mają znaczenie
- Podstawy animate*AsState
- AnimatedVisibility i przejścia
- Animacje gestów i sprężynowe