> 文档中心 > 『牛角书』基于HarmonyOS的运动手表实战项目

『牛角书』基于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);    }}

倒计时页面如下所示
在这里插入图片描述

结语

通过张荣超老师的视频,学到了这个项目的设计思路,非常感谢张老师的讲解。