当前位置: 首页 > article >正文

Compose Indication:点击效果设置

Compose Indication:打造独特点击效果的秘密武器

在Compose开发中,大家可能都碰到过Indication,不少人第一次接触它,是在想去掉Material默认的点击水波纹效果的时候。要是在AI工具里搜“怎么去掉水波纹效果”,会得到这样一段代码:

Box(
        modifier = Modifier
           .size(200.dp)
           .clickable(
                // 去除指示效果
                indication = null, 
                interactionSource = null
            ) {
                // 点击事件处理逻辑
            },
        contentAlignment = Alignment.Center
    ) {
        Text(text = "No Ripple Click", color = Color.Black)
    }

indication参数设成null,水波纹效果就没了。这背后是怎么实现的呢?先看看Indication的注释。Indication代表在一些交互发生时出现的视觉效果,像组件被按下时的涟漪效果,或者被聚焦时的高亮显示。想自定义Indication,可以参考IndicationNodeFactory,它能实现更高效的指示效果。Indication一般通过LocalIndication在整个层级结构中提供,开发者可以自定义LocalIndication,改变组件(比如clickable)的默认指示效果。从这里能看出,Indication就是用来实现点击、聚焦、拖动等组件效果的,有点像传统View系统里的selector,但它的功能可要强大得多。

在传统View系统里,selector能根据组件的不同状态,指定不同的资源或颜色。不过,要实现交互效果,得知道组件的当前状态。而Indication本身没办法获取组件的交互状态,这时就需要Interaction来帮忙了。

在很多情况下,开发普通组件时,我们不用关心Compose组件是怎么解读用户操作的。比如创建一个Button,通过Modifier.clickable就能判断用户有没有点击,设置好onClick代码就行,不用管是点击屏幕还是用键盘操作。但要是想自定义组件对用户行为的响应方式,Interaction就派上用场了。

当用户和界面组件交互时,系统会生成很多Interaction事件。比如用户点击按钮,按钮会生成PressInteraction.Press;在按钮范围内松开手指,会生成PressInteraction.Release,表示点击完成;要是手指拖出按钮范围再松开,就会生成PressInteraction.Cancel,代表点击取消。这些互动事件没有预设的含义,也不解读操作顺序和优先级。

如果想跟踪互动来扩展组件功能,比如让按钮按下时变色,最简单的办法就是观察互动状态。InteractionSource提供了很多方法来获取各种互动状态,像调用InteractionSource.collectIsPressedAsState(),就能知道按钮有没有被按下:

val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()

Button(
    onClick = { /* do something */ },
    interactionSource = interactionSource
) {
    Text(if (isPressed) "Pressed!" else "Not pressed")
}

除了collectIsPressedAsState(),Compose还提供了collectIsFocusedAsState()collectIsDraggedAsState()collectIsHoveredAsState(),这些都是基于InteractionSource低级API的便捷方法,不过在某些场景下,直接用低级函数会更好。

了解完Interaction,再回到Indication。下面讲讲怎么用Indication创建和应用可复用的自定义效果。

IndicationNodeFactory是用来创建Modifier.Node实例的工厂,这些实例可以是有状态或无状态的,能从CompositionLocal检索值,和其他Modifier.Node一样。Modifier.indication是个修饰符,用于绘制Indication组件。Modifier.clickable这类高级互动修饰符能直接接受指示参数,既能发出Interaction,又能绘制视觉效果,所以简单场景下,用Modifier.clickable就行,不一定非要Modifier.indication

来看个例子,把点击缩放效果用Indication实现,步骤如下:

  1. 创建负责应用缩放效果的Modifier.Node。这个节点要观察互动来源,和之前的示例类似,但它会直接启动动画,而不是把互动转成状态。节点需要实现DrawModifierNode,重写ContentDrawScope#draw(),用Compose的图形API渲染缩放效果,调用drawContent()绘制应用Indication的组件,注意一定要调用,不然组件不会显示。
private class ScaleNode(private val interactionSource: InteractionSource) :
    Modifier.Node(), DrawModifierNode {

    var currentPressPosition: Offset = Offset.Zero
    val animatedScalePercent = Animatable(1f)

    private suspend fun animateToPressed(pressPosition: Offset) {
        currentPressPosition = pressPosition
        animatedScalePercent.animateTo(0.9f, spring())
    }

    private suspend fun animateToResting() {
        animatedScalePercent.animateTo(1f, spring())
    }

    override fun onAttach() {
        coroutineScope.launch {
            interactionSource.interactions.collectLatest { interaction ->
                when (interaction) {
                    is PressInteraction.Press -> animateToPressed(interaction.pressPosition)
                    is PressInteraction.Release -> animateToResting()
                    is PressInteraction.Cancel -> animateToResting()
                }
            }
        }
    }

    override fun ContentDrawScope.draw() {
        scale(
            scale = animatedScalePercent.value,
            pivot = currentPressPosition
        ) {
            this@draw.drawContent()
        }
    }
}
  1. 创建IndicationNodeFactory,它的任务就是创建新节点实例。如果没有配置参数,工厂可以是个对象:
object ScaleIndication : IndicationNodeFactory {
    override fun create(interactionSource: InteractionSource): DelegatableNode {
        return ScaleNode(interactionSource)
    }

    override fun equals(other: Any?): Boolean = other === ScaleIndication
    override fun hashCode() = 100
}
  1. Modifier.clickable内部用了Modifier.indication,要让组件带有ScaleIndication的点击效果,直接把Indication作为clickable的参数就行:
Box(
    modifier = Modifier
       .size(100.dp)
       .clickable(
            onClick = {},
            indication = ScaleIndication,
            interactionSource = null
        )
       .background(Color.Blue),
    contentAlignment = Alignment.Center
) {
    Text("Hello!", color = Color.White)
}

这样就实现了一个按住缩放的交互效果,这个Indication可以用在任何Composable函数上。Indication能定义一套交互效果并应用到各种组件上,如果项目里有标准的交互效果设计,用Indication准没错。欢迎大家一起交流,有问题可以在评论区留言或者私信我!


http://www.kler.cn/a/593967.html

相关文章:

  • 碰一碰发视频后端源码技术,支持OEM
  • Python绘图技巧,主流绘图库
  • UI设计中的信息架构:组织内容的艺术
  • java项目之基于ssm的疫苗预约系统(源码+文档)
  • 【面试场景题-你知道readTimeOutException,会引发oom异常吗】
  • 详解加实操C++之分配器
  • 【QT】系统事件入门 -- 文件 QFile基础和示例
  • 介绍一下TiDB、RocksDb、levelDB、LSM 树、SSTable。
  • ai数字人系统功能详细代码
  • 算法|2025最强优化算法
  • 现代复古像素风品牌海报游戏排版设计装饰英文字体 Psygen — Modern Pixel Font
  • Java 中 CopyOnWriteArrayList 的底层数据结构及相关分析
  • 力扣刷题——25.K个一组翻转链表
  • 深拷贝在 JavaScript 中的几种实现方式对比
  • xss-labs靶场训练
  • 调和Django与Sql server2019的关系
  • 【leetcode hot 100 208】实现Trie(前缀树)
  • HAl库开发中断方式接收Can报文的详细流程
  • 使用AI一步一步实现若依前端(15)
  • 体检管理页面开发:问题总结与思考