Android Compose点击事件“黑洞”

背景

项目中有几个Composable Button,产品要求点击置灰状态的FollowStatusButton按钮时,弹一个toast。

意外翻车

作为View体系过来人,这个问题一看就很简单,按钮置灰状态时,为其父布局设置点击事件即可,demo代码如下:

kotlin 复制代码
@Composable
@Preview
fun ButtonDemo(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    Box(
        modifier = Modifier
            .background(color = Color.Yellow)
            .clickable {
                Toast.makeText(context, "Box clicked!", Toast.LENGTH_SHORT).show()
            }
            .padding(30.dp),
    ) {
        Button(
            onClick = {
                 Toast.makeText(context, "Button clicked!", Toast.LENGTH_SHORT).show()
            },
            enabled = false,
        ) {
            Text(text = "Disabled Button")
        }
    }
}

只有按钮外面的黄色区域才能响应点击事件,按钮区域的点击事件被吃掉了,没传递到父布局,这和View的事件传递默认行为是不一样的!

寻找原因

经过一番查找,有人提出了相同的问题issuetracker.google.com/issues/2397...

Compose就是这样设计的,当Button enable=false时, 这块区域就像黑洞一样,点击事件都会被吞掉。

探索解决方案

那么我们就只能另外想办法了,我们可以修改FollowStatusButton内部实现,

  1. 将内部Button始终设置为enable=true
  2. 根据条件提供不同状态的色值、click listener

这样能满足条件,但它侵入原有FollowStatusButton代码,如果其他组件也要类似处理,我们需要对每个组件进行改造,这将极大地增加我们的工作量。

有没有一种方案能像View事件传递那样,不侵入原有组件代码,也能实现类似效果? 我们可以将组件放在Box里面,再添加一个和FollowStatusButton同等大小 可点击 的组件,类似的demo代码如下:

kotlin 复制代码
@Composable
@Preview
fun ButtonDemo(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    val contentClickEnabled = false
    ClickableLayer(
        contentClickEnabled = contentClickEnabled,
        onDisabledContentClick = {
            Toast.makeText(context, "ClickableLayer clicked!", Toast.LENGTH_SHORT).show()
        },
        content = {
            // 包装任意现有组件,调用方式不变
            Button(
                onClick = {
                    Toast.makeText(context, "Button clicked!", Toast.LENGTH_SHORT).show()
                },
                enabled = contentClickEnabled,
            ) {
                Text(text = "Disabled Button")
            }
        }
    )
}

@Composable
fun ClickableLayer(
    modifier: Modifier = Modifier,
    contentClickEnabled: Boolean,
    onDisabledContentClick: () -> Unit,
    content: @Composable () -> Unit,
) {
    Box(modifier = modifier) {
        content()
        if (!contentClickEnabled) {
            Box(
                modifier = Modifier
                    .matchParentSize() // 构建与content等宽高的点击区域
                    .clickable(
                        // 去掉默认的按压效果
                        indication = null, 
                        interactionSource = remember {
                            MutableInteractionSource()
                        },
                        onClick = onDisabledContentClick,
                    ),
            )
        }
    }
}

至此,遇到类似场景就可以用ClickableLayer来包裹组件了,我们无需关心和修改组件内部逻辑,降低了心智负担。

总结

本文从需求出发,基于已有开发经验开始构建代码,发现Compose点击事件与View体系的差异:组件在enable=false时,导致其父组件也无法响应点击事件。通过探索给出了相对优雅的解决方案,运用matchParentSize构建了一个等宽高的点击区域,封装成ClickableLayer简化调用。

相关推荐
锋风Fengfeng38 分钟前
安卓15预置第三方apk时签名报错问题解决
android
User_undefined1 小时前
uniapp Native.js原生arr插件服务发送广播到uniapp页面中
android·javascript·uni-app
程序员厉飞雨2 小时前
Android R8 耗时优化
android·java·前端
丘狸尾4 小时前
[cisco 模拟器] ftp服务器配置
android·运维·服务器
van叶~6 小时前
探索未来编程:仓颉语言的优雅设计与无限可能
android·java·数据库·仓颉
Crossoads9 小时前
【汇编语言】端口 —— 「从端口到时间:一文了解CMOS RAM与汇编指令的交汇」
android·java·汇编·深度学习·网络协议·机器学习·汇编语言
li_liuliu11 小时前
Android4.4 在系统中添加自己的System Service
android
C4rpeDime13 小时前
自建MD5解密平台-续
android
鲤籽鲲14 小时前
C# Random 随机数 全面解析
android·java·c#
m0_5485147718 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php