前言 之前写了一系列的仓颉语言教程,虽然它既能用于鸿蒙应用开发,也能用于服务端开发。但我作为一个移动端开发人员来讲,还是着重于仓颉语言在鸿蒙应用开发中的应用。 在这里,我们移植一个 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 更新。
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 : { 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) }
这样我们就完成了小游戏的主体功能。看下效果
第一篇完结撒花