Compose 手势处理,增进交互体验

Compose 手势处理,增进交互体验

  • 概述
  • 常用手势处理Modifier
    • clickable()
    • combinedClickable()
    • draggable()
    • swipeable()
    • transformable()
    • scrollable()
    • nestedScroll
      • NestedScrollConnection
      • NestedScrollDispatcher
  • 定制手势处理
    • 使用 PointerInput Modifier
      • PointerInputScope
    • awaitPointerEventScope

概述

在处理手势时,应将手势处理修饰符尽可能放到 Modifier 末尾,从而可以避免产生不可预期的行为。

常用手势处理Modifier

clickable()

监听点击事件,在绝大多数情况下,只需要出入 onClick 回调即可。当然也可以将 enable 参数设置为一个可变状态,通过状态来动态控制启用点击监听。

@Composable
private fun GestureOfClick(){
    var colorState by remember { mutableStateOf(false) }
    Box(modifier = Modifier
        .size(60.dp)
        .background(color = if (colorState) Color.LightGray else Color.Gray)
        .clickable { colorState = !colorState },
        contentAlignment = Alignment.Center
    ){
        Text(text = "点击")
    }
}

combinedClickable()

和 clickable() 类似,但支持长按/双击/单击:

// Clickable.kt
fun Modifier.combinedClickable(
  enabled: Boolean = true,
  onClickLabel: String? = null,
  role: Role? = null,
  onLongClickLabel: String? = null,
  onLongClick: (() -> Unit)? = null,
  onDoubleClick: (() -> Unit)? = null,
  onClick: () -> Unit
)

draggable()

只支持检测单一方向的拖动(水平方向或垂直方向),不支持同时监听两个方向上的拖动偏移,要实现这种效果,需要使用更底层的 PointerInputModifier。

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//是否反转方向。
): Modifier

swipeable()

可以拖动元素,释放后,这些元素通常朝一个方向定义的两个或多个锚点继续滑动以呈现动画效果。其常见用途是实现“滑动关闭”模式。

必传的参数有:

  • state:是一个SwipeableState,可以记录当前的偏移数据
  • anchors:锚点,用来记录不同滑动数据对应的状态
  • orientation:滑动方向
  • thresholds:不同锚点之间的临界值

transformable()

多点触控,在日常生活当中,多点触控这样的操作多数是在浏览图片,网页或者地图之类的场景下被用到
transformable有三个参数:

  • state:TransformableState,用来获取多点触控时候目标组件大小,位移,旋转角度变化情况的
  • lockRotationOnZoomPan:Boolean,这个参数的意思是如果设置为false,那么多点触控的时候将会同时监听双指拖动,缩放以及旋转,但是如果设置为true的时候,除非旋转动作比其余两个动作先执行,这样会被监听到,不然的话,只会监听双指拖动和缩放动作,旋转事件将不会被监听
  • enabled:Boolean,是否可用
@Composable
private 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()
    )
}

scrollable()

虽然 verticalScroll() / horizontalScroll() 和 scrollable() 的名字很像,但它们并不是相同的东西,scrollable() 修饰符仅负责检测滚动手势,并不会帮我们自动偏移元素内容,滚动行为由开发者定义,用法类似 draggable() 修饰符

var offsetX by remember { mutableFloatStateOf(0f) }
Column {
  Text(text = "OffsetX: $offsetX")
  Box(
    Modifier
    .size(200.dp)
    .background(Pink)
    .scrollable(
      // 检测水平方向的滚动手势
      orientation = Orientation.Horizontal,
      // 使用 rememberScrollableState 创建并传递一个 ScrollableState 对象。
      // 通过 ScrollableState 可以获取到滚动手势的偏移量,进一步定义滚动行为。
      state = rememberScrollableState { delta ->
        offsetX += delta
        delta // 为了支持嵌套滚动,必须返回消费的滚动距离量
      }
    )
  )
}

nestedScroll

嵌套滑动,需要传递两个参数,connection: NestedScrollConnection 和 dispatcher: NestedScrollDispatcher,源码如下:

fun Modifier.nestedScroll(
    connection: NestedScrollConnection,
    dispatcher: NestedScrollDispatcher? = null
){...}
  1. connection:包含了嵌套滑动的和姓逻辑,通过回调可以在子布局获得滑动事件前,预先消费掉部分或全部手势偏移量,当然也可以获取子布局消费后剩下的手势偏移量。
  2. dispatcher:包含用于父布局的NestedScrollConnection,可以使用包含的 dispatch**系列方法动态控制组件完成滑动。

NestedScrollConnection

interface NestedScrollConnection {
	fun onPreScroll(available: Offset, source: NestedScrollSource): Offset = Offset.Zero
	fun onPostScroll(consumed: Offset,available: Offset,source: NestedScrollSource): Offset = Offset.Zero
	suspend fun onPreFling(available: Velocity): Velocity = Velocity.Zero
	suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {return Velocity.Zero}
}
  • onPreScroll:在子控件滑动之前,会先使用NestedScrollDispatcher询问父控件是否需要消费available的偏移量,父控件可以在该方法内计算自身需要消费的量,然后返回自身消费了的偏移量。
  • onPostScroll:在子控件滑动之后,会使用NestedScrollDispatcher通知父控件,告知其consumed的偏移量以及剩余available的偏移量,而父控件则可以根据情况判断是否还要再偏移,以及使用和子控件同等的偏移还是剩余的偏移。完成之后返回自身消费了的偏移量
  • onPreFling:在子控件进行惯性滑行之前,会先使用NestedScrollDispatcher询问父控件是否需要消费available的速度值,父控件可以在该方法内计算自身需要消费的量,然后返回自身消费了的速度值
  • onPostFling:在子控件进行惯性滑行之后,会使用NestedScrollDispatcher通知父控件,告知其consumed的速度值以及剩余available的速度值,而父控件则可以根据情况判断是否还要再偏移,以及使用和子控件同等的速度还是剩余的速度。完成之后返回自身消费了的速度值。

一句话概括:在滑动前父控件可以通过onPreScroll回调先消费部分或全部偏移量;待子控件消费完后,父控件依旧可以通过onPostScroll方法进行消费,区别在于在onPreScroll回调中,父控件是优先消费的,而onPostScroll则是子控件优先消费,fling的两个方法同理。

NestedScrollDispatcher

class NestedScrollDispatcher {
	// ....
	// 在滑动之前调用,将可用的偏移量传递给父控件
    fun dispatchPreScroll(available: Offset, source: NestedScrollSource): Offset {
        return parent?.onPreScroll(available, source) ?: Offset.Zero
    }
    //在滑动之后调用,将已经消费的偏移量以及剩余可用的偏移量传递给父控件
    fun dispatchPostScroll(consumed: Offset,available: Offset,source: NestedScrollSource): Offset {
        return parent?.onPostScroll(consumed, available, source) ?: Offset.Zero
    }
    //在滑动之后,如果产生了惯性滑动,那么需要将相应的速度值传递给父控件
    suspend fun dispatchPreFling(available: Velocity): Velocity {
       return parent?.onPreFling(available) ?: Velocity.Zero
    }
    //在惯性滑动之后,需要将已经消费了的速度值以及剩余可用的速度值传递给父控件
    suspend fun dispatchPostFling(consumed: Velocity, available: Velocity): Velocity {
        return parent?.onPostFling(consumed, available) ?: Velocity.Zero
    }

一句话概括:通过dispatchPreScroll方法询问父控件是否需要消费,方法返回的就是父控件消费了的量,然后就可以做自己的滑动操作了,在滑动完成之后,再通过dispatchPostScroll方法通知父控件。

定制手势处理

前面提到的手势处理修饰符都是基于低级别的 pointerInput 修饰符进行封装实现的。

使用 PointerInput Modifier

// SuspendingPointerInputFilter.kt
fun Modifier.pointerInput(
  key1: Any?, 
  block: suspend PointerInputScope.() -> Unit
): Modifier
  • keys:当 Composable 发生重组时,如果传入的 keys 发生了变化,则手势事件处理过程会被中断
  • block:在这个 PointerInputScope 作用域代码块中,变可以声明手势事件的处理逻辑了,发生在协程中。

PointerInputScope

  • detectTapGestures():设置更细粒度的点击监听回调
  • detectDragGestures():设置更细粒度的拖动手势监听回调
  • detectTransformGestures():双指拖动、缩放与旋转手势操作中更具体的手势信息
  • detectDragGesturesAfterLongPress():监听长按后的拖动手势
  • detectHorizontalDragGestures():监听水平拖动手势
  • detectVerticalDragGestures():监听垂直拖动手势
  • forEachGesture:允许用户可以对每一个手势事件序列进行相同的定制处理

awaitPointerEventScope

AwaitPointerEventScope 作用域中,可以使用 Compose 中所有低级别的手势处理挂起方法。

suspend fun <R> awaitPointerEventScope(
    block: suspend AwaitPointerEventScope.() -> R
): R
API名称作用
awaitPointerEvent手势事件
awaitFirstDown第一根手指的按下事件
drag拖动事件
horizontalDrag水平拖动事件
verticalDrag垂直拖动事件
awaitDragOrCancellation单次拖动事件
awaitHorizontalDragOrCancellation单次水平拖动事件
awaitVerticalDragOrCancellation单次垂直拖动事件
awaitTouchSlopOrCancellation有效拖动事件
awaitHorizontalTouchSlopOrCancellation有效水平拖动事件
awaitVerticalTouchSlopOrCancellation有效垂直拖动事件

参考资料:巧用Compose来实现手势拖拽效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/978218.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

llaMa模型的创新

LLaMa介绍 LLaMa是基于transformer encoder的生成式模型。 目前有&#xff1a;LLAMA, LLAMA2, LLAMA3 三个大的版本 论文 LLAMA 2: Open Foundation and Fine-Tuned Chat Models&#xff1a; https://arxiv.org/pdf/2307.09288 LLAMA 3: The Llama 3 Herd of Models https…

神经网络 - 激活函数(Sigmoid 型函数)

激活函数在神经元中非常重要的。为了增强网络的表示能力和学习能力&#xff0c;激活函数需要具备以下几点性质: (1) 连续并可导(允许少数点上不可导)的非线性函数。可导的激活函数可以直接利用数值优化的方法来学习网络参数. (2) 激活函数及其导函数要尽可能的简单&#xff0…

PINN求解固体力学问题——论文加代码

PINN求解固体力学问题——论文加代码 1. 训练2. 可视化论文:Physics-Informed Deep Learning and its Application in Computational Solid and Fluid Mechanics 基本问题: 网格: 1. 训练 # %load Plane_Stress_W-PINNs.py """ Forward Problem for Plan…

ktransformers 上的 DeepSeek-R1 671B open-webui

ktransformers 上的 DeepSeek-R1 671B open-webui 一、下载GGUF模型1. 创建目录2. 魔塔下载 DeepSeek-R1-Q4_K_M3. 安装显卡驱动和cuda4. 显卡 NVIDIA GeForce RTX 4090 二、安装ktransformers1. 安装依赖2. 安装uv工具链3. 下载源码4. 创建python虚拟环境 三、编译ktransforme…

QT:Graphics View的坐标系介绍

在 Qt 的 Graphics View 框架中&#xff0c;存在三种不同的坐标系&#xff0c;分别是 物品坐标系&#xff08;Item Coordinates&#xff09;、场景坐标系&#xff08;Scene Coordinates&#xff09; 和 视图坐标系&#xff08;View Coordinates&#xff09;。这三种坐标系在图形…

医院HIS接入大模型:算力基础设施与训练能力的深度剖析与测算

一、引言 1.1 研究背景与意义 在数字化医疗快速发展的当下,医院信息系统(Hospital Information System,HIS)作为医疗信息化的核心枢纽,承载着患者诊疗信息、医院运营管理等关键数据 ,对提升医疗服务质量、优化医院管理流程起着至关重要的作用。然而,传统 HIS 在面对日…

入门网络安全工程师要学习哪些内容【2025年寒假最新学习计划】

&#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 大家都知道网络安全行业很火&#xff0c;这个行业因为国家政策趋势正在大力发展&#xff0c;大有可为!但很多人对网络安全工程师还是不了解&#xff0c;不知道网…

阿里云CDN转https个人测试证书过期更换

网站是http的虚拟主机&#xff0c;微信小程序要求https&#xff0c;所以申请了阿里云CDN和个人测试证书&#xff08;以前叫免费证书&#xff09;&#xff0c;把http转成https。 但是个人测试证书只有三个月有效期&#xff0c;所以每隔三个月要手动申请更换一次。 在阿里云CDN…

东信营销科技巨额补贴仍由盈转亏:毛利率大幅下滑,现金流告急

《港湾商业观察》施子夫 近期&#xff0c;东信营销科技有限公司&#xff08;以下简称&#xff0c;东信营销科技&#xff09;递表港交所&#xff0c;联席保荐机构为海通国际和中银国际。 东信营销科技的国内运营主体为深圳市东信时代信息技术有限公司。尽管期内收入规模有所提…

AOP进阶-04.切入点表达式-@annotation

一.annotation注解 我们在最后一个切入点表达式中要匹配多个无规则的方法&#xff0c;这样的写法有些冗余了。而annotation注解就是来解决这一问题的。 annotation注解使用特定的注解来匹配方法。我们首先自定义一个注解&#xff0c;该注解就相当于一个标签&#xff0c;目标对…

特斯拉 FSD 算法深度剖析:软件层面全解读

一、引言 特斯拉的 FSD&#xff08;Full Self-Driving&#xff09;系统作为自动驾驶领域的前沿成果&#xff0c;其软件层面的算法设计至关重要。本文将从软件的角度&#xff0c;深入探讨特斯拉 FSD 所采用的算法&#xff0c;包括感知、规划、控制等多个方面&#xff0c;以期为…

LabVIEW同步数据采集功能

VI通过使用数据采集&#xff08;DAQ&#xff09;硬件系统&#xff0c;进行多通道同步采集&#xff0c;实时获取模拟信号数据。它利用外部时钟信号触发数据采集&#xff0c;支持连续采样模式&#xff0c;并将采集到的数据实时显示在波形图上&#xff0c;方便用户进行数据监控和分…

YOLOv12 ——基于卷积神经网络的快速推理速度与注意力机制带来的增强性能结合

概述 实时目标检测对于许多实际应用来说已经变得至关重要&#xff0c;而Ultralytics公司开发的YOLO&#xff08;You Only Look Once&#xff0c;只看一次&#xff09;系列一直是最先进的模型系列&#xff0c;在速度和准确性之间提供了稳健的平衡。注意力机制的低效阻碍了它们在…

Ubuntu20.04之VNC的安装使用与常见问题

Ubuntu20.04之VNC的安装与使用 安装图形桌面选择安装gnome桌面选择安装xface桌面 VNC-Server安装配置开机自启 VNC Clientroot用户无法登入问题临时方案永久方案 安装图形桌面 Ubuntu20.04主流的图形桌面有gnome和xface两种&#xff0c;两种桌面的安装方式我都会写&#xff0c…

14.二叉搜索树

二叉搜索树 1.概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树: *若它的左⼦树不为空&#xff0c;则左⼦树上所有结点的值都⼩于等于根结点的值 *若它的右⼦树不为空&#xff0c;则右⼦树上所有结点的值都⼤于等于根结点…

web网络安全---cookie篇

什么是Cookie 由于HTTP是一种无状态的协议&#xff0c;服务器单从网络连接上无从知道客户身份。怎么办呢&#xff1f;就给客户端们颁发一个通行证吧&#xff0c;每人一个&#xff0c;无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的…

Qt 开源音视频框架模块之QtAV播放器实践

Qt 开源音视频框架模块QtAV播放器实践 1 摘要 QtAV是一个基于Qt的多媒体框架&#xff0c;旨在简化音视频播放和处理。它是一个跨平台的库&#xff0c;支持多种音视频格式&#xff0c;并提供了一个简单易用的API来集成音视频功能。QtAV的设计目标是为Qt应用程序提供强大的音视…

WPF学习之Prism(二)

前言 学习一下Prism。 1.Prism Prism框架提供了一套丰富的工具、类和模块&#xff0c;帮助开发人员实现以下功能&#xff1a; 模块化&#xff1a;Prism框架支持将应用程序拆分为多个模块&#xff0c;每个模块具有自己的功能和视图。这种模块化的设计使得应用程序更加灵活和…

前端实现rsa加密功能

本文将从web和小程序两个端来实现rsa的加密功能。 一般项目的登录密码、身份证号以及一些用户敏感信息等在传输的时候需要使用加密传输&#xff0c;一般来说&#xff0c;前端只会得到后端给的公钥&#xff0c;而rsa加密&#xff0c;可以用公钥加密&#xff0c;也可以用私钥加密…

VidSketch:具有扩散控制的手绘草图驱动视频生成

浙大提出的VidSketch是第一个能够仅通过任意数量的手绘草图和简单的文本提示来生成高质量视频动画的应用程序。该方法训练是在单个 RTX4090 GPU 上进行的&#xff0c;针对每个动作类别使用一个小型、高质量的数据集。VidSketch方法使所有用户都能使用简洁的文本提示和直观的手绘…