HarmonyOS 鸿蒙Next ArkUI入门训练营—健康生活实战 基于ArkUI的运动记录App
HarmonyOS 鸿蒙Next ArkUI入门训练营—健康生活实战 基于ArkUI的运动记录App
前言
在参加了"HarmonyOS ArkUI入门训练营——健康生活实战"后,了解并学习了声明式UI开发框架及组件用法,本文是对笔者结营作品的介绍分享。
概述
- 本案例的API版本为8,使用了路由跳转API(比如点击搜索按键,可以跳转至搜索结果的页面);一次开发多段部署API,使用其中介绍的自适应布局能力和响应式布局能力进行多设备(或多窗口尺寸)适配,保证应用在不同设备或不同窗口尺寸下可以正常显示。
- 这是一个运动记录的应用,主要用于管理健康记录运动。可以添加运动信息,包括运动名称、运动时长,并自动计算消耗的卡路里,在记录页面可以查看所添加的运动记录。
运行效果图如下:
| |
|
代码
源码见官方demo仓
正文
一、文件架构说明
二、主要的开发说明
本应用一共有“启动动画页面”、“主页面”和“搜索结果显示页面”三个页面。
1、/pages/logo.ets/
这是启动动画页面,使用Shape绘制组件和绘制命令,切割绘制了一个图案,并添加显式动画,加载页面后设置定时器自动跳转至主页面。背景色及图案都使用了渐变色。以下为图案的绘制及动画设置的主要代码(完整代码请参考代码仓的源码)。pathCommand为预先定义好的路径绘制命令,clip裁剪后得到图案,然后设置放缩和透明度的属性动画。
Shape() {
Path()
.commands(this.pathCommand1)
.fill('none')
.linearGradient({ angle: 90, colors: [['#FBDBBB',0.0],['#F6A95C',1]] })
.clip(new Path().commands(this.pathCommand1))
Path()
.commands(this.pathCommand2)
.fill('none')
.linearGradient({ angle: 90, colors: [['#FBDBBB',0.0],['#F6A95C',1]] })
.clip(new Path().commands(this.pathCommand2))
}
.height('640px')
.width('640px')
.scale({ x: this.scaleValue, y: this.scaleValue })
.opacity(this.opacityValue)
.onAppear(() => {
animateTo({
duration: 1000,
curve: this.curve1,
delay: 100,
onFinish: () =>{
setTimeout(() =>{
router.replace({ url: 'pages/SportsCategoryList' })
}, 2000);
}
}, () =>{
this.opacityValue = 1
this.scaleValue = 1
})
})
2、/pages/SportsCategoryList.ets/
这是主页面,使用Tabs组件把主页面分为两个页签:“主页”页签和“记录”页签。“主页”页签展示所有运动项目,按运动类别分类。在/model/SportsData中定义了运动类和纪录类,在/model/SportsDataModel中定义了将项目中定义的运动项数据初始化存放在一个数组的函数。
在主页面,点击运动项会弹出添加运动的弹窗。自定义弹窗组件Record并用@CustomDialog标识, calculate()
函数计算该项运动合计消耗的卡路里, @Builder valueInput()
定义了四行三列的使用 Grid
去布局数字输入器的组件。
先定义一个数组存放0到9的数字、“删除记录”字符串和回车删除输入的图片,然后在Grid容器中判断数据类型,再相应定义其点击事件,其中数字输入范围设定为0~999,按确认时若数字为0则会弹窗提示。
此外还定义了一个 mode
参数,为0时是添加运动记录
模式,此时“删除记录”按键无响应事件,按确定按键时响应记录数组增加记录项;mode为1时是修改运动记录模式
,点击“删除记录”会弹窗“是否确认删除”,然后从记录数组中移除该记录项;若是修改记录项的时间,笔者实现的逻辑是先从记录数组中删除原记录项,然后再插入新的记录项。以下为主要代码及实现逻辑的说明:
[@CustomDialog](/user/CustomDialog)
export struct Record {
private mode: number = 0 //0:添加,1:修改
private controller: CustomDialogController
@State time: string = '0'
@State sum: number = 0
private Valueinput: any[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '删除记录', '0', $r('app.media.Back')]
calculate() {
if (this.time.length != 0) {
this.sum = Math.round(parseInt(this.time) * this.sportsItem.value / 60)
} else {
this.sum = 0
}
}
@Builder RecordItem(image: Resource, name: string, value: number) {
//记录项的布局
}
@Builder valueInput() {
Column() {
Grid() {
ForEach(this.Valueinput, (item: any) => {
GridItem() {
if (typeof (item) == 'string') {
Text(item) //显示数字
//其他属性
.onClick(() => {
if(item.length<2){ //即是数字按键
//避免‘023’这样的显示
if (this.time == '0') {
this.time = item
}
//判断数值是否<=999,若否,则用拼接字符串的形式显示
else if (parseInt(this.time) < 999 && parseInt(this.time + item) < 999) {
this.time = this.time + item
}
else {
this.time = '999'
}
this.calculate() //实时计算合计卡路里
}
//对于“删除记录”按键
else{
if(this.mode==1){
AlertDialog.show(
{
message: '确认删除这条运动记录吗?',
primaryButton: {
value: '取消',
action: () =>{}
},
secondaryButton: {
value: '确定',
action: () =>{
//删除记录项
RecordDataArray.splice(this.item_index, 1)
RecordSports.splice(this.item_index, 1)
this.controller.close()
}
},
cancel: () =>{}
}
)
}
}
})
} else if (typeof (item) == 'object') {
Image(item).width(20).aspectRatio(1).objectFit(ImageFit.Contain)
//回车删除的逻辑,对字符串取子串,然后再实时计算总消耗的卡路里
.onClick(() => {
if (this.time.length > 1) {
this.time = this.time.substring(0, this.time.length - 1)
}
else if (this.time.length == 1) {
this.time = '0'
}
this.calculate()
})
}
}
})
}
.backgroundColor(Color.White)
.columnsTemplate('1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(0)
.rowsGap(0)
.width('100%')
.height('35%')
}
}
build() {
//弹窗的整体布局
}
}
//列表的运动项
@Component
export struct SportsGridItem {
private sportsItem: SportsData
private controller: CustomDialogController
//弹窗的使用
aboutToAppear() {
this.controller = new CustomDialogController({
builder: Record({ sportsItem: this.sportsItem }),
alignment: DialogAlignment.Center
})
}
build() {
//运动项的布局
}
.onClick(() => {
this.controller.open()
})
}
}
3、/pages/search_result.ets/
这是搜索结果显示页面,根据主页面搜索的词来检索是否有相关运动项,若有则显示此运动项,若无就显示"没有查到相关结果"。由于有运动项是同名但是不同配速,于是增加了关键字匹配。
struct Search_result {
@State name:string = router.getParams()['sports']
private sportsItem: SportsData[] = initializeOnStartup()
private ResultDataArray: Array<SportsData> = []
aboutToAppear() {
let item;
for (item of this.sportsItem) {
//匹配关键字
if (item.name.length >= this.name.length) {
if (this.name == item.name.substring(0, this.name.length)) {
this.ResultDataArray.push(item);
}
}
else {
if (this.name == item.name) {
this.ResultDataArray.push(item);
}
}
}
}
build(){
Column() {
//其他组件
Scroll() {
Column() {
if (this.ResultDataArray.length != 0) {
SportsGrid({ sportsItems: this.ResultDataArray })
}
else {
Text('没有查到与此相关的结果').fontSize(19).width('100%').height(20).margin({ top: 12, left: 20 })
}
}
}
.scrollBar(BarState.Off)
}
}
结语
该Demo还是有待继续优化的,比如在记录页面修改或删除记录项时尚未能实时更新,但由于时间关系,还待优化解决。以上就是本次的小分享了~❀❀。
更多关于HarmonyOS 鸿蒙Next ArkUI入门训练营—健康生活实战 基于ArkUI的运动记录App的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
学习了
更多关于HarmonyOS 鸿蒙Next ArkUI入门训练营—健康生活实战 基于ArkUI的运动记录App的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
学习了
找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:BV1S4411E7LY/?p=17
厉害
强
学习了
明天记得发动朋友为你的帖子投票哦~另外代码不要忘记提交代码仓哈!
已提交~
ArkUI是HarmonyOS(鸿蒙)系统为开发者提供的一套用于构建用户界面的声明式前端框架。它支持使用TypeScript和eTS(Enhanced TypeScript,增强的TypeScript)语言进行开发,允许开发者通过简洁的语法描述UI界面及其逻辑。
在基于ArkUI开发运动记录App时,你需要关注以下几个关键点:
-
页面布局:利用ArkUI提供的布局组件(如FlexboxLayout、GridLayout等)来设计运动记录页面的整体布局。
-
数据绑定:通过ArkUI的数据绑定机制,将运动数据(如步数、运动时长等)实时展示在界面上。
-
组件交互:实现用户与界面之间的交互,如点击按钮开始/停止记录运动,滑动页面查看历史记录等。
-
样式定制:利用ArkUI的样式系统,为运动记录App定制独特的视觉风格。
-
生命周期管理:了解并管理ArkUI页面的生命周期,确保App在不同状态下的行为符合预期。
如果你在开发过程中遇到具体问题,建议查阅HarmonyOS官方文档或参考相关开发教程。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html