HarmonyOS 应用开发学习指南

科技   2024-11-04 08:32   福建  
  
  1. HarmonyOs概述

HarmonyOs是一款面向万物互联时代的、全新的分布式操作系统

在传统的单设备系统能力基础上,HarmonyOs提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持手机、平板、智能穿戴、智慧屏、车机、PC、智能音箱、耳机、AR/VR眼镜等多种终端设备,提供全场景(移动办公、运动健康、社交通信、媒体娱乐等)业务能力。

HarmonyOS有三大特征:

  • 搭载该操作系统的设备在系统层面融为一体、形成超级终端,让设备的硬件能力可以弹性扩展,实现设备之间硬件互助,资源共享,对消费者而言,HarmonyOs能够将生活场景中的各类终端进行能力整合,实现不同终端设备之间的快速连接、能力互助、资源共享,匹配合适的设备、提供流畅的全场景体验。

  • 面向开发者,实现一次开发,多端部署。

对应用开发者而言,HarmonyOs采用了多种分布式技术,使应用开发与不同终端设备的形态差异无关,从而让开发者能够聚焦上层业务逻辑,更加便捷、高效地开发应用。

  • 一套操作系统可以满足不同能力的设备需求,实现统一0S,弹性部署。

对设备开发者而言,HarmonyOs采用了组件化的设计方案,可根据设备的资源能力和业务特征灵活裁剪,满足不同形态终端设备对操作系统的要求。

HarmonyOs提供了支持多种开发语言的API,供开发者进行应用开发。支持的开发语言包括ArkTS、JS(JavaScript)、C/C++、java.

1月18日,华为宣布HarmonyOS NEXT鸿蒙星河版开发者预览面向开发者开放申请。HarmonyOS NEXT命名为鸿蒙星河版,这一版本的推出意味着鸿蒙将去除安卓AOSP代码走向“独立”也就是“纯血版鸿蒙”。

2024年,鸿蒙Harmony os有望取代苹果ios,成为中国市场上第二大智能手机操作系统。

但是鸿蒙的意义不止于手机操作系统,而是开启万物智联时代的“钥匙”。

2.DevEco Studio的使用

在HarmonyOs应用开发学习之前,需要进行一些准备工作,首先需要完成开发工具DevEco Studio的下载与安装以及环境配置。

2.1 安装DevEco Studio

进入DevEco Studio下载官网,下滑找到如下页面内容。DevEco Studio提供了Windows版本和Mac版本选择,可以根据操作系统选择对应的版本进行下载。

这里以window为例进行安装

下载完成后,解压文件,双击下载"devecostudio-windows-xxxx.exe",进入DevEco Studio安装向导,点击“Next”

在如下界面选择安装路径,默认安装于"c:\program Files"下,也可以单独"Browse..."指定其他路径安装,然后点击"Next"。注意:安装路径不要出现中文。

如下安装选项界面勾选DevEco Studio后,单击"Next",直接安装完成

点击Install

进入DevEco Studio安装。

安装完成以后,单击“Finish”完成安装

2.2 配置环境

双击已安装的DevEco Studio快捷方式进入配置页面,IDE会进入配置向导,选择Agree,同意相应的条款,进入配置页。

选择不导入设置,点击ok

进入DevEco Studio配置页面,首先需要进行基础配置,包括Nodejs与Ohpm的安装路径设置,选择从

华为镜像下载至合适的路径。

点击Next进入SDK配置,设置为合适的路径

点击Next会显示"SDK Licence Agreement",阅读 相关协议后,勾选“Accept”点击“Next”

点击“Next”进行配置项预览页,进行配置项的确认.

确定完成后,单击“next”进行下一步

配置成功如图

如果出现配置异常解决方案

//清除npm缓存
npm cache clean --force
//将镜像源设置为华为镜像源
npm config set registry https://mirrors.huaweicloud.com/repository/npm/

等待配置自动下载完成,完成后,单击“Finish”,IDE会进入欢迎页,我们页就成功配置好了开发环境。

准备工作完成后,接下来将进入DevEco Studio进行工程创建和运行。

3.创建HelloWorld工程目录

双击DevEco Studio,首次打开DevEco Studio,那么进入的页面为

在欢迎页单击Create Project,进入项目创建页面,选择第一个空模板,选择以后点击Next

进入到项目配置目录,配置好了以后点击finish

4.运行HelloWorld工程

进入IDE后,我们首先了解一下基础的界面,整个IDE的界面大致上分为4个部分,分别是代码编辑区,通知栏,工程目录区,预览区

注意:

这里的预览区需要点击Previewer,首次打开如果提示

需要点击File->Settings->Build,Execution,Deployment->Build Tools->Hvigor,取消勾选最后一项,点击ok,最后Build-clean Project 预览成功

4.1 工程目录区

工程目录区的内容是我们创建的整个项目工程,包括项目的配置具体的文件,项目对应的依赖包,等等。

4.2 代码编辑区

  • 可以打开/切换我们需要编辑的文件

  • 按住ctrl可以进行缩放大小

  • 可以在文件中进行具体内容的编辑

  • 使用ctrl+s进行保存。

4.3 预览区

需要选择ets文件,右侧会出现 Previewer,需要点击Previewer进行预览效果,默认为移动端,竖屏。

4.3.1 横竖屏幕切换

点击图标切换

会显示横屏幕屏幕,再次点击,会显示竖屏

4.3.2 不同设置预览设置

点击按钮

可以看到设备列表

如果需要配置可以将整个按钮选择

点击编辑按钮

可以设置移动端参数

再次点击可取消。

4.3.4 组件预览界面

点击按钮Tt

右侧会出现组件预览界面,选择组件,会选中编辑页对应的代码

关闭点击眼睛按钮

4.3.4 自动同步效果

点击按钮Live preview,将会关闭 Live preview。

如果出现上图标识,表示编辑器更改代码,预览区不会同步更新。我们使用的时候建议打开live preview,实现同步更新。

4.4 通知栏

  • 通知栏也叫信息栏。

  • run表示项目运行的信息栏。

  • problems 当前错误,提醒信息栏

  • terminal 命令行终端

  • log 模拟器真机运行的日志通知栏

  • proviewerLog 预览器日志通知栏

4.5 使用模拟器,运行Hello World

安装模拟器

使用模拟器运行应用

1.单击File > Settings > SDK(macOS系统为DevEco Studio > Preferences > SDK下拉框选择HarmonyOS,勾选并下载Platforms下的System-image和Tools下的Emulator资源。

图1 下载System-image资源

图2 下载Emulator资源

2.单击菜单栏的Tools > Device Manager,在Local Emulator页签,单击Edit设置本地模拟器的存储路径Local emulator location,默认存储在C:\Users\Users\AppData\Local\Huawei\HarmonyOSEmulator\deployed目录(macOS为:/Users/用户/.Huawei/HarmonyOSEmulator/deployed)。

3.Local Emulator页签中,单击右下角的New Emulator按钮,创建一个本地模拟器。

4.在创建模拟器界面,可以选择一个默认的设备;同时也可以单击New Hardware或默认设备后的克隆图标,添加一个新设备,以便自定义设备的相关参数,如尺寸、分辨率、内存等参数。

创建New Hardware时,可以修改设备的名称、尺寸、分辨率、内存等参数。

5.选择需要创建的Hardware,单击Next,可以看到模拟器的镜像信息,如API、Version、CPU/ABI等信息。

6.单击Next,核实确定需要创建的模拟器信息,同时也可以在该界面修改模拟器信息,然后单击Finish创建本地模拟器

7.在设备管理器页面,单击启动模拟器。

8.单击DevEco Studio的Run > Run'模块名称',或使用默认快捷键Shift+F10(macOS为Control+R)。

9.DevEco Studio会启动应用/服务的编译构建,完成后应用/服务即可运行在Local Emulator上。首次打开运行速度很慢。需要耐心等待。

5.HelloWorld工程目录

工程的目录结构如下

其中详细如下:

  • Appscope中存放应用全局所需要的资源文件。

  • entry是应用的主模块,存放HarmonyOs应用的代码、资源等,

  • oh_modules是工程的依赖包,存放工程依赖的源文件。

  • build-profile.json5是工程级配置信息,包括签名、产品配置等。

  • hvigorfile.ts是工程级编译构建任务脚本,hvigor是基于任务管理机制实现的一款全新的自动化构建工具,主要提供任务注册编排,工程模型管理、配置管理等核心能力。

  • oh-package.json5是工程级依赖配置文件,用于记录引入包的配置信息。

5.1 Appscope

在AppScope,其中有resources文件夹和配置文件app.json5

  • app.json5:配置信息文件

  • resources文件夹:资源文件夹

AppScope>resources>base中包含element和media两个文件夹

  • 其中elemment文件夹主要存故公共的字符串、布局文件等资源.

  • media存放全局公共的多媒体资源文件。

5.2 entry

entry>src目录中主要包含总的main文件夹,单元测试目录ohosTest,以及模块级的配置文件。

  • main文件夹中,ets文件夹用于存放ets代码,resources文件存放模块内的多媒体及布局文件等,module.json5文件为模块的配置文件。

  • ohosTest是单元测试目录。

  • build-profile.json5是模块级配置信息,包括编译构建配置项

  • hvigorfile.ts文件是模块级构建脚本。

  • oh-package.json5是模块级依赖配置信息文件。

进入src>main>ets目录中,其分为entryability、pages两个文件夹。

  • entryability存放ability文件,用于当前ability应用逻辑和生命周期管理。

  • pages存放UI界面相关代码文件,初始会生成一个Index页面.

resources目录下存放模块公共的多媒体、字符串及布局文件等资源,分别存放在element、media文件

夹中。

5.3 app.json5

AppScope>app.json5是应用的全局的配置文件,用于存放应用公共的配置信息。

其中配置信息如下:

  • bundleName是包名。

  • vendor是应用程序供应商。

  • versionCode是用于区分应用版本。

  • versionName是版本号

  • icon对应于应用的显示图标。

  • label是应用名。

5.4 module.json5

entry>src>main>module.json5是模块的配置文件,包含当前模块的配置信息。

其中module对应的是模块的配置信息,一个模块对应一个打包后的hap包,hap包全称是HarmonyOsAbility Package,其中包含了ability、第三方库、资源和配置文件。其具体属性及其描述可以参照下表1

属性

描述

name

该标签标识当前module的名字,module打包成hap后,表示hap的名称,标签值采用字符串表示(最大长度31个字节),该名称在整个应用要唯一。

type

表示模块的类型,类型有三种,分别是entry、feature和har。

srcEntry

当前模块的入口文件路径。

description

当前模块的描述信息。

mainElement

该标签标识hap的入口ability名称或者extension名称,只有配置为mainElement的ability或者extension才允许在服务中心露出。

deviceTypes

该标签标识hap可以运行在哪类设备上,标签值采用字符串数组的表示。

deliveryWithinstall

标识当前Module是否在用户主动安装的时候安装,表示该Module对应的HAP是否跟随应用一起安装。

-true:主动安装时安装。-false:主动安装时不安装。

installationFree

标识当前Module是否支持免安装特性。-true:表示支持免安装特性,且符合免安装约束。-false:表示不支持免安装特性。

pages

对应的是main_pages.json文件,用于配置ability中用到的page信息

abilities

是一个数组,存放当前模块中所有的ability元能力的配置信息,其中可以有多个ability。

对于abilities中每一个ability的属性项,其描述信息如下表2.

属性

描述

name

该标签标识当前ability的逻辑名,该名称在整个应用要唯一,标签值采用字符串表示(最大长度127个字节)。

srcEntry

ability的入口代码路径,

description

ability的描述信息。

icon

ability的图标。该标签标识ability图标,标签值为资源文件的索引。该标签可缺省,缺省值为空。如果ability被配置为MainElement,该标签必须配置。

label

ability的标签名。

startWindowlcon

启动页面的图标。

startWindowBackground

启动页面的背景色。

visible

ability是否可以被其他应用程序调用,true表示可以被其它应用调用,false表示不可以被其它应用调用,

skills

标识能够接收的意图的action值的集合,取值通常为系统预定义的action值,也允许自定义。

entities

标识能够接收Want的Entity值的集合。

actions

标识能够接收的Want的Action值的集合,取值通常为系统预定义的action值,也允许自定义。

5.5 main_pages.json

src/main/resources/base/profile/main_pages.json文件保存的是页面page的路径配置信息,所有需要进行路由跳转的page页面都要在这里进行配置。多页面展示需要在里面进行配置。

6.ArkTs的基本组成

6.1 初识ArkTs语言

ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScrip(简称TS)生态基础上做了进一步扩展,继承了TS的所有特性,是TS的超集。因此,在学习ArkTS语言之前,建议开发者具备TS语言开发能力。

当前,ArkTS在TS的基础上主要扩展了如下能力:

  • 基本语法:ArKTS定义了声明式UI描述、自定义组件和动态扩展UI元素的能力,再配合ArkUl开发框架中的系统组件及其相关的事件方法、属性方法等共同构成了Ui开发的主体。

  • 状态管理:ArkTS提供了多维度的状态管理机制。在UI开发框架中,与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活地利用这些能力来实现数据和UI的联动。

  • 渲染控制:ArkTS提供了渲染控制的能力。条件染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。数据懒加载从数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。

未来,ArKTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、系统类型增强、分布式开发范式等更多特性。

6.2 基本语法

基本语法概述

在初步了解了ArkTS语言之后,我们以一个具体的示例来说明ArkTS的基本组成


  • 装饰器: 用于装饰类、结构、方法以及变量,并赋予其特殊的含义。如上述示例中@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口组件,@State表示组件中的状态变量,状态变量变化会触发UI刷新。

  • UI描述:以声明式的方式来描述UI的结构,例如build()方法中的代码块。

  • 自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Hello。

  • 系统组件:ArkUI框架中默认内置的基础容器组件,可直接被开发者调用,比如示例中的Column、Text、Divider、Button。

  • 属性方法:组件可以通过链式调用配置多项属性,如fontSize()、width()、height()、backgroundColor()等。

  • 事件方法:组件可以通过链式调用设置多个事件的响应逻辑,如跟随在Button后面的onClick()。

  • 系统组件、属性方法、事件方法具体使用可参考基于ArkTS的声明式开发范式

除此之外,ArkTS扩展了多种语法范式来使开发更加便捷:

  • @Builder/@BuilderParam:特殊的封装UI描述的方法,细粒度的封装和复用UI描述。

  • @Extend/@Styles:扩展内置组件和封装属性样式,更灵活地组合内置组件。

  • stateStyles:多态样式,可以依据组件的内部状态的不同,设置不同样式。

实例:

//@Entry装饰器 入口组件
@Entry
//@Component装饰器 自定义组件(一个一个独立的模块)
@Component
struct Index { //自定义组件 Index组件的名称
//@State 装饰器 状态变量
//@State 装饰器 message(变量): string(类型) = 'World'(值)
@State message: string = 'World'

//UI描述
build() {
Row() {//容器组件:水平方向布局容器
Column() {//容器组件:垂直方向布局容器
Text(`Hello ${this.message}`)//基础组件:文本
.fontSize(50)//属性:文字大小
.fontWeight(FontWeight.Bold)//属性:文字加粗
Divider() //基础组件:分割线
Button('click me',{type:ButtonType.Normal})
.onClick(()=>{//事件方法
this.message = "ArkUi"
})
.height(50)
.width(100)
.backgroundColor('red')
.margin({top:20})
}
.width('100%')
}
.height('100%')
}
}

7.ArkTs声明式UI描述

7.1 创建组件

声明式UI描述

ArKTS以声明方式组合和扩展组件来描述应用程序的UI,同时还提供了基本的属性、事件和子组件配置方法,帮助开发者实现应用交互逻辑

创建组件

根据组件构造方法的不同,创建组件包含有参数和无参数两种方式,

无参数

如果组件的接口定义没有包含必选构造参数,则组件后面的"0"不需要配置任何内容。例如,Divider组件不包含构造参数:

Column() {
//文字组件 无参数
Text()
//分割线组件 无参数
Divider()
}

有参数

如果组件的接口定义包含构造参数,则在组件后面的“()“配置相应参数。

  • Image组件的必选参数src。

 //图片组件,必须要参数,否则报错
Image('https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png')
  • Text组件的非必选参数content。

//文字组件  有参数
Text('HELLO')
  • 变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求。

例如,设置变量或表达式来构造lmage和Text组件的参数。

@Entry
@Component
struct Index {
@State title:string ="你好!";
img_src:string= 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png';
public p:string ='p'; //公共的
private n:number=10; //私有的
protected flag:boolean = true; //受保护的
//UI描述
build() {
Row() {//容器组件:水平方向布局容器
Column() {//容器组件:垂直方向布局容器
Text(`${this.title}${this.n}`);
Image(this.img_src);
}
.width('100%')
}
.height('100%')
}
}

7.2 配置属性

属性方法以".“链式调用的方式配置系统组件的样式和其他属性,建议每个属性方法单独写一行

  • 配置Text组件的字体大小。

Text('text')
.fontSize(30)
  • 配置组件的多个属性

Image(this.img_src)
.width(300)
.height(100)
  • 除了直接传递常量参数外,还可以传递变量或表达式

Text('he11o')
.fontSize(this.size)
Image('test.jpg')
.width(this.count%2===0?100:200)//表达式定义宽度
.height(this.offset + 100)
  • 对于系统组件,ArkUl还为其属性预定义了一些枚举类型供开发者调用,枚举类型可以作为参数传递,但必须满足参数类型要求。

例如,可以按以下方式配置Text组件的颜色和字体样式

Text('he11o')
.fontsize(20)
.fontcolor(color.Red)//枚举类型定义颜色
.fontweight(fontweight.Bo1d)//枚举类型加粗

7.3 配置事件

事件方法以”.“链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。

  • 使用箭头函数配置组件的事件方法。

Button('click me')
//箭头函数配置事件,点击按钮改变title变量的值
.onClick(()=>{
this.title = '按钮点击了'
})
  • 使用匿名函数表达式配置组件的事件方法,要求使用bind,以确保函数体中的this指向当前组件

Button('click me')

// 普通函数配置事件,this指向不对会直接写报错,所以需要设置bind属性
.onClick(function(){
this.title = "标题点击"
}.bind(this))

使用组件的成员函数配置组件的事件方法。

普通函数

//函数的定义
myHandler():void{
this.title = "myHandler"
}
...
.onClick(this.myHandler.bind(this))

箭头函数

//函数的定义
myHandler2 = ():void=>{
this.title = "myHandler2"
}
...
.onClick(this.myHandler2)

8.ArkUI-容器组件

8.1 ROW组件

沿水平方向布局容器。可以包含子组件。

Row(value?:{space?:string | number})

参数:

参数名

参数类型

必填

默认值

参数描述

space

string | number

0

横向布局元素间距

属性:

名称

参数类型

默认值

描述

alignItems

VerticalAlign

VerticalAlign.Center

在垂直方向上子组件的对齐格式,

justifycontent

FlexAlign

FlexAlign.Start

设置子组件在水平方向上的对齐格式。

示例:

//@Entry装饰器 入口组件
@Entry
//@Component装饰器 自定义组件(一个一个独立的模块)
@Component
struct Index { //自定义组件 Index组件的名称
@State title: string = "你好!";
build(){
//build中的row组件只能写一个,多个写在row内部
// {space:10} 子组件的间距
Row({space:10}){
//添加其他组件
// Button('1')
// Button('2')
// Button('3')
// 嵌套Row()
Row()
.width('30%')
.height(50)
.backgroundColor('red')
Row()
.width('30%')
.height(50)
.backgroundColor('green')
Row()
.width('30%')
.height(50)
.backgroundColor('blue')
}
.width('100%')
.height(200)
.border({width:2})
.alignItems(VerticalAlign.Top)
//垂直方向的对齐格式VerticalAlign.top /VerticalAlign.center /VerticalAlign.bottom
.justifyContent(FlexAlign.SpaceEvenly)
//水平方向的对齐方式
// FlexAlign.Start FlexAlign.center FlexAlign.End 左中右
//FlexAlign.SpaceBetween 两端对齐
//FlexAlign.SpaceAround 左右间距相同
//FlexAlign.SpaceEvenly 间隔相同
}
}

8.2 Column组件

沿垂直方向布局的容器。可以包含子组件。

Column(value?:{space?:string | number})

参数:

参数名

参数类型

必填

默认值

参数描述

space

string | number

0

纵向布局元素间距

属性:

名称

参数类型

默认值

描述

alignltems

HorizontalAlign

VerticalAlign.Center

设置子组件在水平方向上的对齐格式

justifyContent8+

FlexAlign

FlexAlign.Start

设置子组件在垂直方向上的对齐格式

示例:

@Entry //@Entry装饰器 入口组件
@Component ///@Component装饰器 自定义组件
struct Index { //自定义组件 Index组件的名称
@State title: string = "hello world";
//UI描述
build(){
Column({space:10}){//space:10 设置子组件的间距
//添加子组件
Button('click1')
Button('click2')
Button('click3')
}
.width('100%')
.height(200)
.border({width:2})
// 添加对齐方式(水平方向对齐)
// 左对齐
.alignItems(HorizontalAlign.Start)
// 居中对齐(默认值)
// .alignItems(HorizontalAlign.Center)
// 右对齐
// .alignItems(HorizontalAlign.End)
//添加对齐方式-垂直方向对齐
// 上对齐
.justifyContent(FlexAlign.Start)
// 居中对齐
.justifyContent(FlexAlign.Center)
// 下对齐
.justifyContent(FlexAlign.End)
// 两端对齐
.justifyContent(FlexAlign.SpaceBetween)
// 元素左右外边距相等
.justifyContent(FlexAlign.SpaceAround)
// 元素所有间距相同
.justifyContent(FlexAlign.SpaceEvenly)
}
}

8.3 Flex组件

以弹性方式布局子组件的容器组件。

子组件 可以包含子组件。

接口
Flex(value?:{ direction?: FlexDirection, wrap?: Flexwrap, justifycontent?.FlexAlign,alignItems?: ItemAlign,aligncontent?: FlexAlign })

参数:

参数名

参数类型

必填

默认值

参数描述

direction

FlexDirection

FlexDirection.Row

子组件在Flex容器上排列的方向,即主轴的方向。

wrap

FlexWrap

FlexWrap.NoWrap

Flex容器是单行/列还是多行列排列。

justifyContent

FlexAlign

FlexAlign.Start

子组件在Flex容器主轴上的对齐格式。

alignltems

ItemAlign

ltemAlign.Stretch

子组件在Flex容器交叉轴上的对齐格式。

alignContent

FlexAlign

FlexAlign.Start

交叉轴中有额外的空间时多行内容的对齐方式。仅在wrap为Wrap或WrapReverse下生效

8.3.1 direction

设置子组件在flex容器中的排列方式

语法:

 Flex({direction:FlexDirection.属性值}){

}

属性值:

Row:水平方向排列

Column:垂直方向排列

RowReverse:水平方向逆向排列

ColumnReverse:垂直方向逆向排列

示例:

@Entry //@Entry装饰器 入口组件
@Component ///@Component装饰器 自定义组件
struct Index { //自定义组件 Index组件的名称
@State title: string = "hello world";
//UI描述
build(){
Column({space:10}) {
//子组件在Flex容器上排列的方向,即主轴的方向
// direction
// direction:FlexDirection.Row 横向排列
// direction:FlexDirection.Column 纵向排列
Flex({direction:FlexDirection.Row}){
Text('1')
.width('20%')
.height(50)
.backgroundColor('red')
Text('2')
.width('20%')
.height(50)
.backgroundColor(0x904523)
Text('3')
.width('20%')
.height(50)
.backgroundColor('green')
Text('4')
.width('20%')
.height(50)
.backgroundColor(0x123458)
}
.width('90%')
.height(100)
// 单词
.backgroundColor('red')
// #16进制
.backgroundColor('#F00')
// 0X + 16进制
.backgroundColor(0X0F0)
}
.width('100%')
.margin({top:10})
}
}

8.3.2 wrap

设置子组件在flex容器中的排列方式

语法:

Flex({wrap:FlexWrap.属性值}){

}

属性值:

Warp:换行

NoWarp:不换行

WrapReverse 换行逆向

示例:

@Entry //@Entry装饰器 入口组件
@Component ///@Component装饰器 自定义组件
struct Index { //自定义组件 Index组件的名称
@State title: string = "hello world";
//UI描述
build(){
Column({space:10}) {
// warp 设置flex容器单行/多行排列
// wrap:FlexWrap.Wrap 换行
//wrap:FlexWrap.NoWrap 不换行
// wrap:FlexWrap.WrapReverse 换行逆向
Flex({wrap:FlexWrap.WrapReverse}){
Text('1')
.width('50%')
.height(50)
.backgroundColor('red')
Text('2')
.width('20%')
.height(50)
.backgroundColor(0x904523)
Text('3')
.width('20%')
.height(50)
.backgroundColor('green')
Text('4')
.width('20%')
.height(50)
.backgroundColor(0x123458)
}
.width('90%')
.height(100)
// 单词
.backgroundColor('red')
// #16进制
.backgroundColor('#F00')
// 0X + 16进制
.backgroundColor(0X0F0)
}
.width('100%')
.margin({top:10})
}
}

8.3.3 justifyContent

设置子组件在flex容器中的主轴的对齐方式

语法:

Flex({justifyContent:FlexAlign.属性值}){

}

属性值:

  • Start 顶部对齐

  • Center 居中对齐

  • End 尾部对齐

  • SpaceBetween 两端对齐

  • SpaceAround 左右外边距相同

  • SpaceEvenly 间距相同

示例:

@Entry //@Entry装饰器 入口组件
@Component ///@Component装饰器 自定义组件
struct Index { //自定义组件 Index组件的名称
@State title: string = "hello world";
//UI描述
build(){
Column({space:10}) {
// justifyContent 设置flex容器在主轴的对齐方式
// justifyContent:FlexAlign.Start 顶部对齐
// justifyContent:FlexAlign.Center 居中对齐
// justifyContent:FlexAlign.End 尾部对齐
// justifyContent:FlexAlign.SpaceBetween 两端对齐
// justifyContent:FlexAlign.SpaceAround 左右外边距相同
// justifyContent:FlexAlign.SpaceEvenly 间距相同
Flex({justifyContent:FlexAlign.SpaceEvenly}){
Text('1')
.width('20%')
.height(50)
.backgroundColor('red')
Text('2')
.width('20%')
.height(50)
.backgroundColor(0x904523)
Text('3')
.width('20%')
.height(50)
.backgroundColor('green')
Text('4')
.width('20%')
.height(50)
.backgroundColor(0x123458)
}
.width('90%')
.height(100)
// 单词
.backgroundColor('red')
// #16进制
.backgroundColor('#F00')
// 0X + 16进制
.backgroundColor(0X0F0)
}
.width('100%')
.margin({top:10})
}
}

8.3.4 alignltems

设置子组件在flex容器中的交叉轴的对齐方式

语法:

Flex({alignItems:ItemAlign.属性值}){

}

属性值:

  • Start 顶部对齐

  • Center 居中对齐

  • End  底部对齐

  • Stretch 伸展,拉伸到容器尺寸

示例:

@Entry //@Entry装饰器 入口组件
@Component ///@Component装饰器 自定义组件
struct Index { //自定义组件 Index组件的名称
@State title: string = "hello world";
//UI描述
build(){
Column({space:10}) {
// alignltems 设置子组件在flex容器中的交叉轴的对齐方式
// alignItems:ItemAlign.Start 顶部对齐
// alignItems:ItemAlign.Center 居中对齐
// alignItems:ItemAlign.End 底部对齐
// alignItems:ItemAlign.Stretch 伸展,拉伸到容器尺寸
Flex({alignItems:ItemAlign.Stretch}){
Text('1')
.width('20%')
.height(30)
.backgroundColor('red')
Text('2')
.width('20%')
.height(40)
.backgroundColor(0x904523)
Text('3')
.width('20%')
.height(50)
.backgroundColor('green')
}
.width('90%')
.height(100)
// 单词
.backgroundColor('red')
// #16进制
.backgroundColor('#F00')
// 0X + 16进制
.backgroundColor(0X0F0)
}
.width('100%')
.margin({top:10})
}
}

8.4 List

列表包含一系列相同宽度的列表项。适合连续、多行呈现同类数据,例如图片和文本。

子组件
包含ListItem子组件
接口
List(value?:{space?: number | string, initialIndex?: number, scroller?:Scro11er})

8.5 ListItem

8.6 Tabs

通过页签进行内容视图切换的容器组件,每个页签对应一个内容视图。

子组件
包含子组件Tabcontent。

接口说明

Tabs(value?: {barPosition?: BarPosition, index?: number, controller?:Tabscontro1ler})

参数:

参数名

参数类型

必填

默认值

参数描述

barPosition

BarPosition

BarPosition.Start

指定页签位置来创建Tabs容器组件。

index

number

0

指定初次初始页签索引。

controller

TabsController

-

设置Tabs控制器。

BarPosition枚举说明

名称

描述

Start

vertical属性方法设置为true时,页签位于容器左侧;vertical属性方法设置为false时,页签位于容器顶部。

End

vertical属性方法设置为true时,页签位于容器右侧;vertical属性方法设置为false时,页签位于容器底部。

属性

不支持触摸热区设置。

名称

参数类型

默认值

描述

vertical

boolean

false

设置为false是为横向Tabs,设置为true时为纵向Tabs。

scrollable

BarMode

true

设置为true时可以通过滑动页面进行页面切换,为false时不可滑动切换页面。

barMode

BarMode

BarMode.Fixed

TabBar布局模式,具体描述见BarMode枚举说明。

barWidth

Length

-

TabBar的宽度值。

barHeight

Length

-

TabBar的高度值。

animationDuration

number

200

TabContent滑动动画时长,

BarMode枚举说明

名称

描述

Scrollable

TabBar使用实际布局宽度,超过总长度后可滑动。

Fixed

所有TabBar平均分配宽度。

事件

名称

描述

onChange(event: (index: number)=>

void)

Tab页签切换后触发的事件。-index:tab标签的索引值。

TabsController

Tabs组件的控制器,用于控制Tabs组件进行页签切换。

导入对象

controller:TabsController = new TabsController()

changeindex

控制Tabs切换到指定页签

changelndex(value: number): void

8.7 TabContent

仅在Tabs中使用,对应一个切换页签的内容视图。

接口
Tabcontent()

属性

名称

参数类型

描述

tabBar

string | Resource | {icon?:string | Resource,text?:string | Resource} | CustomBuilder8+

设置TabBar上显示内容。CustomBuilder:构造器,内部可以传入组件(API8版本以上适用)。说明:如果icon采用svg格式图源,则要求svg图源删除其自有宽高属性值。如采用带有自有宽高属性的svg图源,icon大小则是svg本身内置的宽高属性值大小。

8.8 Swiper

滑块视图容器,提供子组件滑动轮播显示的能力。

子组件
可以包含子组件。
接口
Swiper(controller?:SwiperController)

参数

参数名

参数类型

必墳

参数描述

controller

SwiperController

给组件绑定一个控制器,用来控制组件翻页

属性

名称

参数类型

描述

index

number

设置当前在容器中显示的子组件的索引值。默认值:0

autoPlay

boolean

子组件是否自动播放,自动播放状态下导航点不可操作。默认值:false

interval

number

使用自动播放时播放的时间间隔,单位为毫秒。默认值:3000

indicator

boolean

是否启用导航点指示器。默认值:true

loop

boolean

是否开启循环。设置为true时表示开启循环,在LazyForEach懒循环加载模式下加载的组件数量建议大于5个。默认值:true

duration

number

子组件切换的动画时长,单位为毫秒。默认值:400

vertical

boolean

是否为纵向滑动。默认值:false

itemSpace

number  string

设置子组件与子组件之间间隙。默认值:0

displayMode

SwiperDisplayMode

设置子组件显示模式。默认值:SwiperDisplayMode.Stretch

cachedCount8+

number

设置预加载子组件个数。默认值:1

disableSwipe8+

boolean

禁用组件滑动切换功能,默认值:false

displayCount8+

number|string

设置一页中显示子组件的个数,设置为“auto”时等同于

SwiperDisplayMode.AutoLinear的显示效果。默认值:1

effectMode8+

EdgeEffect

设置滑动到边缘时的显示效果。默认值:EdgeEffect.Spring

curve8+

Curve|string

设置Swiper的动画曲线,默认为淡入淡出曲线

indicatorStyle8+

{left?: Length,top?:Length,right?:Length,bottom?: Length,size?:Length,mask?: boolean,color?:ResourceColor,selectedColor?:ResourceColor}

设置导航点样式:-left: 设置导航点距离Swiper组件左边的距离。- top: 设置导航点距离Swiper组件顶部的距离。-right: 设置导航点距离Swiper组件右边的距离。-bottom:设置导航点距离Swiper组件底部的距离。-size:设置导航点的直径。-mask: 设置是否显示导航点蒙层样式。一color: 设置导航点的颜色。-selectedcolor:设置选中的导航点的颜色

8.9 Grid

网格容器,由“行”和“列”分割的单元格所组成,通过指定”项目”所在的单元格做出各种各样的布局。

子组件
包含GridItem子组件。
接口
Grid(scroller?:scroller)

参数:

参数名

参数类型

必填

参数描述

scroller

Scroller

可滚动组件的控制器,用于与可滚动组件进行绑定

属性

名称

参数类型

描述

columnsTemplate

string

设置当前网格布局列的数量,不设置时默认1列。例如,"1fr 1fr 2fr'是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。默认值:'1fr'

rowsTemplate

string

设置当前网格布局行的数量,不设置时默认1行。例如,"1fr 1fr 2fr”是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。默认值:'1fr

columnsGap

Length

设置列与列的间距。默认值:0

rowsGap

Length

设置行与行的间距,默认值:0

scrollBar

BarState

设置滚动条状态。默认值:BarState.0ff

scrollBarColor

string |

number |

Color

设置滚动条的颜色。

scrollBarWidth

string |

number

设置滚动条的宽度。

cachedCount

number

设置预加载的Gridltem的数量,具体使用可参考减少应用白块说明。默认值:1

editMode8+

boolean

是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部Gridltem。默认值:false

8.10 GridItem

9.ArkUI-基础组件

9.1 Text组件

显示一段文本的组件,可以包含Span子组件

接口
Text(content?:string | Resource)

属性:

名称

参数类型

描述

textAlign

TextAlign

设置多行文本的文本对齐方式。默认值:TextAlign.Start

textOverflow

{overflow: TextOverfow}

设置文本超长时的显示方式。默认值:{overflow: TextOverflow.Clip}

说明:文本截断是按字截断。

例如:英文以单词为最小单位进行截断,若需要以字母为单位进行截断,可在字母间添加零宽空格:\u200B。

maxLines

number

设置文本的最大行数。默认值:Infinity

lineHeight

string l number l Resource

设置文本的文本行高,设置值不大于0时,不限制文本行高,自适应字体大小,Length为number类型时单位为fp。

decoration

{type:TextDecorationType,color?:ResourceColor}

设置文本装饰线样式及其颜色。

默认值:{type: TextDecorationType.None,color:Color.Black}

baselineOffset

number l string

设置文本基线的偏移量。

letterSpacing

number | string

设置文本字符间距。

minFontSize

number | string | Resource

设置文本最小显示字号。

maxFontSize

number | string | Resource

设置文本最大显示字号.

textCase

TextCase

设置文本大小写。默认值: TextCase.Normal

9.1.1 fontSize尺寸

语法:

Text('hello world')
.fontSize(40)

9.1.2 fontWeight粗细

语法:需要使用枚举FontWeight设置属性

Text('hello world')
.fontWeight(FontWeight.属性)

属性值:

  • Bold 加粗

  • Normal 不加粗

9.1.3 color文本颜色

语法:

Text('hello world')
.fontColor(颜色)

颜色属性:

  • 单词:red

  • 16进制:#16进制代码

  • 0X:0X + 16进制代码

  • 枚举color:Color.颜色

示例:

Text('hello world')
.fontColor('red')
.fontColor('#f00')
.fontColor(0Xf12345)
.fontColor(Color.Orange)

9.1.4 内外间距

内间距

Text('hello world')
.padding({top:10,left:20})

外间距

Text('hello world')
.margin({top:20})

9.1.5 border边框

语法:

Text('hello world')
.border({width:1}) //边框宽度
.border({width:1,color:0X654321,style:BorderStyle.Solid})//边框宽度 边框颜色 边框实线
.border({width:1,color:0X654321,style:BorderStyle.Dotted})//边框宽度 边框颜色 边框虚线
.border({width:1,color:0X654321,style:BorderStyle.Dashed})//边框宽度 边框颜色 边框点线

9.1.6 lineHeight 行高

语法:

Text('hello world')
.lineHeight(100)

9.1.7 decoration 修饰线

语法:

Text('hello world')
.decoration({type:TextDecorationType.属性,color:'颜色'})

//LineThrough 删除线
// Underline 下划线
// Overline 上划线

属性值:

  • LineThrough 删除线

  • Underline 下划线

  • Overline 上划线

示例:

Text('hello world')
//设置文本装饰线
//LineThrough 删除线
// Underline 下划线
// Overline 上划线
.decoration({type:TextDecorationType.Underline,color:"red"})

baselineOffset

9.1.8 baselineOffset 偏移量

语法:

Text('hello world')
.baselineOffset(50)

9.1.9 letterSpacing 文字间隔

语法:

Text('hello world')
.letterSpacing(10)

9.1.10 TextCase 设置大小写

语法:

Text('hello world')
.textCase(TextCase.属性)

示例:

Text('hello world')
//TextCase.UpperCase 大写
.textCase(TextCase.UpperCase)
//TextCase.UpperCase 小写
.textCase(TextCase.LowerCase)
//TextCase.Normal 默认
.textCase(TextCase.Normal)

9.1.11 多行文本

9.1.11.1 textAlign对齐方式

语法:

Text('英文以单词为最小单位进行截断,若需要以字母为单位进行截断,可在字母间添加零宽空格')
.fontSize(40)
.fontWeight(FontWeight.Bold)
// 多行文本对齐方式textAlign
//左
.textAlign(TextAlign.Start)
// 中
.textAlign(TextAlign.End)
//右
.textAlign(TextAlign.Center)
9.1.11.2 maxLines 最大行数

语法:

Text('英文以单词为最小单位进行截断,若需要以字母为单位进行截断,可在字母间添加零宽空格')
.fontSize(40)
.fontWeight(FontWeight.Bold)
.maxLines(3)
9.1.11.2 textOverflow 截取方式

语法:

Text('英文以单词为最小单位进行截断,若需要以字母为单位进行截断,可在字母间添加零宽空格')
.fontSize(40)
.fontWeight(FontWeight.Bold)
.maxLines(3)
// 文本截断方式
//直接截断
.textOverflow({overflow:TextOverflow.Clip})
//截断部分...
.textOverflow({overflow:TextOverflow.Ellipsis})

9.2 TextInput组件

输入框组件,文本框 ,密码框,数字框,邮箱框

语法:

TextInput({placeholder:"请输入"})

属性:

输入框类型:type(InputType.类型)

类型:Normal 默认文本 Number 数字 PhoneNumber  电话  Email 邮箱 Password 密码

提示语颜色:placeholderColor(0X999999)

最大输入长度:maxLength(6)

事件:

TextInput({placeholder:"请输入用户名"})
.onChange(data=>{//输入的内容发生变化时,触发回调,data代表输入的内容
this.user = data;
console.log('user:'+data)
})


9.3TextinputController8+

9.4 Button

按钮组件,可快速创建不同样式的按钮

方法1:

Button(options?: {type?: ButtonType, stateEffect?: boolean})

参数:

参数名

参数类型

必填

参数描述

type

ButtonType

描述按钮显示样式。默认值:ButtonType.Capsule

stateEffect

boolean

按钮按下时是否开启按压态显示效果,当设置为false时,按压效果关闭。默认值:true

方法2

Button(label?: ResourceStr, options?: { type?: ButtonType, stateEffect?: boolean })

使用文本内容创建相应的按钮组件,此时Button无法包含子组件。

参数名

参数类型

必填

参数描述

label

ResourceStr

按钮文本内容。

options

{type?: ButtonType,stateEffect?: boolean}

见方法1参数说明。

9.5 Image

图片组件,支持本地图片和网络图片的渲染展示,

接口
Image(src:string | pixelMap | Resource)

参数:

参数名

参数类型

必填

默认值

参数描述

src

string

PixelMap

Resource

-

图片的路径,支持本地图片和网络图片

图片格式包括png、jpg、bmp、svg和gif。

使用相对路径引用图片资源

例如Image("common/test.jpg")

不支持该lmage组件被跨包/跨模块调用,建议使用$r方式来管理需全局使用的图片资源

- 支持Base64字符串6+。格式为data:image/[png | jpeg | bmp | webp];base64, [base64 data], 其中[base64 data]为Base64字符串数据。

- 支持dataability://的路径前缀,用于访问通过data ability提供的图片路径

引入网络图片示例

Image('https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png')

本地资源图片放置到 entey>main>resource>base>media中,可以看到icon.png文件,引入该图片地址

Image($r('app.media.icon'))

图片缩放

当图片的原始大小与 Image 组件不同时,可通过 objectFit()方泛来设置图片的显示效果。该方法的参数类型为 ImageFit 枚举类型,可选的枚举值如下

ImageFit.None:保持原有尺寸显示,不做任何缩放,超出显示区域的部分不显示

ImageFit.contain:保持宽高比进行缩小或者放大,使得显示区或刚好包含整个图片

ImageFit.Cover:保持宽高比进行缩小或者放大,使得图片刚好完成覆盖显示区域。

ImageFit.Fill:不保持宽高比进行放大缩小,使得图片充满显示区域

ImageFit.ScaleDown:保持宽高比进行缩小或不变(不会放大),使得图片完全显示在显示区域内。

ImageFit.Auto:自适应显示

示例

Image(item.img)
.objectFit(ImageFit.Cover)//对图片进行剪切
.aspectRatio(1.3) //设置宽高比

10.购物应用APP实战

10.1 项目需求

1.一共4个模块,登录页,首页,精选,我的

2.单击登录跳转到首页展示,首页模块有轮播、导航、列表

3.精选商城,显示列表。

4.单击我的,显示“誉天教育’信息及列表、返回登录页按钮。

支持设备:华为手机或运行在DevEco Studio上的华为手机设备模拟器

支持:API version9,需要使用DevEco Studio进行边距运行

10.2 项目环境搭建

如果你是首次打开DevEco Studio,那么首先进入欢迎页

点击File>New>Create Project

选择空的模板

给项目取一个名称

点击finish创建完成。

10.3 分析项目模块

10.3.1 设置UI页面

打开文件夹目录Shpping>entry>src>main>ets>pages

创建登录模块:LoginPage.ets

创建首页:MainPage.ets

首页

@Entry
@Component
struct Main {
@State message: string = '首页'

build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}

登录页

@Entry
@Component
struct Login {
@State message: string = '登录'

build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}

10.3.2 设置页面跳转

打开目录Shpping>entry>src>main>resource>base>profile>main_pages.json

{
"src": [
"pages/LoginPage",
"pages/MainPage"
]
}

10.4 开发登录页模块

1. 基础组件-Image组件添加图片

Image($r('app.media.icon'))

2.像素单位

提供了4中像素单位,框架采用vp为基准数据单位

名称

描述

px

屏幕物理像素单位、

vp

屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素,当数值不带单位时,默认单位vp。

fp

字体像素,与vp类似适用屏幕密度变化,随系统字体大小设置变化。

lpx

视窗逻辑像素单位,Ipx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值。如配置designWidth为720时,在实际宽度为1440物理像素的屏幕上,1lpx为2px大小

3.像素单位转换

提供其他单位与px单位互相转换的方法

名称

描述

vp2px(value :number) :number

将vp单位的数值转换为以px为单位的数值

px2vp(value :number):number

将px单位的数值转换为以vp为单位的数值

fp2px(value :number):number

将fp单位的数值转换为以px为单位的数值

px2fp(value :number):number

将px单位的数值转换为以fp为单位的数值

lpx2px(value :number):number

将Ipx单位的数值转换为以px为单位的数值

px2lpx(value :number):number

将px单位的数值转换为以Ipx为单位的数值

4.设置文本

@Entry
@Component
struct Login {
@State message: string = '欢迎登录'

build() {
Row() {
Column() {
//logo
Image($r('app.media.icon'))
.width('80vp')
.height('80vp')
.margin({bottom:'20vp'})
//欢迎登录
Text(this.message)
.fontSize('30fp')
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}

5.用户名和密码输入框

@State user:string =''
@State password:string =''

//用户名和密码
TextInput({placeholder:"请输入用户名"})
.placeholderColor(0X999999)
.maxLength(6)
.padding('12vp')
.margin('10vp')
.onChange(data=>{//输入的内容发生变化时,触发回调,data代表输入的内容
this.user = data;
console.log('user:'+data)
})
TextInput({placeholder:"请输入密码"})
.type(InputType.Password)//输入框类型
.placeholderColor(0X999999)
.maxLength(6)
.padding('12vp')
.margin('10vp')
.onChange(data=>{//输入的内容发生变化时,触发回调,data代表输入的内容
this.password = data;
console.log('password:'+data)
})

6.文字提示

Row(){
Text('短信验证码登录')
.fontSize('18fp')
.fontColor(Color.Blue)
Text('忘记密码')
.fontSize('18fp')
.fontColor(Color.Blue)
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)

7.登录按钮

//登录按钮
//type设置状态:Circle 圆形 Capsule 胶囊 Normal 默认
//stateEffect 按压效果去除 默认为true
Button('登陆',{type:ButtonType.Capsule,stateEffect:false})
.width('90%')
.margin('10vp')
.onClick(()=>{
//具体逻辑
})

8.其他内容

Row(){
Text('其他登录方式')
.fontSize('18fp')
.fontColor(Color.Blue)
}
.width('100%')
.justifyContent(FlexAlign.Center)

9.3个按钮

  Button('微信登陆',{type:ButtonType.Circle,stateEffect:false})
.width('80vp')
.height('80vp')
Button('QQ',{type:ButtonType.Circle,stateEffect:false})
.width('80vp')
.height('80vp')
Button('手机',{type:ButtonType.Circle,stateEffect:false})
.width('80vp')
.height('80vp')
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.margin({top:'20vp'})

10.5 @Extend修饰器

我们写好的登陆了页面存在很多重复的样式代码。针对重复的代码可以使用装饰器进行优化

@Extend修饰器:扩展组件样式

语法:

@Extend(组件描述名称) function 样式名称(){}

示例:

@Entry
@Component
struct Login {
@State message: string = '欢迎登录';
@State user:string ='';
@State password:string ='';


build() {
Row() {
Column() {
//logo
Image($r('app.media.icon'))
.width('80vp')
.height('80vp')
.margin({bottom:'20vp'})
//欢迎登录
Text(this.message)
.fontSize('30fp')
.fontWeight(FontWeight.Bold)
//用户名和密码
TextInput({placeholder:"请输入用户名"})
.inputStyle()
.onChange(data=>{//输入的内容发生变化时,触发回调,data代表输入的内容
this.user = data;
console.log('user:'+data)
})
TextInput({placeholder:"请输入密码"})
.type(InputType.Password)//输入框类型
.inputStyle()
.onChange(data=>{//输入的内容发生变化时,触发回调,data代表输入的内容
this.password = data;
console.log('password:'+data)
})

//文字提示
Row(){
Text('短信验证码登录')
.fontSize('18fp')
.fontColor(Color.Blue)
Text('忘记密码')
.fontSize('18fp')
.fontColor(Color.Blue)
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)

//登录按钮
//type设置状态:Circle 圆形 Capsule 胶囊 Normal 默认
//stateEffect 按压效果去除 默认为true
Button('登陆',{type:ButtonType.Capsule,stateEffect:false})
.width('90%')
.margin('10vp')
.onClick(()=>{
//具体逻辑
})

//其他内容
Row(){
Text('其他登录方式')
.fontSize('18fp')
.fontColor(Color.Blue)
}
.width('100%')
.justifyContent(FlexAlign.Center)

//3个按钮
Row(){
Button('微信登陆',{type:ButtonType.Circle,stateEffect:false})
.btnWidthStyle()
Button('QQ',{type:ButtonType.Circle,stateEffect:false})
.btnWidthStyle()
Button('手机',{type:ButtonType.Circle,stateEffect:false})
.btnWidthStyle()
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.margin({top:'20vp'})

}
.width('100%')
}
.height('100%')
}
}

//添加公共的样式函数
// @Extend 装饰器:扩展组件样式
// @Extend(组件描述名称) function 样式名称(){}
@Extend(TextInput) function inputStyle() {
.placeholderColor(0X999999)
.maxLength(6)
.padding('12vp')
.margin('10vp')
}

@Extend(Button) function btnWidthStyle() {
.width('80vp')
.height('80vp')
}

10.6 ForEach循环

登录页面的三个按钮除了文本不一样,其他都是一样的。所以可使用ForEach循环

语法

ForEach('',()=>{},()=>{})

示例:

private otherDate:Array<string> = ["微信","QQ","手机"]

Row(){
ForEach(this.otherDate,(item:string)=>{
Button(item,{type:ButtonType.Circle,stateEffect:false})
.btnWidthStyle()//公共样式
},(item:string)=>JSON.stringify(item))
// Button('微信',{type:ButtonType.Circle,stateEffect:false})
// .btnWidthStyle()
// Button('QQ',{type:ButtonType.Circle,stateEffect:false})
// .btnWidthStyle()
// Button('手机',{type:ButtonType.Circle,stateEffect:false})
// .btnWidthStyle()
}
.width('100%')
.justifyContent(FlexAlign.SpaceAround)
.margin({top:'20vp'})

10.7 @ohos.router页面路由

ohos:翻译表示”OpenHarmonyOS“,使用需要在页面顶部引入路由

语法:

import router from '@ohos.router';
router.pushUrl({
url:'pages/LoginPage'
}).catch((error:Error)=>{
//错误提示
})

示例:

import router from '@ohos.router';
@Entry
@Component
struct Main {
@State message: string = '首页'

build() {
Row() {
Column() {
Text(this.message)
.fontSize('30fp')
.fontWeight(FontWeight.Bold)

Button('返回登录页',{type:ButtonType.Capsule,stateEffect:false})
.width('90%')
.margin('10vp')
.onClick(()=>{
//具体逻辑
//路由跳转
router.pushUrl({
url:'pages/LoginPage'
}).catch((error:Error)=>{
//错误提示
})
})
}
.width('100%')
}
.height('100%')
}
}

10.8 @ohos.promptAction 弹窗

点击登录按钮的逻辑判断:没有输入用户名和密码,不允许跳转。添加判断条件。

语法:

import promptAction from '@ohos.promptAction';
promptAction.showToast({
message:"用户名或密码不能为空",//显示内容 必填
duration:3000, //持续时间 选填
bottom:'60vp' //弹窗离底部的距离 选填
})

示例:

import promptAction from '@ohos.promptAction';
Button('登陆',{type:ButtonType.Capsule,stateEffect:false})
.width('90%')
.margin('10vp')
.onClick(()=>{
//判断
if(this.user=='' || this.password==''){
//不能跳转,同时给出弹窗提示
promptAction.showToast({
message:"用户名或密码不能为空",
duration:3000, //持续时间
bottom:'60vp' //弹窗离底部的距离
})
}else{
//路由跳转
router.pushUrl({
url:'pages/MainPage'
}).catch((error:Error)=>{
//错误提示
})
}

})

10.9 Tabs底部导航

示例

@Entry
@Component
struct Main {
@State message: string = '首页'
//Tabs组件的控制器,用于控制Tabs组件进行页签切换。
private controller:TabsController = new TabsController()
build() {
Row() {
// BarPosition.Start 左侧或顶部
// .vertical(true) true 页签位于左侧 false 页签位于顶部
// BarPosition.End 右侧或底部
// .vertical(true) true 页签位于右侧 false 页签位于底部
Tabs({barPosition:BarPosition.End}){
TabContent(){
Column()
.width('100%')
.height('100%')
.backgroundColor(Color.Red)
}.tabBar('首页')
TabContent(){
Column()
.width('100%')
.height('100%')
.backgroundColor(Color.Pink)
}.tabBar('精选')
TabContent(){
Column()
.width('100%')
.height('100%')
.backgroundColor(Color.Orange)
}.tabBar('我的')
}
.vertical(false)
.barWidth('100%') //设置宽度
.barHeight(100)//设置高度
.animationDuration(400)
}
.height('100%')
}
}

10.10 if...else条件渲染

创建首页,精选,我的页面

在pages同级别创建文件夹view

首页:HomePage

精选:ChoicePage

我的:MinePage

首页

@Component
// 导出
export default struct Home {
@State message: string = '首页'

build() {
Row() {
Text(this.message)
}
.height('100%')
}
}

精选

@Component
// 导出
export default struct Choice {
@State message: string = '精选'

build() {
Row() {
Text(this.message)
}
.height('100%')
}
}

我的

@Component
// 导出
export default struct Mine {
@State message: string = '我的'

build() {
Row() {
Text(this.message)
}
.height('100%')
}
}

优化tabbar使用forEach获取元素,使用if判断索引,显示不同的page

// 导入组件
import Home from '../view/HomePage'
import Choice from '../view/ChoicePage'
import Mine from '../view/MinePage'

@Entry
@Component
struct Main {
@State message: string = '首页'
//Tabs组件的控制器,用于控制Tabs组件进行页签切换。
private controller: TabsController = new TabsController()
private items:Array<{title:string}> = [
{title:"首页"},
{title:"精选"},
{title:"我的"}
]
build() {
Row() {
// BarPosition.Start 左侧或顶部
// .vertical(true) true 页签位于左侧 false 页签位于顶部
// BarPosition.End 右侧或底部
// .vertical(true) true 页签位于右侧 false 页签位于底部
Tabs({barPosition:BarPosition.End}){
ForEach(this.items,(item:{title:string},index:number)=>{
TabContent(){
Column() {
// Text(item.title)
//加载不同的组件
if(index===0){
Home()
}else if(index===1){
Choice()
}else {
Mine()
}
}
}.tabBar(item.title)
})
}
.vertical(false)
.barWidth('100%') //设置宽度
.barHeight(100)//设置高度
.animationDuration(400)
}
.height('100%')
}
}

10.11 @Builder自定义构建函数

实现tabbar图标切换

// 导入组件
import Home from '../view/HomePage'
import Choice from '../view/ChoicePage'
import Mine from '../view/MinePage'

@Entry
@Component
struct Main {
@State message: string = '首页'
//Tabs组件的控制器,用于控制Tabs组件进行页签切换。
private controller: TabsController = new TabsController()
private items:Array<{title:string,iconSelected:Resource,iconNormal:Resource}> = [
{title:"首页",iconSelected:$r('app.media.ic_hone_selected'),iconNormal:$r('app.media.ic_home_normal')},
{title:"精选",iconSelected:$r('app.media.ic_discover_selected'),iconNormal:$r('app.media.ic_discover_normal')},
{title:"我的",iconSelected:$r('app.media.ic_mine_selected'),iconNormal:$r('app.media.ic_mine_normal')}
]
@State currentIndex:number =0;//当前索引下标
// @Builder 装饰器 自定义构建函数
@Builder TabBuilder(title,iconSelected,iconNormal,index){
Column(){
Image(this.currentIndex === index?iconSelected:iconNormal)
.width('25vp')
.height('25vp')
Text(title)
.fontColor(this.currentIndex === index?"#007DEE":"#182431")
.fontSize('14fp')
.fontWeight(500)
}
.width('100%')
}

build() {
Row() {
// BarPosition.Start 左侧或顶部
// .vertical(true) true 页签位于左侧 false 页签位于顶部
// BarPosition.End 右侧或底部
// .vertical(true) true 页签位于右侧 false 页签位于底部
Tabs({barPosition:BarPosition.End}){
ForEach(this.items,(item:{title:string,iconSelected:Resource,iconNormal:Resource},index:number)=>{
TabContent(){
Column() {
// Text(item.title)
//加载不同的组件
if(index===0){
Home()
}else if(index===1){
Choice()
}else {
Mine()
}
}
}.tabBar(this.TabBuilder(item.title,item.iconSelected,item.iconNormal,index))
})
}
.vertical(false)
.barWidth('100%') //设置宽度
.barHeight(100)//设置高度
.animationDuration(400)
.onChange((index:number)=>{
this.currentIndex = index;
})
}
.height('100%')
}
}

10.12 首页组件导入导出

创建文件夹components

entry\src\main\ets\view\components

在该目录下创建3个ets文件

  • SwiperExample.ets 轮播

@Component
// 导出
export default struct SwiperExample {
@State message: string = '轮播组件'

build() {
Row() {
Text(this.message)
}
.height('100%')
}
}
  • NavExample.ets  导航

@Component
// 导出
export default struct NavExample {
@State message: string = '导航组件'

build() {
Row() {
Text(this.message)
}
.height('100%')
}
}
  • ListExample.ets 列表

@Component
// 导出
export default struct ListExample {
@State message: string = '列表组件'

build() {
Row() {
Text(this.message)
}
.height('100%')
}
}

在HomePage首页引入三个组件

import SwiperExample from './components/SwiperExample'
import NavExample from './components/NavExample'
import ListExample from './components/ListExample'
@Component
// 导出
export default struct Home {
build() {
Scroll(){
Column(){
//轮播图
SwiperExample()
// 导航
NavExample()
// 列表
ListExample()
}
.width('100%')
}
.backgroundColor('#F1F3F5')
.scrollBar(BarState.Auto) //设置滚动条状态
}
}

10.13 Swiper轮播组件

  • SwiperExample.ets 轮播

@Component
// 导出
export default struct SwiperExample {
@State SwiperData:Array<Resource> = [
$r('app.media.banner_1'),
$r('app.media.banner_2'),
$r('app.media.banner_3'),
]

build() {
Column(){
Swiper(){
ForEach(this.SwiperData,(item:Resource)=>{
Image(item)
.width('95%')
.height('160vp')
})
}
.index(0) //初始化索引值
.autoPlay(true) //自动播放
.interval(2000)//间隔时间2,默认3s
.loop(true) //是否开启循环 默认true
.duration(2000) //切换动画时长
.itemSpace(0) //图片间隙 默认0
.indicator(true) //是否启动导航点指示器
}
.width('100%')
.margin({top:'5vp'})
}
}

10.14 数据类型接口定义

entry\src\main\ets 创建文件夹mode

在mode中创建文件types.ets 设置定义的文件类型

在types.ets文件中设置接口

// 设置接口,并使用export导出
// ?表示可选的
export interface ItemType {
id?:number;
title?:string | Resource;
img?:string | Resource
}

在NavExample.ets设置导航数据

// 引入类型
import {ItemType} from '../../mode/types'
@Component
// 导出
export default struct NavExample {
@State message: string = '导航组件'

private navData:Array<ItemType> =[
{
title:'喜欢',
img:$r('app.media.ic_like')
},
{
title:'排名',
img:$r('app.media.ic_ranking')
},
{
title:'消息',
img:$r('app.media.ic_msg')
},
{
title:'购物车',
img:$r('app.media.ic_gwc')
},
{
title:'礼物',
img:$r('app.media.ic_gift')
},
{
title:'卡券',
img:$r('app.media.ic_coupons')
},
{
title:'收藏',
img:$r('app.media.ic_collect')
},
{
title:'设置',
img:$r('app.media.ic_setting')
}
]

build() {
Row() {
Text(this.message)
}
.height('100%')
}
}

10.15 Grid组件

在NavExample.ets导航菜单,多行多列,适合使用Grid组件实现数据展示

// 引入类型
import {ItemType} from '../../mode/types'
@Component
// 导出
export default struct NavExample {
@State message: string = '导航组件'

private navData:Array<ItemType> =[
{
title:'喜欢',
img:$r('app.media.ic_like')
},
{
title:'排名',
img:$r('app.media.ic_ranking')
},
{
title:'消息',
img:$r('app.media.ic_msg')
},
{
title:'购物车',
img:$r('app.media.ic_gwc')
},
{
title:'礼物',
img:$r('app.media.ic_gift')
},
{
title:'卡券',
img:$r('app.media.ic_coupons')
},
{
title:'收藏',
img:$r('app.media.ic_collect')
},
{
title:'设置',
img:$r('app.media.ic_setting')
}
]

build() {
Column(){
Grid(){
ForEach(this.navData,(item:ItemType)=>{
GridItem(){
Column(){
Image(item.img)
.width('25vp')
.height('25vp')
Text(item.title)
.fontSize('14fp')
.margin({top:'5vp'})
}
}
})
}
//2行4列
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
.height('120vp')
.rowsGap('12vp')//设置行与行间距
.columnsGap('8vp')//设置列于列间距1
.backgroundColor(Color.White)
.width('95%')
.margin('15vp')
.padding({
top:'10vp',
bottom:'10vp'
})
}
}
}

10.16 List列表组件

在ListExample.ets中,列表适合使用List列表组件

import {ItemType} from '../../mode/types'
@Component
// 导出
export default struct ListExample {
@State message: string = '列表组件'
private listsData:Array<ItemType> =[
{
title:'新品首发',
img:$r('app.media.ic_list1')
},
{
title:'大牌闪购',
img:$r('app.media.ic_list2')
},
{
title:'发现好物',
img:$r('app.media.ic_list3')
},
{
title:'排行榜',
img:$r('app.media.ic_list4')
},
{
title:'宠物市场',
img:$r('app.media.ic_list8')
},
{
title:'反季福利',
img:$r('app.media.ic_list5')
},
{
title:'明星同款',
img:$r('app.media.ic_list6')
},
{
title:'物流特色',
img:$r('app.media.ic_list7')
}
]

build() {
Column() {
Text('列表')
.fontSize('16fp')
.fontWeight(FontWeight.Medium)
.alignSelf(ItemAlign.Start) //文字左对齐
.margin('10vp')
List({space:12}){
ForEach(this.listsData,(item:ItemType)=>{
ListItem(){
Image(item.img)
.objectFit(ImageFit.Cover)//对图片进行剪切
.aspectRatio(1.3) //设置宽高比
Text(item.title)
.fontColor(Color.White)
.fontWeight(FontWeight.Medium)
}
.margin({left:'5vp',right:'5vp'})
})
}
.width('95%')
// .margin('10vp')
.lanes(2)//一行显示几个
}
}
}

10.17 我的页面-退出登录

entry\src\main\ets\view\components目录下创建用户描述

MineUser.ets

@Component
// 导出
export default struct MineUser {
@State message: string = '我的-用户描述'

build() {
Row(){
Text(this.message)
}
}
}

entry\src\main\ets\view\components目录下创建列表

MineList.ets

@Component
// 导出
export default struct MineList {
@State message: string = '我的-列表'

build() {
Column() {
Text(this.message)
}
}
}

在我的页面 MinePage

导入用户描述,导入用户列表

添加顶部标题

添加底部按钮,实现退出登录

import MineUser from './components/MineUser'
import MineList from './components/MineList'
//导入路由
import router from '@ohos.router'
@Component
// 导出
export default struct Mine {
@State message: string = '我的'

build() {
Row() {
Column(){
Text(this.message)
.fontSize('16fp')
.alignSelf(ItemAlign.Start)
.margin({top:"20vp"})
//用户描述
MineUser()
//列表
MineList()
//退出登录
Button('退出登录')
.width('90%')
.height('40vp')
.fontSize('16fp')
.backgroundColor('#e5e8ea')
.fontColor(Color.Red)
.margin({top:'30vp'})
.onClick(()=>{
//跳转
router.pushUrl({
url:'pages/LoginPage' //跳转到登录
}).catch((error:Error)=>{
//异常处理
})
})
}
.width('95%')
.margin('10vp')
.height('100%')
}
.backgroundColor('#f1f3f5')
}
}

运行结果

10.18 我的页面-用户描述信息

整体在一行使用row容器,文本上下结构使用column容器

@Component
// 导出
export default struct MineUser {
@State message: string = '我的-用户描述'

build() {
Row(){
Image($r('app.media.user'))
.height(50)
.width(50)
Column(){
Text('誉天教育')
.fontSize('20fp')
Text('yutian@163.com')
.fontSize('14fp')
.margin({top:'5vp'})
}
.alignItems(HorizontalAlign.Start) //左对齐
.margin({left:'15vp'})
}
.alignItems(VerticalAlign.Center)
.backgroundColor(Color.White)
.width('100%')
.height('100vp')
.margin({top:'20vp',bottom:'12vp'})
.borderRadius('25vp')
.padding({left:'20vp'})
}
}

10.19 我的页面-系统选项列表

1.List设置元素列表

2.使用开关组件Toggle

//导入接口typets
import {ItemType} from '../../mode/types'
// 弹窗
import promptAction from '@ohos.promptAction';
@Component
// 导出
export default struct MineList {
@State message: string = '我的-列表'
private ListsData:Array<ItemType> = [
{
title:'数据推送',
img:$r('app.media.icon_sjts')
},
{
title:'菜单设置',
img:$r('app.media.icon_cdsz')
},
{
title:'服务',
img:$r('app.media.icon_fw')
},
{
title:'收藏',
img:$r('app.media.icon_sc')
},
{
title:'设置',
img:$r('app.media.icon_sz')
},
{
title:'隐私',
img:$r('app.media.icon_ys')
}
]

build() {
List(){
ForEach(this.ListsData,(item:ItemType)=>{
ListItem(){
//两端内容
Row(){
// 左侧内容 图片文字间距
Row({space:10}){
Image(item.img)
.width('22vp')
.height('22vp')
Text(item.title)
.fontSize('16fp')
}
//开关组件
// type:ToggleType.Checkbox 复选框
// type:ToggleType.Switch 开关
// isOn 默认状态,true打开 false 关闭
Toggle({type:ToggleType.Switch,isOn:false})
.onChange((isChange:boolean)=>{
let tip:string = isChange ? item.title+'打开' :item.title+'关闭'
//isChange 打开或者关闭的状态
promptAction.showToast({
message:tip,
duration:3000
})
})
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding('12vp')
}
.height('50vp')
})
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius('25vp')
.padding({top:'5vp',bottom:'5vp'})
//分割线
.divider({
color:"#ddd",
strokeWidth:'1vp',
startMargin:'46vp',//分割线开始的位置
endMargin:'12vp'
})
}
}

10.20 精选模块

使用list数据渲染

import {ItemType} from '../mode/types'
@Component
// 导出
export default struct Choice {
@State message: string = '精选'
private listsData:Array<ItemType> =[
{
title:'新品首发',
img:$r('app.media.ic_list1'),
num:'¥30'
},
{
title:'大牌闪购',
img:$r('app.media.ic_list2'),
num:'¥30'
},
{
title:'发现好物',
img:$r('app.media.ic_list3'),
num:'¥30'
},
{
title:'新品首发',
img:$r('app.media.ic_list1'),
num:'¥30'
},
{
title:'大牌闪购',
img:$r('app.media.ic_list2'),
num:'¥30'
},
{
title:'发现好物',
img:$r('app.media.ic_list3'),
num:'¥30'
},
{
title:'新品首发',
img:$r('app.media.ic_list1'),
num:'¥30'
},
{
title:'大牌闪购',
img:$r('app.media.ic_list2'),
num:'¥30'
},
{
title:'发现好物',
img:$r('app.media.ic_list3'),
num:'¥30'
}
]

build() {
Column() {
List({space:12}){
ForEach(this.listsData,(item:ItemType)=>{
ListItem(){
Column(){
Image(item.img)
.objectFit(ImageFit.Cover)
.aspectRatio(0.85) //设置宽高比
Text(item.title)
.fontColor('#999')
.fontWeight(FontWeight.Medium)
.alignSelf(ItemAlign.Start)
.height('25vp')

Text(item.num)
.fontColor(Color.Red)
.fontWeight(FontWeight.Medium)
.alignSelf(ItemAlign.Start)
.height('25vp')
}
}
.width('96%')
.margin({top:'10vp',left:'3%'})
.backgroundColor(Color.White)
.borderRadius('10vp')
.padding('5vp')
})
}
.width('100%')
.lanes(2)//一行显示几个
}
.backgroundColor('#F1F3F5')
}
}


HarmonyOS 的入门资料分享,有兴趣的可以看看
资源链接:https://pan.quark.cn/s/bea37113ecae
资源链接:https://pan.quark.cn/s/bea37113ecae

HarmonyOS科技
HarmonyOS 纯血鸿蒙是华为开发的国产智能终端操作系统 ,专注于分享鸿蒙学习教程、鸿蒙资讯、鸿蒙实战开发 !
 最新文章