هناك عدة أمور يجب أخذها في الاعتبار عند التعامل مع أحداث اللمس والصور المتحركة، مقارنةً بالتعامل مع الصور المتحركة وحدها. أولاً، قد نحتاج إلى مقاطعة صورة متحركة مستمرة عندما تبدأ أحداث اللمس لأنّ تفاعل المستخدم يجب أن يكون له الأولوية القصوى.
في المثال التالي، نستخدم 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())
من الأنماط الشائعة الأخرى التي نحتاج إليها هي مزامنة قيم الحركة مع القيم الواردة من أحداث اللمس، مثل السحب. في المثال التالي، نرى أنّه تم تنفيذ "التمرير السريع للإغلاق" كدالة Modifier (بدلاً من استخدام الدالة المركّبة SwipeToDismiss). يتم تمثيل الإزاحة الأفقية للعنصر كقيمة Animatable. تحتوي واجهة برمجة التطبيقات هذه على سمة مفيدة
في الصور المتحركة للإيماءات. ويمكن تغيير قيمتها من خلال أحداث اللمس بالإضافة إلى
الرسوم المتحركة. عندما نتلقّى حدث النقر مع الاستمرار، نوقف 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) } }
اقتراحات مخصصة لك
- ملاحظة: يتم عرض نص الرابط عندما تكون JavaScript غير مفعّلة.
- الصور المتحركة المستندة إلى القيمة
- السحب والتمرير السريع والتمرير الإصبع ثم رفعه بسرعة
- التعرّف على الإيماءات