Kyu-Ho Hwang
Kyu-Ho Hwang

Reputation: 183

how can I one touch drag and multi touch drag in android compose kotlin

I want to move Box one touch drag and multi touch drag, however, it only moves multitouch. How can I use both of them using one offset variable

@Composable
fun TransformableSample() {
    // set up all transformation states
    var scale by remember { mutableStateOf(1f) }
    var rotation by remember { mutableStateOf(0f) }
    var offset by remember { mutableStateOf(Offset.Zero) }
    val state = rememberTransformableState { zoomChange, offsetChange, rotationChange ->
        scale *= zoomChange
        rotation += rotationChange
        offset += offsetChange
    }
    Box(
        Modifier
            // apply other transformations like rotation and zoom
            // on the pizza slice emoji
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale,
                rotationZ = rotation,
                translationX = offset.x,
                translationY = offset.y
            )
            // add transformable to listen to multitouch transformation events
            // after offset
            .transformable(state = state)
            .background(Color.Blue)
            .fillMaxSize()
    )
}

Upvotes: 2

Views: 1644

Answers (1)

Alec Ramirez
Alec Ramirez

Reputation: 31

You're going to want to use a Modifier.pointerInput() combined with Modifier.graphicsLayer(). The important thing for you is that this will let you drag your image around with one finger or with two fingers. It'll still use the remember states (although I usually hold these values as well as all the math in my ViewModel). Here's the code copied from the documentation.

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput

/**
 * Rotates the given offset around the origin by the given angle in degrees.
 *
 * A positive angle indicates a counterclockwise rotation around the right-handed 2D Cartesian
 * coordinate system.
 *
 * See: [Rotation matrix](https://en.wikipedia.org/wiki/Rotation_matrix)
 */
fun Offset.rotateBy(angle: Float): Offset {
   val angleInRadians = angle * PI / 180
   return Offset(
      (x * cos(angleInRadians) - y * sin(angleInRadians)).toFloat(),
      (x * sin(angleInRadians) + y * cos(angleInRadians)).toFloat()
   )
}

var offset by remember { mutableStateOf(Offset.Zero) }
var zoom by remember { mutableStateOf(1f) }
var angle by remember { mutableStateOf(0f) }

Box(
   Modifier
      .pointerInput(Unit) {
         detectTransformGestures(
            onGesture = { centroid, pan, gestureZoom, gestureRotate ->
               val oldScale = zoom
               val newScale = zoom * gestureZoom

               // For natural zooming and rotating, the centroid of the gesture should
               // be the fixed point where zooming and rotating occurs.
               // We compute where the centroid was (in the pre-transformed coordinate
               // space), and then compute where it will be after this delta.
               // We then compute what the new offset should be to keep the centroid
               // visually stationary for rotating and zooming, and also apply the pan.
               offset = (offset + centroid / oldScale).rotateBy(gestureRotate) -
                   (centroid / newScale + pan / oldScale)
               zoom = newScale
               angle += gestureRotate
            }
         )
      }
      .graphicsLayer {
         translationX = -offset.x * zoom
         translationY = -offset.y * zoom
         scaleX = zoom
         scaleY = zoom
         rotationZ = angle
         transformOrigin = TransformOrigin(0f, 0f)
      }
      .background(Color.Blue)
      .fillMaxSize()
)

Upvotes: 3

Related Questions