Handle Click Events

key apis are clickable Modifier

@Composable
private fun ClickableItem() {
    Box(Modifier.background(Color.Yellow).clickable {
        println("Clicked")
    }) { Text("Hello") }
}

@Preview
@Composable
fun Compose15() {
    ClickableItem()
}
Handle Press Events

key apis are pointerInput detectTapGestures

@Composable
private fun DetectGestures() {
    Box(Modifier.background(Color.Yellow).pointerInput(Unit) {
        detectTapGestures(
            onTap = { println("OnTap $it") },
            onDoubleTap = { println("onDoubleTap $it") },
            onPress = { println("onPress $it") },
            onLongPress = { println("onLongPress $it") }
        )
    }) { Text("Hello") }
}

@Preview
@Composable
fun Compose16() {
    DetectGestures()
}
Handle Drag Events

key apis are draggable rememberDraggableState

background must set after offset, otherwise offset will not applied to background

@Composable
private fun Draggable() {
    Box(Modifier.background(Color.Red).width(300.dp).height(200.dp)) {
        var offsetX by remember { mutableFloatStateOf(0f) }
        var parentWidth by remember { mutableFloatStateOf(0f) }
        var elementWidth by remember { mutableFloatStateOf(0f) }
        val minOffset by remember {
            derivedStateOf { (elementWidth - parentWidth) / 2 }
        }
        val maxOffset by remember {
            derivedStateOf { (parentWidth - elementWidth) / 2 }
        }
        Text(
            text = "Hello",
            modifier = Modifier.offset {
                println("$offsetX $minOffset $maxOffset")
                IntOffset(offsetX.coerceAtLeast(minOffset).coerceAtMost(maxOffset).roundToInt(), 0)
            }.draggable(
                orientation = Orientation.Horizontal,
                state = rememberDraggableState { delta ->
                    offsetX = (offsetX + delta).coerceAtLeast(minOffset).coerceAtMost(maxOffset)
                }
            ).onGloballyPositioned { coordinates ->
                elementWidth = coordinates.size.width.toFloat()
                parentWidth = coordinates.parentLayoutCoordinates!!.size.width.toFloat()
            }.background(Color.Yellow).align(Alignment.Center)
        )
    }
}

@Preview
@Composable
fun Compose17() {
    Draggable()
}
Handle Drag Events on Multiple Directions

key apis are pointerInput detectDragGestures

@Composable
private fun Draggable() {
    Box(Modifier.background(Color.Red).width(300.dp).height(200.dp)) {
        var offsetX by remember { mutableFloatStateOf(0f) }
        var offsetY by remember { mutableFloatStateOf(0f) }
        val minOffset = (-100).dp
        val maxOffset = 100.dp
        Box(
            Modifier.width(100.dp).height(100.dp).offset {
                IntOffset(
                    offsetX.coerceAtLeast(minOffset.toPx()).coerceAtMost(maxOffset.toPx()).roundToInt(),
                    offsetY.coerceAtLeast(minOffset.toPx()).coerceAtMost(maxOffset.toPx()).roundToInt()
                )
            }.pointerInput(Unit) {
                detectDragGestures { change, amount ->
                    change.consume()
                    offsetX = (offsetX + amount.x).coerceAtLeast(minOffset.toPx()).coerceAtMost(maxOffset.toPx())
                    offsetY = (offsetY + amount.y).coerceAtLeast(minOffset.toPx()).coerceAtMost(maxOffset.toPx())
                }
            }.background(Color.Yellow).align(Alignment.Center)
        )
    }
}

@Preview
@Composable
fun Compose18() {
    Draggable()
}
Handle Scroll Event

key apis are horizontalScroll verticalScroll rememberScrollState

by default scroll range is area size of box content

both horizontalScroll and verticalScroll are implemented based on scrollable Modifier

@Composable
private fun ScrollBar() {
    val gradient1 = Brush.verticalGradient(colors = listOf(Color.Red, Color.Yellow, Color.Red))
    val gradient2 = Brush.verticalGradient(colors = listOf(Color.Blue, Color.Green, Color.Blue))
    val gradient3 = Brush.verticalGradient(colors = listOf(Color.Black, Color.White, Color.Black))
    Box(Modifier.background(gradient1).width(400.dp).height(200.dp).verticalScroll(rememberScrollState())) {
        Column(Modifier.background(gradient2).width(300.dp).height(300.dp).align(Alignment.Center).verticalScroll(rememberScrollState())) {
            Box(Modifier.background(gradient3).width(200.dp).height(600.dp).align(Alignment.CenterHorizontally))
        }
    }
}

@Preview
@Composable
fun Compose19() {
    ScrollBar()
}
Handle Swipe Event

key apis are swipeable offset rememberSwipeableState

background must set after offset, otherwise offset will not applied to background

swipeable is implemented based on draggable Modifier

@Composable
@OptIn(ExperimentalMaterialApi::class)
private fun Swipe() {
    val swipeState = rememberSwipeableState(0f)
    val endPosition = with(LocalDensity.current) { 80.dp.toPx() }
    val anchors = mapOf(0f to 0f, endPosition to 1f)
    Box(Modifier.width(200.dp).swipeable(
        state = swipeState,
        anchors = anchors,
        orientation = Orientation.Horizontal,
        thresholds = { from, to -> FractionalThreshold(0.5f) }
    ).background(Color.Yellow)) {
        val gradient = Brush.horizontalGradient(colors = listOf(Color.Black, Color.White, Color.Black))
        Box(Modifier.size(100.dp).offset {
            println(swipeState.currentValue)
            IntOffset(swipeState.offset.value.roundToInt(), 0)
        }.background(gradient))
    }
}

@Preview
@Composable
fun Compose21() {
    Swipe()
}
  • anchors, decide where can stop, and corresponding states at stop points
  • SwipeableState, save states at current offset
  • thresholds, decide the threshold for auto-swipe-to-target or swipe-back-to-initial
Handle Transform Event

Transform means transfrom through multi-pointer-touch here

that is, scale translate rotate by multi-finger gestures

key apis are transformable rememberTransformableState graphicsLayer

@Composable
private fun MultiFingerTouch() {
    var scale by remember { mutableFloatStateOf(1f) }
    var rotation by remember { mutableFloatStateOf(0f) }
    var translation by remember { mutableStateOf(Offset.Zero) }
    val state = rememberTransformableState { zoomChange, offsetChange, rotationChange ->
        scale *= zoomChange
        rotation += rotationChange
        translation += offsetChange
    }
    Box(Modifier.width(250.dp).height(250.dp).graphicsLayer {
        scaleX = scale
        scaleY = scale
        translationX = translation.x
        translationY = translation.y
        rotationZ = rotation
    }.transformable(state).background(Color.Yellow))
}

@Preview
@Composable
fun Compose22() {
    MultiFingerTouch()
}
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐