Jetpack Compose 12: Mastering Gestures and Touch Interactions

Jetpack Compose 12: Mastering Gestures and Touch Interactions

TB

Teqani Blogs

Writer at Teqani

June 5, 20254 min read

In modern UI development, touch interactions are paramount. This article delves into how Jetpack Compose simplifies handling various gestures, offering a range of APIs from simple taps to intricate zoom and pan interactions. We will explore different Modifiers that can be used to implement gesture recognition, as well as the handling of touch events, empowering developers to create truly interactive user experiences.



في تطوير واجهات المستخدم الحديثة، تعد تفاعلات اللمس ذات أهمية قصوى. تتعمق هذه المقالة في كيفية تبسيط Jetpack Compose للتعامل مع الإيماءات المختلفة، وتقدم مجموعة من واجهات برمجة التطبيقات (APIs) تتراوح من النقرات البسيطة إلى تفاعلات التكبير والتحريك المعقدة. سوف نستكشف مُعدِّلات مختلفة يمكن استخدامها لتنفيذ التعرف على الإيماءات، بالإضافة إلى التعامل مع أحداث اللمس، مما يمكّن المطورين من إنشاء تجارب مستخدم تفاعلية حقًا.



1. Modifier.pointerInput – Low-Level Gesture Detection

The Modifier.pointerInput allows you to detect taps, long presses, drags, and other gestures using suspending gesture detectors. This provides fine-grained control over touch behavior, ideal for custom gesture implementations.



يسمح لك Modifier.pointerInput بالكشف عن النقرات والضغطات الطويلة والسحب والإيماءات الأخرى باستخدام كاشفات الإيماءات المعلقة. يوفر هذا تحكمًا دقيقًا في سلوك اللمس، وهو مثالي لتطبيقات الإيماءات المخصصة.



Example:



@Composable
fun PointerInputExample() {
    Box(
        modifier = Modifier
            .size(200.dp)
            .background(Color.LightGray)
            .pointerInput(Unit) {
                detectTapGestures(
                    onTap = { offset ->
                        println("Tapped at $offset")
                    },
                    onLongPress = {
                        println("Long pressed!")
                    }
                )
            }
    ) {
        Text("Tap or Long Press", modifier = Modifier.align(Alignment.Center))
    }
}


2. Modifier.transformable – Pan, Zoom, Rotate

Use Modifier.transformable for implementing scale and rotation gestures, commonly used on images or maps. This modifier simplifies the implementation of pan, zoom, and rotate functionalities.



استخدم Modifier.transformable لتنفيذ إيماءات المقياس والتدوير، والتي يشيع استخدامها في الصور أو الخرائط. يعمل هذا المُعدِّل على تبسيط تنفيذ وظائف التحريك والتكبير والتدوير.



Example:



@Composable
fun TransformableExample() {
    var scale by remember { mutableStateOf(1f) }
    var rotation by remember { mutableStateOf(0f) }
    var offset by remember { mutableStateOf(Offset.Zero) }
    val state = rememberTransformableState { zoomChange, rotationChange, offsetChange ->
        scale *= zoomChange
        rotation += rotationChange
        offset += offsetChange
    }
    Box(
        modifier = Modifier
            .size(250.dp)
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale,
                rotationZ = rotation,
                translationX = offset.x,
                translationY = offset.y
            )
            .background(Color.Cyan)
            .transformable(state)
    ) {
        Text("Pinch, Drag, Rotate", modifier = Modifier.align(Alignment.Center))
    }
}


3. Modifier.swipeable – Swipe-Based UI Control

Create swipe actions such as dismiss, toggle, or sliding panels using Modifier.swipeable. This is suitable for implementing swipe-based controls within your UI.



أنشئ إجراءات التمرير مثل الرفض أو التبديل أو اللوحات المنزلقة باستخدام Modifier.swipeable. هذا مناسب لتنفيذ عناصر التحكم المستندة إلى التمرير داخل واجهة المستخدم الخاصة بك.



Example:



@Composable
fun SwipeableExample() {
    val width = 300f
    val swipeState = rememberSwipeableState(0)
    val anchors = mapOf(0f to 0, width to 1)
    Box(
        modifier = Modifier
            .width(width.dp)
            .height(100.dp)
            .background(Color.LightGray)
            .swipeable(
                state = swipeState,
                anchors = anchors,
                thresholds = { _, _ -> FractionalThreshold(0.3f) },
                orientation = Orientation.Horizontal
            )
    ) {
        Box(
            modifier = Modifier
                .offset { IntOffset(swipeState.offset.value.roundToInt(), 0) }
                .fillMaxSize()
                .background(Color.Green)
        ) {
            Text("Swipe Me", modifier = Modifier.align(Alignment.Center))
        }
    }
}


4. Modifier.pointerInteropFilter – Handle Raw MotionEvent

When interoperability with the traditional Android touch system is required, use Modifier.pointerInteropFilter to handle raw MotionEvents. This is helpful for integrating legacy views or gestures.



عندما تكون هناك حاجة إلى التشغيل البيني مع نظام اللمس التقليدي لنظام Android، استخدم Modifier.pointerInteropFilter للتعامل مع MotionEvents الخام. هذا مفيد لدمج طرق العرض أو الإيماءات القديمة.



Example:



@Composable
fun PointerInteropExample() {
    Box(
        modifier = Modifier
            .size(200.dp)
            .background(Color.Yellow)
            .pointerInteropFilter {
                when (it.action) {
                    MotionEvent.ACTION_DOWN -> {
                        println("Finger down at: ${it.x}, ${it.y}")
                        true
                    }
                    MotionEvent.ACTION_UP -> {
                        println("Finger up!")
                        true
                    }
                    else -> false
                }
            }
    ) {
        Text("Interop Touch", modifier = Modifier.align(Alignment.Center))
    }
}


  • pointerInput: Tap, drag, long press, raw gestures
  • transformable: Pinch, zoom, rotate, and drag gestures
  • swipeable: Swipe actions like toggle or dismiss
  • pointerInteropFilter: Use raw MotionEvents like Android XML
TB

Teqani Blogs

Verified
Writer at Teqani

Senior Software Engineer with 10 years of experience

June 5, 2025
Teqani Certified

All blogs are certified by our company and reviewed by our specialists
Issue Number: #a08ef291-dbbc-4b40-aeef-ac59acf88342