在入口组件(@Entry修饰的组件)中绘制布局,该布局使用Stack容器,展示文字,图片的堆叠效果,在容器中依次填入Image,Text,Image,Butto,Text组件,具体代码如下
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Stack() {
Image($r("app.media.bg"))
Column() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Text('井字游戏大作战').fontSize(30).textAlign(TextAlign.Center).fontColor(Color.White)
}.height('10%')
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
Stack() {
Image($r("app.media.bg_start_game"))
Column() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Image($r("app.media.battles")).height(100).width(100)
}.height('50%')
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button('开始游戏').height(50).width(200).backgroundColor('#32CD99').fontSize(20).onClick(() => {
this.startDialog.open()
})
Text("").height(20)
Text('游戏规则').fontSize(20).fontColor('#FF00FF').height(60).decoration({ type: TextDecorationType.Underline, color: Color.Red }).onClick(() => {
this.rulesDialog.open()
})
}.height('40%')
}
}
}.height('50%').width('90%')
Flex() {
}.height('30%')
}
}
}.width('100%').height('100%')
}
自定义弹窗
1.点击开始按钮的弹窗如下图所示,窗口从上到下由Text, List,Button组成,List中的子元素由Text和Radio组成,以下代码的省略号表示非UI相关的逻辑代码,具体实现参考源代码
@CustomDialog
struct gameStart {
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
//顶部标题
Text('发现以下在线设备').fontColor(Color.Black).fontSize(30)
}.width('100%').height('20%')
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
//使用List容器动态加载在线设备
List() {
ForEach(this.deviceName, (item) => {
ListItem() {
Row() {
//Text组件显示设备名
Text(item.deviceName).width('80%').fontSize(30).fontColor(Color.Black)
//Radio组件显示单选框
Radio({ value: '' }).checked(this.check[item.id]).onChange(() => {
//这里保证List里面点击了多个Radio组件时,只有当前点击的为选中状态
for (let i = 0; i < this.check.length; i++) {
this.check[i] = false
}
this.check[item.id] = true
})
}
}
}, item => item.id)
}
.height('80%')
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button('确定').width(200).height(50).fontSize(30).onClick(() => {
//......
this.controller.close()
})
}.height('30%')
}.width('100%').height('80%')
}.height('100%').width('100%')
}
}
点击游戏规则的弹窗由Text,Text,button组成,具体实现可以参考弹窗1,和源代码
2) 棋盘界面
棋盘界面由状态栏(左上图标表示当前由X或者O执行,右上表示自己本局使用X或者O),九宫格棋盘(Grid组件绘制),button按钮组成。
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Row({ useAlign: HorizontalAlign.Start }) {
//左上图标
if (this.selfChess === EMPTY) {
Image(this.current_X).height(150).width('30%')
} else if (this.curChess === CIRCLE) {
Image(this.current_O).height(150).width('30%')
} else {
Image(this.current_X).height(150).width('30%')
}
//填充空格
Text("").width('40%')
//右上图标
if (this.selfChess === EMPTY) {
Image(this.whiteBg).height(150).width('30%')
} else {
Image(this.selfChess === FORK ? this.self_X : this.self_O).height(150).width('30%')
}
}.height('30%')
Flex() {
//堆叠容器
Stack() {
//绘制九宫格
Grid() {
ForEach(this.gridVis, (item) => {
GridItem() {
//每个子格子当前需要填充的图片
if (item.value === FORK) {
Image(this.fork).backgroundColor(Color.White)
} else if (item.value === CIRCLE) {
Image(this.circle).backgroundColor(Color.White)
} else {
Image(this.whiteBg)
}
}.onClick(() => {
//点触格子触发事件
this.dealPoint(item)
})
}, item => item.id)
}.columnsTemplate('1fr 1fr 1fr').rowsTemplate('1fr 1fr 1fr').columnsGap(3).rowsGap(3).align(Alignment.Center).alignSelf(ItemAlign.Center).backgroundColor('#9F5F9F')
//根据胜负状态显示图片
if (result === YOU_WIN) {
Image($r("app.media.game_win")).width(224).height(224)
} else if (result === YOU_LOSE) {
Image($r("app.media.game_fail")).width(224).height(224)
} else if (result === TIE) {
Image($r("app.media.game_tie")).width(224).height(224)
}
}
}.height('50%')
Row({ useAlign: HorizontalAlign.Center }) {
Button('重新开始').width(200).height(50).fontSize(30).backgroundColor('#8E6B23').onClick(() => {
this.initGame()
})
}.height('20%')
}
}
3.设备认证
设备认证是依赖DeviceManager组件来实现的,详细代码参考源码RemoteDeviceModel.ets
1.创建DeviceManager实例
registerDeviceListCallback(callback) {
if (typeof (this.#deviceManager) === 'undefined') {
deviceManager.createDeviceManager('com.example.tictactoegame', (error, value) => {
if (error) return
this.#deviceManager = value;
this.registerDeviceListCallback_(callback);
});
} else {
this.registerDeviceListCallback_(callback);
}
}
2.查询可信设备列表
var list = this.#deviceManager.getTrustedDeviceListSync();
if (typeof (list) != 'undefined' && typeof (list.length) != 'undefined') {
this.deviceList = list;
}
3.注册设备上下线监听
this.#deviceManager.on('deviceStateChange', (data) => {
switch (data.action) {
case 0:
this.deviceList[this.deviceList.length] = data.device;
this.callback();
if (this.authCallback != null) {
this.authCallback();
this.authCallback = null;
}
break;
case 2:
if (this.deviceList.length > 0) {
for (var i = 0; i < this.deviceList.length; i++) {
if (this.deviceList[i].deviceId === data.device.deviceId) {
this.deviceList[i] = data.device;
break;
}
}
}
this.callback();
break;
case 1:
if (this.deviceList.length > 0) {
var list = [];
for (var i = 0; i < this.deviceList.length; i++) {
if (this.deviceList[i].deviceId != data.device.deviceId) {
list[i] = data.device;
}
}
this.deviceList = list;
}
this.callback();
break;
default:
break;
}
});
4.设备发现
this.#deviceManager.on('deviceFound', (data) => {
for (let i = 0; i < this.discoverList.length; i++) {
if (that.discoverList[i].deviceId === data.device.deviceId) {
return;
}
}
this.discoverList[this.discoverList.length] = data.device;
this.callback();
});
5.设备认证
authDevice(deviceInfo, callback){
let extraInfo = {
"targetPkgName": 'com.example.tictactoegame',
"appName": 'com.example.tictactoegame',
"appDescription": 'com.example.tictactoegame',
"business": '0'
};
let authParam = {
"authType": 1,
"appIcon": '',
"appThumbnail": '',
"extraInfo": extraInfo
};
this.#deviceManager.authenticateDevice(deviceInfo, authParam, (err, data) => {
if (err) {
this.authCallback = null;
} else {
this.authCallback = callback;
}
});
}
4.数据管理
分布式数据管理依赖@ohos.data.distributedData模块实现,详细参考源码RemoteDataManager.ets
1.导入该模块
import factory from '@ohos.data.distributedData';
2.创建KVManager实例,用于管理数据库对象
registerDataListCallback(callback) {
let that = this
if (this.kvManager == null) {
try {
const config = {
userInfo: {
userId: '0',
userType: 0
},
bundleName: 'com.example.tictactoegame'
}
factory.createKVManager(config).then((manager) => {
that.kvManager = manager
that.registerDataListCallback_(callback)
}).catch((err) => {
})
} catch (e) {
}
} else {
this.registerDataListCallback_(callback)
}
}
3.创建并获取KVStore数据库
registerDataListCallback_(callback) {
let that = this
if (that.kvManager == null) {
callback()
return
}
if (that.kvStore == null) {
try {
let options =
{
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: 1,
securityLevel: 3
}
this.kvManager.getKVStore(this.STORE_ID, options).then((store) => {
that.kvStore = store
that._registerDataListCallback_(callback)
}).catch((err) => {
})
} catch (e) {
}
} else {
this._registerDataListCallback_(callback)
}
}
4.订阅指定类型的数据变更通知
_registerDataListCallback_(callback) {
let that = this
if (that.kvManager == null) {
callback()
return
}
this.kvStore.on('dataChange', 1, function(data) {
if (data) {
that.arr = data.updateEntries
callback()
}
})
}
5.添加指定类型键值对到数据库
dataChange(key, value) {
let that = this
try {
that.kvStore.put(JSON.stringify(key), JSON.stringify(value)).then((data) => {
}).catch((err) => {
prompt.showToast({message:'put err:'+JSON.stringify(value)})
})
} catch (e) {
}
}
5.远程拉起设备app
使用FeatureAbility模块的startAbility接口拉起远程设备app
startAbilityContinuation(deviceId) {
let wantValue = {
bundleName: 'com.example.tictactoegame',
abilityName: 'com.example.tictactoegame.MainAbility',
deviceId: deviceId,
parameters: {
uri: 'pages/Fight'
}
};
featureAbility.startAbility({ want: wantValue }).then(() => {
router.replace({ uri: 'pages/Fight' })
});
}
6.棋局胜负判定
对于棋盘中的任一个格子只存在已落子和未落子两种状态,我们把这两种状态定义为1和0,则九宫格可以看成是一个只有9位的二进制数,胜利状态有八种情况如下,详细代码参考源码Chess.ets:
0b111000000
0b100100100
0b100010001
0b010010010
0b001001001
0b000111000
0b000000111
0b001010100
胜负判定实现如下
isWin() {
let x = this.result_X
let o = this.result_O
if (448 == (x & 448) || 292 == (x & 292) || 273 == (x & 273) || 146 == (x & 146) || 73 == (x & 73) || 56 == (x & 56) || 7 == (x & 7) || 84 == (x & 84)){
this.allIsTrue()
return FORK
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/a7a74da9a092022d2726414aaba0deda.png)
![img](https://img-blog.csdnimg.cn/img_convert/e430c5abb5ec90357a496f3003a6d0d9.png)
![img](https://img-blog.csdnimg.cn/img_convert/716d6a88d64ca2429a1b0ee0fe8ab3b8.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新**
**如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)**
![img](https://img-blog.csdnimg.cn/img_convert/2f0c5890782fb7f52174db6cf947257c.png)
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
友,同时减轻大家的负担。**
[外链图片转存中...(img-ZLrXe6ac-1712922192930)]
[外链图片转存中...(img-F5QK7WqV-1712922192931)]
[外链图片转存中...(img-g9qlrGfh-1712922192931)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新**
**如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)**
[外链图片转存中...(img-TiJaIInn-1712922192931)]
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**