مثال انیمیشن پیشرفته: ژست ها
با مجموعهها، منظم بمانید
ذخیره و طبقهبندی محتوا براساس اولویتهای شما.
هنگام کار با رویدادها و انیمیشن های لمسی، در مقایسه با زمانی که به تنهایی با انیمیشن کار می کنیم، باید چندین چیز را در نظر بگیریم. اول از همه، ممکن است نیاز داشته باشیم که وقتی رویدادهای لمسی شروع میشوند، یک انیمیشن در حال انجام را قطع کنیم زیرا تعامل کاربر باید بالاترین اولویت را داشته باشد.
در مثال زیر، ما از Animatable
برای نشان دادن موقعیت افست یک جزء دایره استفاده می کنیم. رویدادهای لمسی با اصلاحکننده pointerInput
پردازش میشوند. هنگامی که یک رویداد ضربه ای جدید را تشخیص می دهیم، animateTo
را صدا می کنیم تا مقدار افست را به موقعیت ضربه متحرک کنیم. یک رویداد ضربه ای نیز می تواند در طول انیمیشن اتفاق بیفتد، و در آن صورت، animateTo
انیمیشن در حال انجام را قطع می کند و انیمیشن را در موقعیت هدف جدید شروع می کند در حالی که سرعت انیمیشن قطع شده را حفظ می کند.
@Composable
fun Gesture() {
val offset = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) }
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
coroutineScope {
while (true) {
// Detect a tap event and obtain its position.
awaitPointerEventScope {
val position = awaitFirstDown().position
launch {
// Animate to the tap position.
offset.animateTo(position)
}
}
}
}
}
) {
Circle(modifier = Modifier.offset { offset.value.toIntOffset() })
}
}
private fun Offset.toIntOffset() = IntOffset(x.roundToInt(), y.roundToInt())
الگوی رایج دیگر این است که ما باید مقادیر انیمیشن را با مقادیر ناشی از رویدادهای لمسی، مانند کشیدن، همگام کنیم. در مثال زیر، میبینیم که "swipe to dismiss" بهعنوان یک Modifier
(بهجای استفاده از SwipeToDismiss
) اجرا شده است. افست افقی عنصر به عنوان Animatable
نشان داده می شود. این API یک ویژگی مفید در انیمیشن ژست دارد. مقدار آن را می توان با رویدادهای لمسی و همچنین انیمیشن تغییر داد. هنگامی که یک رویداد لمسی دریافت می کنیم، Animatable
را با روش stop
متوقف می کنیم تا هر انیمیشن در حال انجام رهگیری شود.
در طول یک رویداد کشیدن، از snapTo
برای به روز رسانی مقدار Animatable
با مقدار محاسبه شده از رویدادهای لمسی استفاده می کنیم. برای پرت کردن، Compose VelocityTracker
را برای ثبت رویدادهای کشیدن و محاسبه سرعت فراهم می کند. سرعت را می توان به طور مستقیم به animateDecay
برای انیمیشن پرتاب کرد. وقتی می خواهیم مقدار افست را به حالت اولیه برگردانیم، مقدار افست هدف 0f
را با متد animateTo
مشخص می کنیم.
fun Modifier.swipeToDismiss(
onDismissed: () -> Unit
): Modifier = composed {
val offsetX = remember { Animatable(0f) }
pointerInput(Unit) {
// Used to calculate fling decay.
val decay = splineBasedDecay<Float>(this)
// Use suspend functions for touch events and the Animatable.
coroutineScope {
while (true) {
val velocityTracker = VelocityTracker()
// Stop any ongoing animation.
offsetX.stop()
awaitPointerEventScope {
// Detect a touch down event.
val pointerId = awaitFirstDown().id
horizontalDrag(pointerId) { change ->
// Update the animation value with touch events.
launch {
offsetX.snapTo(
offsetX.value + change.positionChange().x
)
}
velocityTracker.addPosition(
change.uptimeMillis,
change.position
)
}
}
// No longer receiving touch events. Prepare the animation.
val velocity = velocityTracker.calculateVelocity().x
val targetOffsetX = decay.calculateTargetValue(
offsetX.value,
velocity
)
// The animation stops when it reaches the bounds.
offsetX.updateBounds(
lowerBound = -size.width.toFloat(),
upperBound = size.width.toFloat()
)
launch {
if (targetOffsetX.absoluteValue <= size.width) {
// Not enough velocity; Slide back.
offsetX.animateTo(
targetValue = 0f,
initialVelocity = velocity
)
} else {
// The element was swiped away.
offsetX.animateDecay(velocity, decay)
onDismissed()
}
}
}
}
}
.offset { IntOffset(offsetX.value.roundToInt(), 0) }
}
{% کلمه به کلمه %}
{% آخر کلمه %} برای شما توصیه می شود
{% کلمه به کلمه %} {% آخر کلمه %}
محتوا و نمونه کدها در این صفحه مشمول پروانههای توصیفشده در پروانه محتوا هستند. جاوا و OpenJDK علامتهای تجاری یا علامتهای تجاری ثبتشده Oracle و/یا وابستههای آن هستند.
تاریخ آخرین بهروزرسانی 2025-08-27 بهوقت ساعت هماهنگ جهانی.
[[["درک آسان","easyToUnderstand","thumb-up"],["مشکلم را برطرف کرد","solvedMyProblem","thumb-up"],["غیره","otherUp","thumb-up"]],[["اطلاعاتی که نیاز دارم وجود ندارد","missingTheInformationINeed","thumb-down"],["بیشازحد پیچیده/ مراحل بسیار زیاد","tooComplicatedTooManySteps","thumb-down"],["قدیمی","outOfDate","thumb-down"],["مشکل ترجمه","translationIssue","thumb-down"],["مشکل کد / نمونهها","samplesCodeIssue","thumb-down"],["غیره","otherDown","thumb-down"]],["تاریخ آخرین بهروزرسانی 2025-08-27 بهوقت ساعت هماهنگ جهانی."],[],[],null,["There are several things we have to take into consideration when we are working\nwith touch events and animations, compared to when we are working with\nanimations alone. First of all, we might need to interrupt an ongoing animation\nwhen touch events begin as user interaction should have the highest priority.\n\nIn the example below, we use an `Animatable` to represent the offset position of\na circle component. Touch events are processed with the\n[`pointerInput`](/reference/kotlin/androidx/compose/ui/input/pointer/package-summary#(androidx.compose.ui.Modifier).pointerInput(kotlin.Any,%20kotlin.coroutines.SuspendFunction1))\nmodifier. When we detect a new tap event, we call `animateTo` to animate the\noffset value to the tap position. A tap event can happen during the animation\ntoo, and in that case, `animateTo` interrupts the ongoing animation and starts\nthe animation to the new target position while maintaining the velocity of the\ninterrupted animation.\n\n\n```kotlin\n@Composable\nfun Gesture() {\n val offset = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) }\n Box(\n modifier = Modifier\n .fillMaxSize()\n .pointerInput(Unit) {\n coroutineScope {\n while (true) {\n // Detect a tap event and obtain its position.\n awaitPointerEventScope {\n val position = awaitFirstDown().position\n\n launch {\n // Animate to the tap position.\n offset.animateTo(position)\n }\n }\n }\n }\n }\n ) {\n Circle(modifier = Modifier.offset { offset.value.toIntOffset() })\n }\n}\n\nprivate fun Offset.toIntOffset() = IntOffset(x.roundToInt(), y.roundToInt())https://github.com/android/snippets/blob/5673ffc60b614daf028ee936227128eb8c4f9781/compose/snippets/src/main/java/com/example/compose/snippets/animations/AdvancedAnimationSnippets.kt#L62-L88\n```\n\n\u003cbr /\u003e\n\nAnother frequent pattern is we need to synchronize animation values with values\ncoming from touch events, such as drag. In the example below, we see \"swipe to\ndismiss\" implemented as a `Modifier` (rather than using the\n[`SwipeToDismiss`](/reference/kotlin/androidx/compose/material/package-summary#SwipeToDismiss(androidx.compose.material.DismissState,%20androidx.compose.ui.Modifier,%20kotlin.collections.Set,%20kotlin.Function1,%20kotlin.Function1,%20kotlin.Function1))\ncomposable). The horizontal offset of the element is represented as an\n`Animatable`. This API has a characteristic useful in gesture animation. Its\nvalue can be changed by touch events as well as the animation. When we receive a\ntouch down event, we stop the `Animatable` by the `stop` method so that any\nongoing animation is intercepted.\n\nDuring a drag event, we use `snapTo` to update the `Animatable` value with the\nvalue calculated from touch events. For fling, Compose provides\n`VelocityTracker` to record drag events and calculate velocity. The velocity can\nbe fed directly to `animateDecay` for the fling animation. When we want to slide\nthe offset value back to the original position, we specify the target offset\nvalue of `0f` with the `animateTo` method.\n\n\n```kotlin\nfun Modifier.swipeToDismiss(\n onDismissed: () -\u003e Unit\n): Modifier = composed {\n val offsetX = remember { Animatable(0f) }\n pointerInput(Unit) {\n // Used to calculate fling decay.\n val decay = splineBasedDecay\u003cFloat\u003e(this)\n // Use suspend functions for touch events and the Animatable.\n coroutineScope {\n while (true) {\n val velocityTracker = VelocityTracker()\n // Stop any ongoing animation.\n offsetX.stop()\n awaitPointerEventScope {\n // Detect a touch down event.\n val pointerId = awaitFirstDown().id\n\n horizontalDrag(pointerId) { change -\u003e\n // Update the animation value with touch events.\n launch {\n offsetX.snapTo(\n offsetX.value + change.positionChange().x\n )\n }\n velocityTracker.addPosition(\n change.uptimeMillis,\n change.position\n )\n }\n }\n // No longer receiving touch events. Prepare the animation.\n val velocity = velocityTracker.calculateVelocity().x\n val targetOffsetX = decay.calculateTargetValue(\n offsetX.value,\n velocity\n )\n // The animation stops when it reaches the bounds.\n offsetX.updateBounds(\n lowerBound = -size.width.toFloat(),\n upperBound = size.width.toFloat()\n )\n launch {\n if (targetOffsetX.absoluteValue \u003c= size.width) {\n // Not enough velocity; Slide back.\n offsetX.animateTo(\n targetValue = 0f,\n initialVelocity = velocity\n )\n } else {\n // The element was swiped away.\n offsetX.animateDecay(velocity, decay)\n onDismissed()\n }\n }\n }\n }\n }\n .offset { IntOffset(offsetX.value.roundToInt(), 0) }\n}https://github.com/android/snippets/blob/5673ffc60b614daf028ee936227128eb8c4f9781/compose/snippets/src/main/java/com/example/compose/snippets/animations/AdvancedAnimationSnippets.kt#L94-L152\n```\n\n\u003cbr /\u003e\n\nRecommended for you\n\n- Note: link text is displayed when JavaScript is off\n- [Value-based animations](/develop/ui/compose/animation/value-based)\n- [Drag, swipe, and fling](/develop/ui/compose/touch-input/pointer-input/drag-swipe-fling)\n- [Understand gestures](/develop/ui/compose/touch-input/pointer-input/understand-gestures)"]]