一、开发准备
1.官网
https://developer.huawei.com/consumer/cn/
2.工具
DevEco Studio
下载: 下载中心 | 华为开发者联盟-HarmonyOS开发者官网,共建鸿蒙生态
3.安装
4.开发组件ArkTs
ArkTS是HarmonyOS主力应用开发语言。它在TypeScript(简称TS)的基础上,匹配ArkUI框架,扩展了声明式UI、状态管理等相应的能力,让开发者以更简洁、更自然的方式开发跨端应用。
5.快速上手
6.ArkTS工程目录结构(Stage模型)
7.安装模拟器
解决措施
首先打开控制面板 > 程序 > 程序与功能 > 启动或关闭Windows功能,找到并勾选“Hyper-V”、“Windows虚拟机监控程序平台”、“虚拟机平台”,点击确定并重启电脑。若勾选后启动模拟器仍然提示该错误,需要以管理员权限打开命令行窗口并执行:`bcdedit /set hypervisorlaunchtype auto`并重启电脑。
二、ArkUI
ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面开发。
1.图片显示组件
a.声明Image组件并设置图片源
Image(src:string|PixelMap|Resource)
Image('https://xxx.png')
Image(PixelMapObject)
Image($r('app.media.mate60'))
Image($rawfile('mate60.png'))
string格式,通常用来加载网络图片,需要申请网络访问权限:obos.permission.INTERNET
PixelMap格式,可以加载像素图,常用在图片编辑器中
Resource格式,加载本地图片,推荐使用
b.添加图片属性
Image($r('app.media.icon'))
.width(100)
.height(120)
.borderRadius(10)
.interpolation(ImageInterpolation.High)
2.文本显示组件
a.声明Text组件并设置文本内容
Text(content?:string|Resource)
Text('图片宽度')
Text($r('app.string.width_label'))
string格式,直接写文本内容
Resource格式,读取本地资源文件
b.添加图片属性
Text('注册账号')
.lineHeight(32)
.fontSize(20)
.fontColor('#ff1876f8')
.fontWeight(FontWeight.Medium)
3.文本输入框
a.声明TextInput组件
TextInput({placeHolder?:ResourceStr,text?:ResourceStr})
TextInput({placeHolder:'请输入账号或手机号'})
placeHolder输入框无输入时的提示文本
text输入框当前的文本内容
b.添加属性和事件
TextInput({text:'请输入账号或手机号'})
.width(150)
.height(30)
.backgroundColor('#fff')
.type(InputType.Password)
.onChange(value=>{console.log(value)})
4.按钮输入框
a.声明Button组件,label是按钮文字
Button(label?:ResourceStr)
Button('点我')
Button(){
Image($r('app.media.search))
.width(20)
.margin(10)
}
文字型按钮
自定义按钮,在Button组件内嵌套其他组件
b.添加属性和事件
Button('点我')
.width(100)
.height(30)
.type(ButtonType.Normal)
.onClick(()=>{})
5.滑动条组件
Slider(options?:SilderOptions)
Slider({
min:10,
max:100,
value:30,
step:10,
style:SliderStyle.OutSet,
direction:Axis.Horizontal,
reverse:false
})
.width('90%')
.showTips(true)
.blockColor('#36d')
.onChange(()=>{})
6.布局组件
Column容器从上往下排列
Row容器从左往右排列
间距:Space
7.示例
@Entry
@Component
struct Index {
@State imageWidth: number = 250;
build() {
Column() {
Row(){
Image('https://tse4-mm.cn.bing.net/th?id=OIP.nweYnRD-9eFP3nNgNDU9gwHaGt&pid=1.7&w=217&h=198&c=8&dpr=0.9')
.width(this.imageWidth)
}
.width('100%')
.height(400)
.justifyContent(FlexAlign.Center)
Row(){
Text($r('app.string.width_label'))
.fontSize(20)
.fontWeight(FontWeight.Bold)
TextInput({ placeholder: '请输入图片宽度', text: this.imageWidth.toFixed(0) })
.width(150)
.backgroundColor('#fff')
.type(InputType.Number)
.onChange(val=>{
console.log(val)
this.imageWidth=parseInt(val)
})
}
.width('100%')
.height(60)
.justifyContent(FlexAlign.SpaceAround)
Divider().width('91%')
Row(){
Button('缩小')
.width(80)
.height(30)
.type(ButtonType.Normal)
.onClick(()=>{
if(this.imageWidth>=10){
this.imageWidth-=10
}
})
Button('放大')
.width(80)
.height(30)
.type(ButtonType.Normal)
.onClick(()=>{
if(this.imageWidth<=340){
this.imageWidth+=10
}
})
}
.width('100%')
.height(60)
.justifyContent(FlexAlign.SpaceAround)
Slider({
min:10,
max:340,
value:this.imageWidth,
step:10,
style:SliderStyle.OutSet,
direction:Axis.Horizontal,
reverse:false
})
.width('90%')
.showTips(true)
.trackThickness(8)
.blockColor('#36d')
.onChange((val)=>{this.imageWidth=val})
}
.width('100%')
.height('100%')
}
}
三、渲染控制(ForEach,if-else)
1.ForEach
a.语法
ForEach(
//要遍历的数组数据
arr:Array,
//页面组件生成函数
(item:any,index?:number)=>{
Row(){
Image('mate60.jpg')
Column(){
Text('mate60')
Text('12700.00')
}
}
},
//键生成函数,为数组每一项生成一个唯一标识,组件是否重新渲染的判断标准
keyGenerator?:(item:any,index?:numbe):string=>{
}
)
b.示例
class Item {
name:string
image:ResourceStr
price:number
constructor( name:string, image:ResourceStr, price:number) {
this.name=name
this.image=image
this.price=price
}
}
@Entry
@Component
struct Index {
private items:Array<Item>=[
new Item('华为Mate60',$r('app.media.mate'),6999),
new Item('华为MateBookProX',$r('app.media.mate'),13999),
new Item('华为WatchGT4',$r('app.media.mate'),1438),
new Item('华为FreeBudPro3',$r('app.media.mate'),1499),
new Item('华为MateX5',$r('app.media.mate'),12999)
]
build() {
Column({space:8}) {
Row(){
Text('商品列表')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height(60)
.margin({bottom:20})
ForEach(
this.items,
(item:Item)=>{
Row({space:10}){
Image(item.image)
.width('20%')
Column({space:4}){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`¥ ${item.price}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
}
.alignItems(HorizontalAlign.Start)
}
.width('92%')
.height(100)
.padding(10)
.backgroundColor('#fff')
.borderRadius(20)
.justifyContent(FlexAlign.Start)
}
)
}
.width('100%')
.height('100%')
.backgroundColor('#ccc')
}
}
2.if-else条件渲染
class Item {
name:string
image:ResourceStr
price:number
discount:number
constructor( name:string, image:ResourceStr, price:number,discount:number=0) {
this.name=name
this.image=image
this.price = price;
this.discount=discount
}
}
@Entry
@Component
struct Index {
private items:Array<Item>=[
new Item('华为Mate60',$r('app.media.mate'),6999,500),
new Item('华为MateBookProX',$r('app.media.mate'),13999),
new Item('华为WatchGT4',$r('app.media.mate'),1438),
new Item('华为FreeBudPro3',$r('app.media.mate'),1499),
new Item('华为MateX5',$r('app.media.mate'),12999)
]
build() {
Column({space:8}) {
Row(){
Text('商品列表')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height(60)
.margin({bottom:20})
ForEach(
this.items,
(item:Item)=>{
Row({space:10}){
Image(item.image)
.width('20%')
Column({space:4}){
if(item.discount){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`原件¥ ${item.price}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#ccc')
.decoration({type:TextDecorationType.LineThrough})
Text('折扣价¥ '+(item.price-item.discount))
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
Text(`补贴¥ ${item.discount}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
}else{
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`¥ ${item.price}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
}
}
.alignItems(HorizontalAlign.Start)
}
.width('92%')
.height(120)
.padding(10)
.backgroundColor('#fff')
.borderRadius(20)
.justifyContent(FlexAlign.Start)
}
)
}
.width('100%')
.height('100%')
.backgroundColor('#ccc')
}
}
3.List
列表List是一种复杂容器,具备以下特点:
列表项ListItem数量过多超出屏幕后,会自动提供滚动功能
列表项ListItem既可以纵向排列,也可以横向排列
a.语法
List({space:10}){
ForEach([1,2,3,4],(item)=>{
ListItem(){
//列表项内容,只能包含一个根组件
}
})
}
.width('100%')
.listDirection(Axis.Vertical)//列表方向,默认纵向垂直,水平方向Horizontal
b.示例
class Item {
name:string
image:ResourceStr
price:number
discount:number
constructor( name:string, image:ResourceStr, price:number,discount:number=0) {
this.name=name
this.image=image
this.price = price;
this.discount=discount
}
}
@Entry
@Component
struct Index {
private items:Array<Item>=[
new Item('华为Mate60',$r('app.media.mate'),6999,500),
new Item('华为MateBookProX',$r('app.media.mate'),13999),
new Item('华为WatchGT4',$r('app.media.mate'),1438),
new Item('华为FreeBudPro3',$r('app.media.mate'),1499),
new Item('华为MateX5',$r('app.media.mate'),12999),
new Item('华为WatchGT4',$r('app.media.mate'),1438),
new Item('华为FreeBudPro3',$r('app.media.mate'),1499),
new Item('华为MateX5',$r('app.media.mate'),12999)
]
build() {
Column({space:8}) {
Row(){
Text('商品列表')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.height(30)
.margin({bottom:20})
List({space:8}){
ForEach(this.items,(item:Item)=>{
ListItem(){
//列表项内容,只能包含一个根组件
Row({space:10}){
Image(item.image)
.width('20%')
Column({space:4}){
if(item.discount){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`原件¥ ${item.price}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#ccc')
.decoration({type:TextDecorationType.LineThrough})
Text('折扣价¥ '+(item.price-item.discount))
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
Text(`补贴¥ ${item.discount}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
}else{
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`¥ ${item.price}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
}
}
.alignItems(HorizontalAlign.Start)
}
.width('92%')
.height(120)
.padding(10)
.backgroundColor('#fff')
.borderRadius(20)
.justifyContent(FlexAlign.Start)
}
})
}
.width('100%')
.layoutWeight(1)
}
}
}
四、自定义组件
1.拆分组件
@Component
struct Com {
private title:string=''
build(){
//组件UI描述
}
}
@Component
struct XxxPage {
build(){
//使用组件
Com({title:'标题'})
}
}
2.在页面中引入Header组件
3.全局自定义构建函数
@Builder function XxxBuilder(){
//UI描述
}
@Component
struct XxxPage {
build(){
XxxBuilder()
}
}
4.局部自定义构建函数
@Component
struct XxxPage {
@Builder YyyBuilder(){
//UI描述
}
build(){
this.YyyBuilder()
}
}
5.全局公共样式函数
仅可封装组件的通用属性
@Styles function fillScreen(){
.width('100%')
.height('100%')
.backgroundColor('#f6f6f6')
}
//Extend装饰器,继承模式,只能写在全局位置
@Extend(Text) function priceText(){
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
}
6.局部公共样式函数
@Component
struct XxxPage {
@Builder YyyBuilder(){
//UI描述
}
build(){
this.YyyBuilder()
.fillScreen()
}
//局部公共样式函数
@Styles fillScreen(){
.width('100%')
.height('100%')
.backgroundColor('#f6f6f6')
}
}
7.示例
@Component
export struct Header {
private title:ResourceStr|string=''
build(){
Row(){
Image($r('app.media.left'))
.width(30)
.onClick(()=>{})
Text(this.title)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Blank()
Image($r('app.media.reset'))
.width(24)
.onClick(()=>{})
}
.width('100%')
.height(30)
.backgroundColor('#f6f6f6')
}
}
import {Header} from '../Com/CommomCom';
class Item {
name:string
image:ResourceStr
price:number
discount:number
constructor( name:string, image:ResourceStr, price:number,discount:number=0) {
this.name=name
this.image=image
this.price = price;
this.discount=discount
}
}
//全局自定义构建函数
// @Builder function ItemCard(item:Item){
// }
//全局公共样式函数
@Styles function fillScreen(){
.width('100%')
.height('100%')
.backgroundColor('#f6f6f6')
}
//继承模式,只能写在全局位置
@Extend(Text) function priceText(){
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor('red')
}
@Entry
@Component
struct Index {
private items:Array<Item>=[
new Item('华为Mate60',$r('app.media.mate'),6999,500),
new Item('华为MateBookProX',$r('app.media.mate'),13999),
new Item('华为WatchGT4',$r('app.media.mate'),1438),
new Item('华为FreeBudPro3',$r('app.media.mate'),1499),
new Item('华为MateX5',$r('app.media.mate'),12999),
new Item('华为WatchGT4',$r('app.media.mate'),1438),
new Item('华为FreeBudPro3',$r('app.media.mate'),1499),
new Item('华为MateX5',$r('app.media.mate'),12999)
]
build() {
Column({space:8}) {
//标题部分
Header({title:'商品列表'})
.margin({top:10})
.padding({left:20,right:20})
//商品列表部分
List({space:8}){
ForEach(this.items,(item:Item)=>{
ListItem(){
//列表项内容,只能包含一个根组件
this.ItemCard(item)
}
})
}
.width('100%')
.padding(20)
.layoutWeight(1)
}
.fillScreen()
}
//局部自定义构建函数
@Builder ItemCard(item:Item){
Row({space:10}){
Image(item.image)
.width('20%')
Column({space:4}){
if(item.discount){
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`原件¥ ${item.price}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#ccc')
.decoration({type:TextDecorationType.LineThrough})
Text('折扣价¥ '+(item.price-item.discount))
.priceText()
Text(`补贴¥ ${item.discount}`)
.priceText()
}else{
Text(item.name)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(`¥ ${item.price}`)
.priceText()
}
}
.alignItems(HorizontalAlign.Start)
}
.justifyContent(FlexAlign.Start)
.CardItemScreen()
}
//局部公共样式函数
@Styles fillScreen(){
.width('100%')
.height('100%')
.backgroundColor('#f6f6f6')
}
@Styles CardItemScreen(){
.width('100%')
.height(120)
.backgroundColor('#fff')
.borderRadius(20)
}
}