鸿蒙-仓颉-使用仓颉语言开发鸿蒙应用-初窥门径

前言

之前写了一系列的仓颉语言教程,虽然它既能用于鸿蒙应用开发,也能用于服务端开发。但我作为一个移动端开发人员来讲,还是着重于仓颉语言在鸿蒙应用开发中的应用。
在这里,我们移植一个 Emacs 上简单的 55 小游戏。有的地方也称为开关灯、开关窗等。
具体规则如下:
在一个 5
5 的二维矩阵中,初始都为黑色,点击某一个格子,则它自己和它上下左右的格子都变成红色(如果已经是红色,则变为黑色)。继续点击不同的格子,直到所有的格子都变成红色。

比如初始都为黑色,以左上角为[0,0],右下角为[4,4]。当点击 [1,1]处的格子,则[1,0] [0,1] [1,1],[1,2],[2,1] 都由黑色变为红色。
示例 1
这时在点击[0,0], 则[0,0]由黑色变为红色。[0,1][1,0]则由红色变为黑色
 示例 2
我们的目的就是让格子都变为红色。
规则介绍完了,我们开始写代码

开始

最简单的方法就是使用布尔类型的二维数组来保存状态,点击时将它本身和上下左右的值取反就可以了。
布局可以搞两个 ForEach 循环渲染。

创建项目

在 windows 上使用DevEco Studio 6.0.0 Release版本创建仓颉项目失败,只能安装5.1.0版本了。
有知道原因的朋友可以评论留言指导一下。
创建项目和 ArkTS 项目差不多,跟着提示走就行了,这里不再赘述。

写代码

我们先写点代码整体感受一下,先不用关心其他的文件是做什么的。
找到entry->src->main->cangjie->index.cj这个文件开始写代码。
首先声明一个二维数组,需要注意的是,在仓颉中,如果想要观察数组中元素、数组大小的变化,需要使用ObservedArray或者ObservedArrayList,他们是状态管理的数组类型,当其中数组发生变化时,如修改其中一项的值,删除或添加一项,就会触发 UI 更新。

1
2
3
4
5
6
7
8
9
10
@State
var status: ObservedArray<ObservedArray<Bool>> = ObservedArray<ObservedArray<Bool>>(
[
ObservedArray<Bool>([false, false, false, false, false]),
ObservedArray<Bool>([false, false, false, false, false]),
ObservedArray<Bool>([false, false, false, false, false]),
ObservedArray<Bool>([false, false, false, false, false]),
ObservedArray<Bool>([false, false, false, false, false])
]
)

仓颉中的组件写法和 ArkTS 中基本一致,不一致也是语法上的不一致。
我们先添加一个标题

1
2
3
4
5
6
7
func build() {
Column {
Row() {
Text("Emacs 5x5 小游戏").fontSize(18)
}.width(100.percent).justifyContent(FlexAlign.Center)
}
}

使用关键字func声明函数,在 ArkTS 中的width("100%")改为了width(100.percent)。其他的属性保持了一致。
我们接着添加两个 ForEach 循环,先看一下函数类型

1
ForEach(dataSource: ArrayList<T>, itemGeneratorFunc: (T, Int64) -> Unit, keyGeneratorFunc: (T, Int64) -> String)

第一个参数是数据源,第二个参数是一个用于生成组件的函数,第三个参数是用于生成 key 的函数,和 ArkTS 中是一致的。

1
2
3
4
5
6
7
8
ForEach(
status,
{
state: ObservedArray<Bool>, rowIndex: Int64 => Row() {
ForEach(state, {current: Bool, columIndex: Int64 => Rect()})
}
}
)

第一个循环生成Row(),第二个循环在每一个Row中生成Rect()
然后我们根据状态来给 Rect 添加颜色:

1
2
3
4
5
6
func getColor(light: Bool): Color {
if (light) {
return Color.RED
}
return Color.BLACK
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ForEach(
status,
{
state: ObservedArray<Bool>, rowIndex: Int64 => Row() {
ForEach(
state,
{
current: Bool, columIndex: Int64 => Rect()
.width(50)
.height(50)
.fill(getColor(current))
.borderColor(Color.WHITE)
.borderWidth(1)
}
)
}
}
)

这里需要注意一点,如果需要自定义 key 生成器,需要这么写:

1
2
3
4
5
6
7
8
9
ForEach(
status,
itemGeneratorFunc: { //itemGeneratorFunc 可以不写
state: ObservedArray<Bool>, rowIndex: Int64 => Row() {}
},
keyGeneratorFunc:{
state: ObservedArray<Bool>, rowIndex: Int64 => return "keyGeneratorFunc"
}
)

而不能写成

1
2
3
4
5
6
7
8
9
10
ForEach(
status,
{
state: ObservedArray<Bool>, rowIndex: Int64 => Row() {}
},
{
state: ObservedArray<Bool>, rowIndex: Int64 => return "keyGeneratorFunc"
}
)

会提示positional argument cannot appear after named argument翻译过来就是位置参数不能出现在命名参数之后

点击事件

接下来我们添加一下点击事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.onClick {
_ =>
AppLog.error("AppLog 点击了${rowIndex} ${columIndex}")
Hilog.error(0x01, "huangyuan", "点击了${rowIndex} ${columIndex}")
status[rowIndex][columIndex] = !status[rowIndex][columIndex]
if (rowIndex - 1 >= 0) {
status[rowIndex - 1][columIndex] = !status[rowIndex - 1][columIndex]
}
if (columIndex - 1 >= 0) {
status[rowIndex][columIndex - 1] = !status[rowIndex][columIndex - 1]
}

if (rowIndex + 1 <= 4) {
status[rowIndex + 1][columIndex] = !status[rowIndex + 1][columIndex]
}
if (columIndex + 1 <= 4) {
status[rowIndex][columIndex + 1] = !status[rowIndex][columIndex + 1]
}

if (checkResult()) {
PromptAction.showToast(message: "success", duration: 1500, bottom: "80vp")
}
showStatus()
}

这里打印日志有两种方案,一种是用AppLog,另外一种就是延续 ArkTS 中Hilog
代码也很简单,就是按照前面的规则来修改对应位置的值。
这里有一个checkResult()的函数调用,当二维数组中的值全部为true时则弹出toast提示成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func checkResult(): Bool {
var success = true
for (row in 0..status.size) {
for (colum in 0..status[row].size) {
if (!status[row][colum]) {
success = false
break
}
}
if (!success) {
break
}
}
return success
}

这里有一点需要注意:由于ObservedArray没有实现Iterator,因此不能使用for(xx in status)方式进行遍历。
showStatus()函数则是打印当前的状态,没啥特殊的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func showStatus() {
var result = ""
for (row in 0..status.size) {
for (colum in 0..status[row].size) {
if (status[row][colum]) {
result += "红 "
} else {
result += "黑 "
}
}
result += "\n"
}
Hilog.error(0x01, "huangyuan", result)
}

这样我们就完成了小游戏的主体功能。看下效果
效果


第一篇完结撒花


鸿蒙-仓颉-使用仓颉语言开发鸿蒙应用-初窥门径
https://blog.huangyuanlove.com/2025/11/24/鸿蒙-仓颉-使用仓颉语言开发鸿蒙应用-初窥门径/
作者
HuangYuan_xuan
发布于
2025年11月24日
许可协议
BY HUANG兄