前言

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

比如初始都为黑色,以左上角为[0,0],右下角为[4,4]。当点击 [1,1]处的格子,则[1,0] [0,1] [1,1],[1,2],[2,1] 都由黑色变为红色。

在这里插入图片描述


这时在点击[0,0], 则[0,0]由黑色变为红色。[0,1][1,0]则由红色变为黑色

在这里插入图片描述


我们的目的就是让格子都变为红色。
规则介绍完了,我们开始写代码

开始

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

创建项目

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

写代码

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

    @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中基本一致,不一致也是语法上的不一致。
我们先添加一个标题

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

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

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

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

 ForEach(
     status,
     {
         state: ObservedArray<Bool>, rowIndex: Int64 => Row() {
             ForEach(state, {current: Bool, columIndex: Int64 => Rect()})
         }
     }
 )

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

func getColor(light: Bool): Color {
    if (light) {
        return Color.RED
    }
    return Color.BLACK
}
 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生成器,需要这么写:

ForEach(
    status,
   itemGeneratorFunc: { //itemGeneratorFunc 可以不写
        state: ObservedArray<Bool>, rowIndex: Int64 => Row() {}
    },
    keyGeneratorFunc:{
         state: ObservedArray<Bool>, rowIndex: Int64 => return  "keyGeneratorFunc"
    }
)

而不能写成

ForEach(
    status,
    {
        state: ObservedArray<Bool>, rowIndex: Int64 => Row() {}
    },
    {
         state: ObservedArray<Bool>, rowIndex: Int64 => return  "keyGeneratorFunc"
    }
)

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

点击事件

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

.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提示成功

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()函数则是打印当前的状态,没啥特殊的

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)
}

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

在这里插入图片描述


第一篇完结撒花

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐