Home > database >  Clickable area not updated in Jetpack compose
Clickable area not updated in Jetpack compose

Time:10-31

I'm currently experimenting with the new Jetpack Compose UI toolkit and for now I really love it. With the following code I can draw any shape on the screen, make it clickable and move it around:

@Composable
fun Shape(
    color: Color, @FloatRange(from = 0.0, to = 1.0) transparency: Float, strokeWidth: Dp,
    onMoveShape: (dragAmount: Offset) -> Unit,
    builder: Path.(size: Size, layoutDirection: LayoutDirection) -> Unit
) {
    val fillColor = color.copy(transparency)
    val drawingShape = GenericShape(builder)

    val position = Modifier.fillMaxSize()

    val border = Modifier.border(strokeWidth, color, drawingShape)
    val background = Modifier.background(fillColor, drawingShape)
    val clip = Modifier.clip(drawingShape)

    val clickListener = Modifier.clickable { /* ... */ }

    val dragListener = Modifier.draggable { change, dragAmount ->
        change.consumeAllChanges()
        onMoveShape(dragAmount)
    }

    Box(position   border   background   clip   clickListener   dragListener)
}

The onMoveShape callback moves the points used in the builder around which triggers a recomposition which works great. My problem here is that the area from where I can start the drag and click events does not update when I move the shape. I think it's best to explain that in this screenshot:

enter image description here

I removed the code for the red dots above since it is not important for the question. I guess since the click and drag listeners do not depend on the drawingShape they don't get recomposed and that's why the area is not updated. Any ideas how I can archive this?

The for Modifier and the draggable Modifier are extension functions btw.

CodePudding user response:

To create a clickable and draggable composable,

@Composable
fun DraggableShape() {
    Box(modifier = Modifier.fillMaxSize()) {
        var offsetX by remember { mutableStateOf(0f) }
        var offsetY by remember { mutableStateOf(0f) }

        Box(
            Modifier
                .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                .background(Color.Blue)
                .size(50.dp)
                .clickable {
                    Log.e("TAG","Clicked")
                }
                .pointerInput(Unit) {
                    detectDragGestures { change, dragAmount ->
                        change.consumeAllChanges()
                        offsetX  = dragAmount.x
                        offsetY  = dragAmount.y
                    }
                }
        )
    }
}

Source: Android Docs

The click events are working even after dragging.

Note:
Use pointerInput for dragging in all directions and draggable for dragging in a single direction.

If this is still not working for you, kindly provide a minimal code with the issue reproducible.
(Current code has extensions, shapes, etc and I couldn't find the exact issue in that)

CodePudding user response:

Do not place your dragListener inside your composable. If your composable recomposes, it will get destroyed and recreated causing it to lose state. Instead, pass in the the drag listener as a lamda parameter. Use state hoisting to do this. Your hoisted composable should not recompose. Placing normal listeners like click listeners inside a composable is fine because most of the time, recomposing them isn't going to effect their logic - such as a button click event. But if your listener needs to maintain state data during events like dragging, you need to maintain that state. Try something like this:

@Composable
fun ShapeHandler() {

    val dragListener = Modifier.draggable { change, dragAmount ->
        change.consumeAllChanges()
        onMoveShape(dragAmount)
    }
   
   Shape(
      color = xxx,
      transparency = xxx,
      strokeWidth = xxx
      onMoveShape = xxx,
      builder = xxx,
      dragListener = dragListener
   )
}

@Composable
fun Shape(
    color: Color, 
    @FloatRange(from = 0.0, to = 1.0) transparency: Float, 
    strokeWidth: Dp,
    onMoveShape: (dragAmount: Offset) -> Unit,
    builder: Path.(size: Size, layoutDirection: LayoutDirection) -> Unit,
    dragListener: DragListener
) {
    val fillColor = color.copy(transparency)
    val drawingShape = GenericShape(builder)

    val position = Modifier.fillMaxSize()

    val border = Modifier.border(strokeWidth, color, drawingShape)
    val background = Modifier.background(fillColor, drawingShape)
    val clip = Modifier.clip(drawingShape)

    val clickListener = Modifier.clickable { /* ... */ }

    Box(position   border   background   clip   clickListener   dragListener)
}
  • Related