首页
学习
活动
专区
圈层
工具
发布

使用简单代码在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 之上。

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

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

触控检测

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

代码语言:javascript
复制
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 修饰符以检测触摸操作。

代码语言:javascript
复制
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 以存储所有坐标。

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

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

代码语言:javascript
复制
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、绘图即被触发,具体如下所示。

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

完整代码

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

代码语言:javascript
复制
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

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/W9tdH01cJQYrrGuSC8Jy
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券