『牛角书』基于HarmonyOS的运动手表实战项目
『牛角书』基于HarmonyOS的运动手表实战项目
文章目录
- 『牛角书』基于HarmonyOS的运动手表实战项目
- 前言
- 创建项目
- 创建主页面
-
- index.hml
- index.css
- index.js
- 创建训练页面
-
- xunlian.hml
- xunlian.css
- xunlian.js
- 创建倒计时页面
-
- daojishi.hml
- daojishi.css
- daojishi.js
- 结语
前言
张荣超老师的从零开始编写一个HarmonyOS(鸿蒙)运动手表上的实战项目App视频,简单易懂,对于初学者有很大的帮助。
创建项目
在课程中我们所用到的软件为DevEco Studio下载地址如下:https://developer.harmonyos.com/cn/develop/deveco-studio#download
首先打开DevEco Studio,点击Create HarmonyOS Project,选择Lite Wearable选项,选择默认的模板(如图),点击next,然后将文件命名为huxi。
创建主页面
打开index文件夹,里面有三个文件,文件名分别为index.css,index.hml,index.js。我们要完成对主页面的修改就是通过修改这三个文件来完成的。
index.hml
首先创建一个名为container1的区域组件,组件包含一个名为container2的区域组件和一个按钮组件。在container2组件创建一个名为pv1 的滚动选择器,里面的数据picker1range通过动态绑定的方式获得;创建一个名为changeAction1的onchange事件,当滚动选择器中的数据发生改变的时候,会引发onchange事件;创建一个文本组件,使其显示左边滚动选择器的数据的单位:分;创建一个名为img的图片组件,src为图片的地址;创建一个名为pv2 的滚动选择器,里面的数据picker2range通过动态绑定的方式获得;创建一个名为changeAction2的onchange事件,当滚动选择器中的数据发生改变的时候,会引起onchange事件;创建一个名为btn的按钮组件,按钮上显示的文字为点击开始,点击按钮会引发clickAction事件。
代码如下:
<div class="container1"> <div class="container2"> <picker-view class="pv1" range="{{picker1range}}" selected="1" onchange="changeAction1"/> <text class="txt"> 分 </text> <image src="/common/hm.png" class="img"/> <picker-view class="pv2" range="{{picker2range}}" selected="1" onchange="changeAction2"/> </div> <input type="button" value="点击开始" class="btn" onclick="clickAction"/></div>
index.css
设计index中组件的样式
设计container1组件的样式,使在.container1内的元素横向排列、居中对齐规定.container1的范围。Container1在手表上最多显示的范围就宽:454px,高:454px。
设计container2组件的样式,使在.container2的元素竖向排列,元素居中对齐
令.container2组件的外上边距为50px,高度和宽度为250px,454px;
设计滚动选择器pv1的样式、高度和宽度
设计txt组件的样式、高度和宽度使txt组件的文本居中对齐
设计滚动选择器pv2的样式、高度和宽度
设计图片组件的样式,高度、宽度、左边距和右边距
设计按钮组件的样式、高度、宽度背景颜色、边框的颜色和字体的大小
代码如下:
.container1 { flex-direction: column; justify-content: center; align-items: center; width: 454px; height: 454px;}.container2{ flex-direction: row; justify-content: center; align-items: center; margin-top: 50px; width: 454px; height: 250px;}.txt{ text-align: center; width: 50px; height: 36px;}.pv1{ width: 30px; height: 250px;}.pv2{ width: 80px; height: 250px;}.img{ width: 208px; height: 208px; margin-left: 15px; margin-right: 15px;}.btn{ width: 200px; height: 100px; font-size: 38px; background-color: #000000; border-color: #000000;}
index.js
导入要用到的router,声明变量picker1value和picker2value,并初始化变量,把数据赋予动态绑定的两个滚动选择器中的数据,点击按钮所引发的事件,点击按钮会通过router.replace跳转到倒计时页面,并把picker1value和picker2value的值通过字典的形式传递到训练页面当滚动选择器pv1和滚动选择器pv2的值发生改变时所引发的事件。当滚动器的值发生改变时通过pv.newValue获得改变后的值,并将其给picker1value或者picker2value
这样我们就完成了主页面的所有功能。
代码如下:
import router from '@system.router'var picker1value = null;var picker2value = null; export default { data: { picker1range: ["1", "2", "3"], picker2range: ["较慢", "舒缓", "较快"] }, clickAction() { router.replace({ uri: 'pages/daojishi/daojishi', params:{"data1":picker1value,"data2":picker2value} }); }, changeAction1(pv) { console.log("左边的选中项:" + pv.newValue); picker1value = pv.newValue; }, changeAction2(pv) { console.log("右边的选中项:" + pv.newValue); picker2value = pv.newValue; }}
主页面如下图所示
创建训练页面
xunlian.hml
创建一个叫.container的区域组件,组件包含一个图片组件,一个叫txt1文本组件,一个叫txt2的文本组件,一个按钮。通过动态绑定的方式设定图片的动画效果,animation-duration的作用为定义动画完成一个周期需要的时间,具体的时间由index.js中this.duration得到,通过动态绑定的方式设定图片动画播放的次数,具体的次数由index.js中的this.count得到;在txt1文本组件中,里面的文字通过动态绑定的方式由index.js中得到;在txt2文本组件中,通过show="{{isshow}}来控制txt2的显示与否,txt2的内容为再坚持的秒数,具体的时间由index,js中的this.seconds得到;在按钮组件中,按钮的名字被赋为点击重新开始,class="btn"设置一个叫btn的组件,并创建一个叫onclick的点击事件(当按钮被点击的时候会发生该事件)
代码如下(示例):
<div class="container"> <image class="img" src="/common/hm.png" style="animation-duration: {{duration}}; animation-iteration-count: {{count}};" /> <text class="txt1"> {{huxi}}({{percent}}%) </text> <text class="txt2" show="{{isshow}}"> 再坚持 {{seconds}} 秒 </text> <input type="button" value="点击重新开始" class="btn" onclick="clickAction"/></div>
xunlian.css
设计index中组件的样式
设计container组件的样式,使在.container内的元素横向排列、居中对齐规定.container的范围。Container在手表上最多显示的范围就宽:454px,高:454px。
设计txt1组件的样式、高度和宽度使txt1组件的文本居中对齐
设计txt2组件的样式、高度和宽度使txt2组件的文本居中对齐
为图片的@keyframes动画指定名称,设定动画效果,把图片由顺时针旋转一圈
设计图片组件的样式,高度、宽度、下边距
设计按钮组件的样式、高度、宽度背景颜色、边框的颜色和字体的大小
代码如下(示例):
<div class="container1"> <div class="container2"> <picker-view class="pv1" range="{{picker1range}}" selected="1" onchange="changeAction1"/> <text class="txt"> 分 </text> <image src="/common/hm.png" class="img"/> <picker-view class="pv2" range="{{picker2range}}" selected="1" onchange="changeAction2"/> </div> <input type="button" value="点击开始" class="btn" onclick="clickAction"/></div>
xunlian.js
引入router,声明要用到的变量并初始化变量,把xunlian.hml所用到的动态绑定的变量置为0,清除计时器time1,time2,time3,并把timer1,timer2,timer3置为初始量。跳转到pages目录下的index页面,也就是主页面。计时器timer1运行时的运行的函数,使this.seconds的值递减,当this.seconds的值减为0时,清除计时器timer1,并把变量timer1的值重置,把this.isshow的值赋予false,即使组件.txt2中的内容改为不可见。计时器timer2运行时运行的函数,使变量counter递增,如果counter的值等于吸气和呼气的总次数的话,清除定时器timer2,并重置timer2的值,并把在txt2组件显示的文字改为已完成。如果不是,就把txt2组件的文字从呼气改为吸气,或者从吸气改为呼气。计时器timer3运行时运行的函数,把this.percent的值取整再加一并转换为字符串并重新赋予this.percent(this.percent即在txt2组件中显示百分比的动态绑定变量)。如果this.percnet的值小于10,就在显示的数字前加0。如果this.percent的值等于100,就使其重新置为零。如果计时器timer2=null,就清除计时器timer3,并重置timer3的值,把this.percent的值改为100。把从主页面传递过来的两个值this.key1(选择训练的时间),this.key2(选择训练的强度)赋值给picker1value,picker2value。如果pikcer1value的值为不同的分钟,就把对应分钟的秒数赋予给picker1seconds。如果picker2value的值为不同的强度,就把对应强度的数字赋予picker2seconds。
把picker1seconds赋予在txt2组件动态绑定的seconds(即显示训练剩下的秒数)
把picker2seconds赋予为图片组件动画完成一个周期的时间
把(picker1seconds/picker2seconds)的值赋予图片组件的动画播放的次数
代码如下:
import router from '@system.router'var picker1value = null;var picker2value = null;var picker1seconds = null;var picker2seconds = null;var timer1 = null;var timer2 = null;var timer3 = null;var counter = 0;export default { data: { seconds:0, isshow: true, huxi:"吸气", percent:"0", duration: "", count: "" }, clickAction(){ clearInterval(timer1); timer1 = null; clearInterval(timer2); timer2 = null; clearInterval(timer3); timer3 = null; router.replace({ uri: 'pages/index/index' }) }, run1(){ this.seconds --; if(this.seconds == 0){ clearInterval(timer1); timer1 = null; this.isshow = false; } }, run2(){ counter++; if (counter == picker1seconds / picker2seconds) { clearInterval(timer2); timer2 = null; this.huxi = "已完成"; }else { if (this.huxi == "吸气") { this.huxi = "呼气" } else if (this.huxi == "呼气") { this.huxi = "吸气" } } }, run3(){ this.percent = (parseInt(this.percent) + 1).toString(); if(parseInt(this.percent) < 10){ this.percent = "0" +this.percent; } if(parseInt(this.percent) == 100){ this.percent = "0"; } if(timer2 == null){ clearInterval(timer3); timer3 = null; this.percent = "100"; } }, onInit(){ console.log("训练页面的onInit()被调用"); console.log("接收到的左边选择器的值:" +this.data1); console.log("接收到的右边选择器的值:" +this.data2); picker1value = this.key1; picker2value = this.key2; if (picker1value == "1") { picker1seconds = 60; }else if (picker1value == "2") { picker1seconds = 120; }else if(picker1value == "3"){ picker1seconds = 180; } if(picker2value == "较慢"){ picker2seconds = 6; }else if(picker2value == "舒缓"){ picker2seconds = 4; }else if(picker2value == "较快"){ picker2seconds = 2; } this.seconds = picker1seconds; this.duration = picker2seconds + "s"; this.count = (picker1seconds / picker2seconds).toString(); }, onReady(){ console.log("训练面的onReady()被调用"); }, onShow(){ console.log("训练面的onShow()被调用"); timer1 = setInterval(this.run1,1000); timer2 = setInterval(this.run2,picker2seconds*1000); timer3 = setInterval(this.run3,picker2seconds / 100 *1000); }, onDestory(){ console.log("训练面的onDestory()被调用"); }}
训练页面如下图所示
创建倒计时页面
daojishi.hml
创建一个区域组件container,组件中包含一个图片组件,图片地址通过动态绑定的方式获得;一个文本组件,内容是显示“请保持静止”;一个文本组件,内容为多少秒后跟随训练指引,秒数通过动态绑定的方式获得;创建一个文本组件,内容是显示“进行吸气和呼气”
<div class="container"> <image class="img" src="{{imgsrc}}"/> <text class="txt"> 请保持静止 </text> <text class="txt"> {{seconds}}秒后跟随训练指引 </text> <text class="txt"> 进行吸气和呼气 </text></div>
daojishi.css
设计container组件的样式,使在.container1内的元素横向排列、居中对齐规定.container的范围。Container在手表上最多显示的范围就宽:454px,高:454px。
设计图片组件的样式,高度、宽度、下边距
设计txt组件的样式、高度和宽度使txt组件的文本居中对齐
.container { flex-direction: column; justify-content: center; align-items: center; width: 454px; height: 454px;}.img{ width: 100px; height: 100px; margin-bottom: 30px;}.txt { font-size: 38px; text-align: center; width: 484px; height: 50px; margin-top: 10px;}
daojishi.js
引入router,声明要用到的变量,并初始化它们。初始化在doajishi.hml中动态绑定的变量,计时器timer运行时运行的函数,当函数运行时,计数的变量counter递减,如果counter的值不为0,那么换下张图片,把counter赋予动态绑定的变量sconds。如果counter等于0,把图片地址置为空,秒数也置为空,清除计时器timer,并重置变量timer,页面跳转到训练页面,并把pv1和pv2 的值也传递过去。onInit()为在页面数据准备时被引发的事件,把从主页面传递过来的值分别赋予pv1和pv2。把counter字符化赋予imgsrc,以实现更换图片的作用,把counter字符化赋予this,seconds。onShow()事件为页面显示时所引发的事件,当页面显示时,触发定时器timer1,每过1毫秒运行一次run()函数。
import router from '@system.router'var counter = 3;var timer = null;var pv1 = null;var pv2 = null;export default { data: { imgsrc: "", seconds: "" }, run(){ counter = counter - 1; if(counter != 0){ this.imgsrc = "/common/" + counter.toString() + ".png"; this.seconds = counter.toString(); }else{ this.imgsrc = ""; this.seconds = ""; clearInterval(timer); timer = null; router.replace({ uri: 'pages/xunlian/xunlian', params: {"key1": pv1,"key2": pv2} }); } }, onInit(){ pv1 = this.data1; pv1 = this.data1; this.imgsrc = "/common/" + counter.toString() + ".png"; this.seconds = counter.toString(); }, onShow(){ timer = setInterval(this.run,1000); }}
倒计时页面如下所示
结语
通过张荣超老师的视频,学到了这个项目的设计思路,非常感谢张老师的讲解。