Compose (11/N) - 手势
一、点击
1.1 可点击 Modifier.clickable( )
允许应用检测对该元素的点击。
@Composable
fun ClickableSample() {val count = remember { mutableStateOf(0) }Text(text = count.value.toString(),modifier = Modifier.clickable { count.value += 1 })
}
1.2 手势检测 Modifier.pointerInput( )
当需要更大灵活性时,提供点按手势检测器。
detectTapGestures( 检测点击手势 |
detectDragGestures( 检测拖动手势:dragAmount.x 和 dragAmount.y 拿到各方向上的拖动距离。 |
Modifier.pointerInput(Unit) {detectTapGestures(...)
}
二、滚动
2.1 滚动修饰符 Modifier.verticalScroll( )、Modifier.horizontalScroll( )
Modifier.verticalScroll 和 Modifier.horizontalScroll 可以让内容边界大于最大尺寸约束时滚动里面的元素。借助 ScrollState 还可以更改滚动位置或获取当前状态。
@Composable
fun ScrollBoxes() {val scrollState = rememberScrollState()LaunchedEffect(Unit) { scrollState.animateScrollTo(100) }Column(modifier = Modifier.background(Color.LightGray).size(100.dp)
// .verticalScroll(rememberScrollState()) //使用默认参数.verticalScroll(scrollState) //一显示就会自动滚动100px) {repeat(10) {Text("Item $it", modifier = Modifier.padding(2.dp))}}
}
2.2 可滚动修饰符 Modifier.scrollable( )
只检测手势不偏移内容。构造时需要提供一个 consumeScrollDelta( ) 函数,该函数在每个滚动步骤都会调用,以像素为单位,返回所消耗的距离。
@Composable
fun ScrollableSample() {var offset by remember { mutableStateOf(0f) }Box(Modifier.size(150.dp).scrollable(orientation = Orientation.Vertical,state = rememberScrollableState { delta ->//拿到每次滑动的偏移量deltaoffset += deltadelta}).background(Color.LightGray),contentAlignment = Alignment.Center) {Text(offset.toString())}
}
2.3 嵌套滚动
2.3.1 自动嵌套滚动
简单的嵌套滚动无需额外操作,当子元素无法进一步滚动时手势会由父元素处理,手势会自动从子元素传播到父元素。
//父Box嵌套10个子Box,子Box滚动到边界会滚动父Box
@Composable
fun ScrollableSample() {//设置渐变色方便观察子Box滚动(蓝→黄1000级)val gradient = Brush.verticalGradient(0f to Color.Blue, 1000f to Color.Yellow)Box(modifier = Modifier.background(Color.LightGray).verticalScroll(rememberScrollState()).padding(32.dp)) {Column {repeat(10) {Box(modifier = Modifier.height(128.dp).verticalScroll(rememberScrollState())) {Text(text = "Scroll here",color = Color.Red,modifier = Modifier.border(12.dp, Color.DarkGray).background(brush = gradient).padding(24.dp).height(150.dp))}}}}
}
2.3.2 nestedScroll 修饰符
2.3.3 嵌套滚动互操作性(v1.2.0)
三、拖动
只检测手势不偏移内容(需要保存状态并在屏幕上表示,例如通过 offset 修饰符移动元素),以像素为单位。
3.1 线性拖动(一维)Modifier.draggable( )
//文字横向拖动
var offsetX by remember { mutableStateOf(0f) }
Text(modifier = Modifier.offset { IntOffset(offsetX.roundToInt(), 0) }.draggable(orientation = Orientation.Horizontal,state = rememberDraggableState { delta ->offsetX += delta}),text = "Drag me!"
)
3.2 平面拖动(二维)Modifier.pointerInput( )
改为 pointerInput 修饰符使用手势检测。
//父Box中拖动蓝色子Box
@Composable
fun ScrollableSample() {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).pointerInput(Unit) {detectDragGestures { change, dragAmount ->change.consume()offsetX += dragAmount.xoffsetY += dragAmount.y}})}
}
四、滑动 Modifier.swipeable( )
只检测手势不偏移内容(需要保存状态并在屏幕上表示,例如通过 offset 修饰符移动元素)。具有惯性,释放后会朝着锚点呈现动画效果,常见用途是滑动关闭。
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SwipeableSample() {val squareSize = 48.dp //子Box的大小val swipeableState = rememberSwipeableState(0)val sizePx = with(LocalDensity.current) { squareSize.toPx() } //DP转PX//设置锚点(key是像素,value是索引)val anchors = mapOf(0f to 0, sizePx to 1)Box(modifier = Modifier.width(96.dp).swipeable(state = swipeableState,anchors = anchors,//阈值(超过就会自己滑到底,达不到就会滑回来)thresholds = { _, _ -> FractionalThreshold(0.3f) },orientation = Orientation.Horizontal).background(Color.LightGray)) {Box(Modifier.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }.size(squareSize).background(Color.DarkGray))}
}
五、多点触控 Modifier.transformable( )
只检测手势不转换元素。平移、缩放、旋转。
@Composable
fun TransformableSample() {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 *= zoomChangerotation += rotationChangeoffset += offsetChange}Box(Modifier.graphicsLayer(scaleX = scale, //等比缩放scaleY = scale, //等比缩放rotationZ = rotation,translationX = offset.x,translationY = offset.y).transformable(state = state).background(Color.Blue).fillMaxSize())
}