最佳实践 - ArkTS 驱动鸿蒙元服务开发:从界面布局到交互逻辑,打造多功能决策类元服务

最佳实践 - ArkTS 驱动鸿蒙元服务开发:从界面布局到交互逻辑,打造多功能决策类元服务

最佳实践 - ArkTS 驱动鸿蒙元服务开发:从界面布局到交互逻辑,打造多功能决策类元服务

img

项目目录结构与功能模块说明

img
entry/src/main/ets/ ├── entryability/ # 应用程序入口能力 │ └── EntryAbility.ets # 主入口文件 ├── entryformability/ # 卡片能力相关 ├── images/ # 图片资源文件 ├── pages/ # 页面组件 │ ├── Index.ets # 主页面 │ ├── FingertipTable.ets # 决策币功能 │ ├── LuckyNumber.ets # 幸运号码功能 │ └── TurnLuck.ets # 转出好运功能 └── widget/ # 小组件相关

主页面设计(Index)

img
声明式布局:Column + List

响应式状态管理:@StorageProp

统一路由跳转机制:router.pushUrl

交互动画多态样式:stateStyles + animation
布局主体结构
页面外层采用 Column 实现垂直布局,内部通过 List 组件构建 “决策币、幸运号码、转出好运” 功能入口,使用 padding 动态适配顶部与底部安全区域。
Column(){NoticeBar().margin({bottom:10})List({space:40}){// 子项列表}.alignListItem(ListItemAlign.Center)}.padding({top:this.topHeight +51,bottom:this.bottomHeight }).height('100%').width('100%')
功能入口组件封装
每个 ListItem 作为独立功能入口,通过 stateStyles 实现按压缩放动画增强交互体验,以 router.pushUrl() 完成页面的跳转。
ListItem(){Row(){Text('决策币').fontSize(50).fontColor('#FFFFFF')Image('images/shouzhi.svg').width(40)}}.backgroundColor('#6699FF').width('90%').height(170).borderRadius(20).stateStyles({normal:{.scale({x:1,y:1})},pressed:{.scale({x:0.95,y:0.95})}}).animation({duration:200}).onClick(_=>{ router.pushUrl({url:'pages/FingertipTable'})})
多功能模块统一交互逻辑
相同跳转逻辑复用:决策币、幸运号码、转出好运三个功能入口,通过统一的 router.pushUrl() 实现页面导航,保证路由逻辑一致。
router.pushUrl({url:'pages/FingertipTable'}) router.pushUrl({url:'pages/LuckyNumber'}) router.pushUrl({url:'pages/TurnLuck'})

应用入口能力实现(EntryAbility)

img
全屏窗口初始化
应用启动时创建主窗口并设置全屏显示
const win = windowStage.getMainWindowSync() win.setWindowLayoutFullScreen(true)
安全区域适配
动态计算顶部与底部安全区高度并存入全局状态,实现多设备屏幕与系统栏的自适应显示
// 获取系统状态栏(如信号栏、时间栏)等区域的避让范围const top = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM).topRect // 将顶部安全区域的高度(单位像素)转换为 vp 并存储到全局状态,用于页面动态适配 AppStorage.setOrCreate<number>('topHeight',px2vp(top.height))// 获取系统导航指示栏(如手势导航区域)的避让范围const bottom = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR).bottomRect // 将底部安全区域的高度转换为 vp 并存储到全局状态,用于底部留白或控件布局自适应 AppStorage.setOrCreate<number>('bottomHeight',px2vp(bottom.height))
首页内容加载
窗口创建完成后加载主页面 Index.ets,正式进入应用界面渲染阶段
windowStage.loadContent('pages/Index',(err)=>{});

核心功能模块设计与实现

决策币功能(FingertipTable)
img
动态硬币翻转动画机制
循环调用 animateToImmediately(),短时间内多次递增旋转角度,实现硬币连续旋转的视觉效果,基于状态驱动的动画更新,让 UI 与数据绑定紧密,不依赖复杂的帧渲染逻辑
.animateToImmediately({delay: i * totalAnimationDuration / maxAnimationSteps,duration:100,},()=>{this.rotationAngle +=90;// 每次增加90度旋转});
双阶段抛掷模拟
双阶段动画链式执行让动作更贴近真实物理效果第一段:硬币上抛,verticalOffset 为负值模拟上升第二段:硬币下落,verticalOffset 回归 0,恢复初始位置
animateToImmediately({duration: totalAnimationDuration /2,onFinish:()=>{animateToImmediately({duration: totalAnimationDuration /2,onFinish:()=>{/* 落地后逻辑 */}},()=>{this.verticalOffset =0;});}},()=>{this.verticalOffset =-100*(1+ Math.floor(Math.random()*5));});
幸运号码功能(LuckyNumber)
img
img
状态与属性声明部分
@StorageProp 保存顶部与底部间距等布局数据在不同组件间共享

@State 控制组件内部动态状态通过 isUnit 判断显示个位数或十位数
@StorageProp('topHeight') topHeight: number =0 @StorageProp('bottomHeight') bottomHeight: number =0 @State isUnit: boolean =true
切换按钮逻辑
两个按钮分别对应个位数和十位数,点击任意一个都会切换 isUnit 状态,按钮颜色与文字样式随状态变化而更新,从而实现视觉与逻辑同步的动态切换效果
Row(){Column(){Text('切换个位数').fontSize(15).fontColor(this.isUnit ? Color.White :'#00000').fontWeight(900)}.onClick(_=>{this.isUnit =!this.isUnit }).backgroundColor(this.isUnit ? Color.Red : Color.White).height(50).width('50%')Column(){Text('切换十位数').fontSize(15).fontColor(this.isUnit ?'#00000': Color.White).fontWeight(900)}.onClick(_=>{this.isUnit =!this.isUnit }).backgroundColor(this.isUnit ? Color.White : Color.Red).height(50).width('50%')}
转出好运功能(TurnLuck)
img
扇形单元格绘制与角度计算
数据驱动转盘的可视化和旋转逻辑
// 计算每个单元格在转盘上的角度和旋转信息privatecalculateAngles(){// 计算所有单元格比例的总和const totalProportion =this.cells.reduce((sum, cell)=> sum + cell.proportion,0);// 根据每个单元格的比例计算对应的扇形角度this.cells.forEach(cell=>{ cell.angle =(cell.proportion *360)/ totalProportion;// 扇形角度 = 单元格比例占比 * 360°});let cumulativeAngle =0;// 用于累加每个单元格的角度,确定起始位置// 遍历单元格,设置起始角度、结束角度及旋转角度this.cells.forEach(cell=>{ cell.angleStart = cumulativeAngle;// 扇形起始角度 cumulativeAngle += cell.angle;// 更新累计角度 cell.angleEnd = cumulativeAngle;// 扇形结束角度 cell.rotate = cumulativeAngle -(cell.angle /2);// 扇形文本或元素旋转角度,使其居中显示});}
转盘旋转动画与选中逻辑
实现转盘动画和随机选择功能
// “开始”按钮点击事件:触发转盘旋转Button('开始').onClick(()=>{// 如果转盘正在旋转,直接返回,避免重复触发if(this.isAnimating)return;this.selectedName ="";// 清空当前选中名称this.isAnimating =true;// 标记动画开始// 调用动画函数进行旋转animateTo({duration:5000,// 动画持续时间 5 秒curve: Curve.EaseInOut,// 缓入缓出动画曲线onFinish:()=>{// 动画结束回调this.currentAngle %=360;// 保持角度在 0~360° 范围内// 判断当前角度落在哪个单元格for(const cell ofthis.cells){if(360-this.currentAngle >= cell.angleStart &&360-this.currentAngle <= cell.angleEnd){this.selectedName = cell.title;// 设置选中单元格的标题break;// 找到目标单元格后退出循环}}this.isAnimating =false;// 动画结束,重置状态},},()=>{// 动画进行中回调:更新当前角度,实现旋转效果this.currentAngle +=(360*5+ Math.floor(Math.random()*360));// 随机旋转多圈});});
单元格编辑与动态更新
提供转盘单元格内容和比例的动态管理
// 遍历每个单元格,创建可编辑行ForEach(this.cells,(item: Cell,index: number)=>{Row(){// 文本输入框:显示并编辑单元格标题TextInput({text: item.title }).onChange(value=> item.title = value);// 内容变化时更新单元格标题// 计数器组件:调整单元格比例CounterComponent({options:{numberOptions:{value: item.proportion,// 初始比例值onChange:(v)=>{// 值变化回调 item.proportion = v;// 更新单元格比例this.calculateAngles();// 重新计算转盘角度}}}});// 删除按钮:移除当前单元格Button('删除').onClick(()=>{this.cells.splice(index,1);// 从数组中删除this.calculateAngles();// 重新计算转盘角度});}});// 添加新单元格按钮Button('添加新内容').onClick(()=>{// 新建单元格,分配颜色并添加到数组this.cells.push(newCell(1,"新内容",this.colorPalette[this.colorIndex++%this.colorPalette.length]));this.calculateAngles();// 更新转盘角度});
元服务卡片设计与生命周期管理
卡片生命周期管理(EntryFormAbility.ets) :介绍卡片的创建、更新、删除等生命周期方法实现卡片用户界面设计(WidgetCard.ets) :分析卡片的UI结构、组件使用和交互设计
卡片生命周期实现(EntryFormAbility)
import{ formBindingData, FormExtensionAbility, formInfo }from'@kit.FormKit';import{ Want }from'@kit.AbilityKit';// 表单扩展能力类exportdefaultclassEntryFormAbilityextendsFormExtensionAbility{// 当添加表单时调用,返回 FormBindingData 对象onAddForm(want: Want){let formData ='';return formBindingData.createFormBindingData(formData);}// 当临时表单成功转换为普通表单时调用onCastToNormalForm(formId: string){// 可在此处理转换后的逻辑}// 通知表单提供者更新指定表单onUpdateForm(formId: string){// 可在此实现更新表单逻辑}// 指定表单触发事件时调用onFormEvent(formId: string,message: string){// 可在此处理表单事件逻辑}// 通知表单提供者指定表单已被销毁onRemoveForm(formId: string){// 可在此处理表单移除逻辑}// 获取表单状态时调用,返回 FormState 对象onAcquireFormState(want: Want){return formInfo.FormState.READY;// 表单准备就绪状态}}
卡片界面结构与交互设计(WidgetCard)
@Entry @Component struct WidgetCard {/* * 卡片标题文本 */ readonly TITLE: string ='开始决策 🫵';/* * 点击行为类型,例如路由跳转 */ readonly ACTION_TYPE: string ='router';/* * 目标能力或页面名称 */ readonly ABILITY_NAME: string ='EntryAbility';/* * 跳转时传递的参数消息 */ readonly MESSAGE: string ='add detail';/* * 卡片宽度设置 */ readonly FULL_WIDTH_PERCENT: string ='100%';/* * 卡片高度设置 */ readonly FULL_HEIGHT_PERCENT: string ='100%';build(){// 可点击卡片,点击后触发路由或能力调用FormLink({action:this.ACTION_TYPE,// 动作类型abilityName:this.ABILITY_NAME,// 目标能力/页面params:{message:this.MESSAGE}// 传递参数}){// 卡片内部布局Row(){Column(){// 显示卡片标题Text(this.TITLE).fontSize($r('app.float.font_size'))// 字体大小.fontWeight(FontWeight.Medium)// 字体粗细.fontColor($r('app.color.item_title_font'))// 字体颜色}.width(this.FULL_WIDTH_PERCENT)// 列宽度填满容器}.height(this.FULL_HEIGHT_PERCENT)// 行高度填满容器}}}

总结

文章以鸿蒙元服务的决策币、幸运号码、转盘抽奖功能为核心,展示 ArkTS 开发实践:声明式布局搭 UI@State 等做响应式管理animateToImmediately 与 router 实现动画和导航

覆盖应用入口适配、功能核心逻辑及元服务卡片设计,形成完整开发流程,为鸿蒙元服务开发提供可复用参考

👉如果你也在探索鸿蒙元服务开发,或是想第一时间 get 鸿蒙新特性适配,点击链接加入和开发者们一起交流经验吧!https://work.weixin.qq.com/gm/afdd8c7246e72c0e94abdbd21bc9c5c1

Read more

无人机遥感航拍巡检数据集 无人机遥感图像识别 无人机视角山区泥石流和滑坡图像识别数据集-数据集第10067期

无人机遥感航拍巡检数据集 无人机遥感图像识别 无人机视角山区泥石流和滑坡图像识别数据集-数据集第10067期

滑坡检测数据集核心信息介绍 ** 这个滑坡检测数据集主要用于目标检测任务,整体数据规模和细节都比较明确。从数量上看,数据集总共包含 1660 张图像, 往期热门主题 主题搜两字"关键词"直达 代码数据获取: 获取方式:***文章底部卡片扫码获取*** 覆盖了YOLO相关项目、OpenCV项目、CNN项目等所有类别, 覆盖各类项目场景(包括但不限于以下----欢迎咨询定制): 项目名称项目名称基于YOLO+deepseek 智慧农业作物长势监测系统基于YOLO+deepseek 人脸识别与管理系统基于YOLO+deepseek 无人机巡检电力线路系统基于YOLO+deepseek PCB板缺陷检测基于YOLO+deepseek 智慧铁路轨道异物检测系统基于YOLO+deepseek 102种犬类检测系统基于YOLO+deepseek 人脸面部活体检测基于YOLO+deepseek 无人机农田病虫害巡检系统基于YOLO+deepseek 水稻害虫检测识别基于YOLO+deepseek 安全帽检测系统基于YOLO+deepseek 智慧铁路接触网状态检测系统基于YOLO+

Spatial Joy 2025 全球 AR&AI 赛事:开发者要的资源、玩法、避坑攻略都在这

Spatial Joy 2025 全球 AR&AI 赛事:开发者要的资源、玩法、避坑攻略都在这

Spatial Joy 2025 全球 AR&AI 赛事:开发者要的资源、玩法、避坑攻略都在这 * 引言: * 正文: * 一、赛事核心价值:资源、履历、落地全具备 * 1.1 硬核资源支持 * 1.2 行业背书与机遇 * 1.3 低门槛试错 * 二、赛道核心玩法:AI 和 AR 创作方向解析 * 2.1 AI 赛道:拼的是 "空间认知协作" 能力 * 2.1.1 应用示例 * 2.2 AR 赛道:

飞书机器人与Claude Code交互:从手机指令到AI处理的全自动流程

飞书机器人与Claude Code交互:从手机指令到AI处理的全自动流程

飞书机器人与Claude Code交互:从手机指令到AI处理的全自动流程 * 一、背景 * 二、实现方案概览 * 三、操作步骤 * 前置准备 * 第一步:创建并进入Claude Code容器 * 配置Claude Code使用本地模型 * 测试Claude Code是否正常工作 * 第二步:安装Python依赖 * 第三步:获取飞书应用的凭证 * 第四步:编写并运行中间件脚本 * 脚本解释 * 运行脚本 * 第五步:在飞书中与机器人对话 * 常见问题 * 总结 一、背景 在日常开发中,我们经常需要快速查询代码问题、生成文档或执行简单的编程任务。如果有一款AI助手能随时响应,就像在电脑终端前一样,那该多方便!本教程将演示如何搭建一个飞书机器人,当你在手机飞书App上发送消息时,该消息会传递给运行在电脑上的Claude Code(一个智能编码助手),Claude Code处理后将结果回复到你的飞书会话中。 通过这个方案,你可以: * 在手机上随时向AI提问编程问题。 * 让AI帮你调试

openclaw 对接完飞书群机器人配置踩坑记:消息不回、Gateway 断开问题排查

openclaw 对接完飞书群机器人配置踩坑记:消息不回、Gateway 断开问题排查

前言 用 OpenClaw 配飞书机器人,踩了两个坑:群消息不回、Gateway 总是断开。排查了好一阵子,总算搞定了,记录一下希望能帮到遇到同样问题的朋友。 发现问题 飞书消息不回复 在飞书群里 @ 了机器人,完全没反应。一开始以为是网络不好或者机器人没上线,但状态显示明明是连接着的,这就奇怪了。 Gateway 频繁断开 每次改完配置跑 openclaw gateway restart,或者根本什么都没干,Gateway 说断就断。再想启动就报错,必须跑一遍 openclaw doctor --fix 重新安装才能用。太影响使用了。 查看原因 飞书机器人 ID 搞错了 翻日志看到这么一句: receive events or callbacks through persistent connection only available in