GMTC全球大前端技术大会(北京站)门票9折特惠截至本周五,点击立减¥480 了解详情
写点什么

使用简单代码在 Android Jetpack Compose 中开发绘图应用

2020 年 12 月 02 日

使用简单代码在Android Jetpack Compose中开发绘图应用

使用 Jetpack Compose 触控功能在 Canvas 上画出图形。


如果大家有意学习 Android,不妨先从妙趣横生的绘图应用起步。在今天的文章中,我们将共同了解如何使用最新 Android Jetpack Compose 开发一款绘图应用。


设置 Jetpack Compose 的先决条件


目前 Jetpack Compose 仍处于 Alpha 测试阶段,因此大家必须下载 Android Studio 4.2(Canary 版)并完成以下设置才能使用。


在 Jetpack Compose 中绘图


绘图应用的开发流程非常简单,只需要三步:


  1. Canvas 绘图画布

  2. 触控检测(按压与触控移动)

  3. 根据触控检测绘制路径


设置 Canvas


与传统 Android 开发有所不同,这一次我们不再使用布局。因此,我们不需要构建自定义视图并将其绘制到 Canvas 之上。


override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContent {         Canvas(modifier = Modifier.fillMaxSize()) {            // Drawing happens here        }    }}
复制代码


在这里,我们只需要通过 fillMaxSize() 设置 Modifier,确保其占用应用程序中的整个空间。


触控检测


要检测触控,我们通常需要在 Android 中的自定义视图内覆盖 onTouchEvent 函数。


override fun onTouchEvent(event: MotionEvent?): Boolean {    when (event?.action) {        MotionEvent.ACTION_DOWN -> { }        MotionEvent.ACTION_MOVE -> { }        MotionEvent.ACTION_UP -> { }        else -> return false    }    invalidate()    return true}
复制代码


在 Jetpack Compose 当中,我们使用 pointerInteropFilter 修饰符以检测触摸操作。


Canvas(modifier = Modifier        .fillMaxSize()        .pointerInteropFilter {            when (it.action) {                MotionEvent.ACTION_DOWN -> { }                MotionEvent.ACTION_MOVE -> { }                MotionEvent.ACTION_UP -> { }                else -> false            }            true        })
复制代码


从以上代码来看,二者其实非常相似,唯一的区别在于后者不再需要 invalidate。Jetpack Compose 会通过更改部分状态值通知所需图形。


下面,我们具体聊聊传统 Android 与 Jetpack Compose 之间的工作方式差异。


基于触控的路径绘制


如下图所示,检测触控与绘制的位置有所不同。



因此,为了触发绘图,我们需要使用 mutableState 值,其行为类似于传统开发中的 invalidate。此外,我们还需要 path 以存储所有坐标。


private val action: MutableState<Any?> = mutableStateOf(null)private val path = Path()
复制代码


接下来,在检测到图形之后,我们可以更新 action 与 path,具体如下所示。


when (it.action) {    MotionEvent.ACTION_DOWN -> {        action.value = it        path.moveTo(it.x, it.y)    }    MotionEvent.ACTION_MOVE -> {        action.value = it        path.lineTo(it.x, it.y)    }    else -> false}
复制代码


在 action 更新完成之后,只要能够访问 action、绘图即被触发,具体如下所示。


{    action.value?.let {        drawPath(                path = path,                color = Color.Green,                alpha = 1f,                style = Stroke(10f))    }}
复制代码


完整代码


没错,只需要不到 50 行代码,我们就拥有了一款由 Jetpack Compose 开发而成的 Android 绘图应用。


private val action: MutableState<Any?> = mutableStateOf(null)private val path = Path()override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContent {        Canvas(modifier = Modifier                .fillMaxSize()                .pointerInteropFilter {                    when (it.action) {                        MotionEvent.ACTION_DOWN -> {                            action.value = it                            path.moveTo(it.x, it.y)                        }                        MotionEvent.ACTION_MOVE -> {                            action.value = it                            path.lineTo(it.x, it.y)                        }                        else -> false                    }                    true                }        ) {            action.value?.let {                drawPath(                        path = path,                        color = Color.Green,                        alpha = 1f,                        style = Stroke(10f))            }        }    }}
复制代码


警告


如果您是一位经验丰富的开发者,也许会好奇我们能否直接在绘图函数内设置 path 坐标,由此代替通过 action 发送该坐标。相应代码如下所示:



没问题,这在技术上完全可行。


但根据我的经验,一旦触发后续 action,则某些 action 更新有可能无法正确被发送至绘图函数(特别是在 ACTION_DOWN 之后由 ACTION_MOVE 触发 action 的情况下,此时由 ACTION_DOWN 发出的 action 将会丢失)。


不知道这是功能层面的限制,还是受到 alpha 版本的影响,具体情况仍然有待观察。


因此,为了实现正确的操作效果并获取完整路径信息,请在触控检测函数中设置 path 以避免丢失问题。


原文链接:


https://elye-project.medium.com/code-simple-android-jetpack-compose-drawing-app-886d1146ad20


2020 年 12 月 02 日 14:581259

评论

发布
暂无评论
发现更多内容

如何帮助技术员工高效成长?这几家企业的做法值得借鉴

极客时间企业版

研发管理 研发团队培训

寻找握剑的手,青睐懂行的人

脑极体

我想模糊删除redis key🤔

山中兰花草

Java lua redis 面试 批量任务

毕业半年的憨憨,将公司的代码上传到GitHub上了

诸葛小猿

GitHub 代码上传

【API进阶之路】老板给我涨薪30%!如何通过SDK接口搞定千万级流量直播

华为云开发者社区

运维 服务器 直播 云服务 华为云

以中立性的立场看Severless的目标和流派

韩超

云原生 serverles

可读代码编写炸鸡六 - 控制流尽量向前奔涌就好,不要分心

多选参数

代码 代码优化 代码规范 可读代码编写 可读代码

平价又好用的学习电脑小轩PRO来啦,为孩子创造超强学习体验

最新动态

防止数据重复提交的6种方法(超简单)!

王磊

Java

《北京市政务服务领域区块链应用创新蓝皮书(第一版)》正式发布

CECBC区块链专委会

如何进行需求梳理及埋点方案设计

易观大数据

第六周作业

Geek_a327d3

CAP原理

jason

Week 06 命题作业

Jeremy

数十家技术社区联名推荐的GeekOnline来了!

Geek_116789

解决问题 1474 个,Flink 1.11 究竟有哪些易用性上的改善?

Apache Flink

flink

区块链加持的家用摄像头能拯救你的隐私吗?

CECBC区块链专委会

LeetCode题解:141. 环形链表,JavaScript HashMap,详细注释

Lee Chen

LeetCode 前端进阶训练营

图解:如何实现最小生成树

淡蓝色

Java 数据结构 算法

Python的四种作用域及调用顺序

BigYoung

Python 局部作用域 全局作用域

单例模式的几种写法你用的哪种?

Java小咖秀

Java 设计模式 23种设计模式

聊聊Dubbo(二):简单入门

猿灯塔

Week 06学习总结

Jeremy

计算机揭秘之:网络分类和性能分析

程序那些事

TCP 计算机网络 网络协议 计算机基础 udp

可读代码编写炸鸡七 - 表达式太长就拆

多选参数

代码质量 代码组织 代码规范 可读代码编写 可读代码

林左鸣 史瑞华:人类应鼎力进行的探索

CECBC区块链专委会

30分钟学会应用正则表达式

墨灵

正则表达式 前端进阶训练营

朱嘉明:区块链成为经济转型、形成产业新业态的技术手段

CECBC区块链专委会

第六周总结

Geek_a327d3

MQTT的搭建、测试、应用及小程序的集成!

诸葛小猿

物联网 IoT mqtt broker

企业架构框架之TOGAF

Winfield

企业架构

使用简单代码在Android Jetpack Compose中开发绘图应用-InfoQ