【Jetpack Compose】手势之点击、拖动和滑动事件

Compose在手势处理方面,为开发者提供了众多的API,可以说比原生View的手势处理更为方便,考虑到手势内容种类比较多,分两节内容详细介绍,本篇文章主要介绍点击、双击、长按、拖动和滑动事件,较为复杂的滚动、和多点触控放在下一篇文章介绍。

在上一篇【Jetpack Compose】仿微信查看大图渐入渐出效果中也是使用了比较常用的几种手势,看完本篇可以更加轻松的去理解上一篇的处理逻辑。

单击、双击和长按事件

在学习手势的开始,先从最简单的单击事件入手,看看Compose是如何为开发者提供单击事件的入口。

kotlin 复制代码
@Composable
fun ClickGestureScreen() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize()
    ) {
        Text(text = "点击事件", modifier = Modifier.clickable {
            Log.d(TAG, "ClickGestureScreen: click")
        })
    }
}

fun Modifier.clickable(
    enabled: Boolean = true,
    onClickLabel: String? = null,
    role: Role? = null,
    onClick: () -> Unit
)

单击事件可以由Modifier.clickable()方法提供,此方法有四个参数,其中只有onClick参数为必传,其余三个参数都是可以默认不传入,参数的介绍分别如下:

  • enabled:表示点击事件是否可用,默认为true,可使用此参数控制点击事件是否响应;
  • onClickLabel:此参数主要用于无障碍模式下播报的标签,一般情况下不会使用,如果应用需要适配无障碍模式,那么此参数就需要设置对应的语义;
  • role:此参数也是用于无障碍模式,它充当了一种角色,Compose默认提供了一系列角色比如:Button、CheckBox等;
  • onClick:此参数就是点击事件响应的回调,可在此Lambda内部进行点击事件的逻辑处理。

Compose的点击事件中,默认会添加水波纹效果,如果不需要此效果,可以将indication设置为null

kotlin 复制代码
@Composable
fun ClickGestureScreen() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize()
    ) {
        Text(text = "去除点击事件的水波纹", modifier = Modifier.clickable(
            interactionSource = remember {
                MutableInteractionSource()
            },
            indication = null
        ) {
            Log.d(TAG, "ClickGestureScreen: click")
        })
    }
}

点击事件为手势中最为简单的事件之一了,下面我们继续看下长按和双击事件是如何处理的。

less 复制代码
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun DoubleClickGestureScreen() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize()
    ) {
        Text(text = "双击和长按事件", modifier = Modifier.combinedClickable(
            onLongClick = { Log.d(TAG, "DoubleClickGestureScreen: onLongClick") },
            onDoubleClick = { Log.d(TAG, "DoubleClickGestureScreen: onDoubleClick") },
            onClick = { Log.d(TAG, "DoubleClickGestureScreen: onClick") }
        ))
    }
}

长按和双击事件只是将Modifier.clickable()方法替换成combinedClickable()即可,combinedClickable方法内部有三个Lambda参数,只需要在对应的参数中处理相应的逻辑即可,还是非常便捷的。

combinedClickable()方法也是同样可以去除水波纹效果的,用法可单击事件一样~

拖动事件

在Compose中,拖动事件可以通过Modifier.draggable()方法提供,可以监听水平或者垂直方向上拖动事件,下面来看看具体是如何监听拖动事件的。

kotlin 复制代码
@Composable
fun DraggableGestureScreen() {

    var dragDistance by remember {
        mutableStateOf(0F)
    }
    val draggableState = rememberDraggableState(onDelta = {
        dragDistance += it
        Log.d(TAG, "DraggableGestureScreen onDelta: $it, dragDistance: $dragDistance")
    })
    Box(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize()
    ) {
        Text(
            text = "拖动", modifier = Modifier
                .draggable(
                    draggableState, Orientation.Vertical,
                    onDragStarted = { offset ->
                        Log.d(TAG, "DraggableGestureScreen start offset: $offset")
                    },
                    onDragStopped = { offset ->
                        Log.d(TAG, "DraggableGestureScreen end dragDistance: $offset")
                    }
                )
                .offset(y = dragDistance.dp)
        )
    }
}

fun Modifier.draggable(
    state: DraggableState,
    orientation: Orientation,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource? = null,
    startDragImmediately: Boolean = false,
    onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {},
    onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {},
    reverseDirection: Boolean = false
)

draggable()方法参数比较多,这里列举几个比较重要的参数做下解释:

  • state:用于监听指定方向上手指拖动的偏移量;
  • orientation:指定水平或者垂直方向;
  • onDragStarted:拖动开始的回调,是一个挂起函数,内部的startedPosition表示手指落下的位置;
  • onDragStopped:拖动停止的回调,也是一个挂起函数,内部的velocity表示停止时拖动的速度;
  • reverseDirection:表示是否反转拖动反向,默认为false。

示例代码中监听垂直方向的拖动事件,并且将文本组件随着拖动做偏移动作,这里采用的是设置Modifier.offset(y = dragDistance)来偏移文本组件在y轴的坐标,下面是滑动的具体效果:

通过Modifier.draggable()方法只能监听单一方向上的拖动事件,除此之外还可以Modifier.pointInput()来同时监听水平和垂直方向上的拖动事件,这个后续会单独介绍,感兴趣的小伙伴可以提前去了解下🙂。

滑动事件

Compose中滑动事件可以通过Modifier.swipeable()方法进行监听,但是swipeable目前之后material包下才有,material3包下面还未提供。

滑动事件和拖动事件极为类似,都是在指定的方向上随着手指的移动而响应的事件,只是滑动事件比拖动事件多了个锚点的概念,它可以根据锚点的状态来设置阈值,事件会在对应的阈值范围内进行额外的滑动事件,比如说我们设置一个Box从上滑到下,并且阈值为0.3,当手指从上滑到下总长的70%时停止,那么剩下的30%距离也会进行滑动事件。

下面我们来看具体代码实现:

scss 复制代码
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SwipGestureScreen() {

    val swipeableState = rememberSwipeableState(initialValue = 0)
    val boxSize = with(LocalDensity.current) { 50.dp.toPx() }
    val anchorMap = mapOf(0F to 0, boxSize to 1)

    Box(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize()
    ) {
        Box(
            modifier = Modifier
                .size(50.dp, 100.dp)
                .background(color = Color.Blue)
        ) {
            Box(
                modifier = Modifier
                    .offset { IntOffset(0, swipeableState.offset.value.toInt()) }
                    .swipeable(
                        state = swipeableState,
                        anchors = anchorMap,
                        orientation = Orientation.Vertical,
                        thresholds = { from, to ->
                            Log.d("SwipGestureScreen", "SwipGestureScreen from: $from, to: $to")
                            if (from == 1) {
                                FractionalThreshold(0.2F)
                            } else {
                                FractionalThreshold(0.4F)
                            }
                        }
                    )
                    .size(50.dp)
                    .background(color = Color.Red)
            )
        }
    }
}

swipeableState是记录滑动事件的状态,可以从此state中获取滑动的偏移量;

anchorMap是一个锚点集合,当偏移量为0时,锚点值设置为0,当偏移量为Box大小时,此时锚点值设置为1,后续可以根据锚点值进行对应的阈值设置;

Modifier.offset { IntOffset(0, swipeableState.offset.value.toInt()) }设置最小的Box随之滑动进行Y轴偏移;

Modifier.swipeable()方法有四个比较重要的参数,分别作用如下:

  • state:用于监听滑动事件的状态;
  • anchors:配置滑动事件的锚点集合,也就是我们上面提到的anchorMap;
  • orientation:配置滑动事件的方向,此处设置为垂直方向;
  • thresholds:根据锚点值设置阈值,当从上到下滑动时,阈值设置为0.4F,当从下到上滑动时,阈值设置为0.2F。

下面来看下具体实现的效果:

从上面的录屏中可以清晰的看出,当手指从上到下滑动是,触发到0.4F阈值时,此时松开手指,红色的方块依旧会往下滑动;当从下往上滑动是只需要触发0.2F阈值时,手指松开方块也会继续往上滑动,这就是swipe比drag更美妙的地方。

我们可以通过swipe实现滑动关闭、折叠标题栏的各种炫酷的效果,小伙伴们赶紧去试试吧~

关于我

我是Taonce,如果觉得本文对你有所帮助,帮忙关注、赞或者收藏三连一下,谢谢😆😆~

相关推荐
m0_748235953 小时前
CentOS 7使用RPM安装MySQL
android·mysql·centos
ac-er88887 小时前
Yii框架中的队列:如何实现异步操作
android·开发语言·php
流氓也是种气质 _Cookie9 小时前
uniapp 在线更新应用
android·uniapp
zhangphil10 小时前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲11 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥12 小时前
python操作mysql
android·python
Couvrir洪荒猛兽13 小时前
Android实训十 数据存储和访问
android
五味香15 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录15 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽17 小时前
Android实训九 数据存储和访问
android