鸿蒙基础入门与高频知识点梳理

介绍鸿蒙高频知识点,持续更新中

一、鸿蒙代码结构

├──entry/src/main/ets        // 代码区
│  ├──common
│  │  └──Constant.ets        // 常量类
│  ├──entryability            
│  │  └──EntryAbility.ts     // 程序入口类
│  ├──pages
│  │  ├──MainPage.ets        // 主页入口文件
│  │  └──WebPage.ets         // 抽奖页入口文件
│  └──viewmodel                          
│     └──NavigatorModel.ets  // 导航model
├──entry/src/main/resources  
│  ├──base
│  │  ├──element             // 尺寸、颜色、文字等资源文件存放位置
│  │  ├──media               // 媒体资源存放位置
│  │  └──profile             // 页面配置文件存放位置
│  ├──en_US                  // 国际化英文
│  ├──rawfile                // 本地html代码存放位置 
│  └──zh_CN                  // 国际化中文
└──HttpServerOfWeb           // 服务端代码

二、配置文件

1、module.json5

用于配置UIAbility页面模块信息。

位置:/entry/src/main/module.json5

{"module": {"name": "entry",//当前Module的名称"type": "entry",//Module的类型(entry:应用的主模块, feature:应用的动态特性模块)"description": "$string:module_desc","mainElement": "EntryAbility",//标识当前Module的入口UIAbility名称或者ExtensionAbility名称。"deviceTypes": [//运行设备"phone","tablet"],"deliveryWithInstall": true,//标识当前Module是否在用户主动安装的时候安装,表示该Module对应的HAP是否跟随应用一起安装。"installationFree": false,//是否支持免安装特性"pages": "$profile:main_pages",//页面配置文件json"abilities": [//UIAbility的配置信息{"name": "EntryAbility",//当前UIAbility组件的名称,该名称在整个应用要唯一"srcEntry": "./ets/entryability/EntryAbility.ts",//入口UIAbility的路径"description": "$string:EntryAbility_desc","icon": "$media:icon",//app图标"label": "$string:EntryAbility_label",//app"startWindowIcon": "$media:icon",//当前UIAbility组件启动页面图标(暂时没发现有啥用,与上面保持一致即可)"startWindowBackground": "$color:start_window_background","exported": true,//当前UIAbility组件是否可以被其他应用调用"skills": [//能够接收的Want的特征集{"entities": ["entity.system.home"],"actions": ["action.system.home"]}]}]}
}

2、main_pages.json

页面列表json,对应上面module.json5的pages字段。

位置:/entry/src/main/resources/base/profile/main_pages.json

{"src": ["pages/SecondPage","pages/SimpleVideoPlay","pages/Index"]
}

3、build-profile.json5

定制HAP多目标构建产物。

位置:entry/build-profile.json5

{"apiType": 'stageMode',"buildOption": {},"targets": [{"name": "default","runtimeOS": "HarmonyOS"},{"name": "ohosTest",}]
}

例如,以ArkTS Stage模型为例,定义一个免费版和付费版,示例如下。参考资料

{"apiType": 'stageMode',"buildOption": {},"targets": [{"name": "default"  //默认target名称default,未定义deviceType,默认支持config.json或module.json5中定义的设备类型},{"name": "free", //免费版target名称"config": {"deviceType": [  //定义free支持的设备类型为Tablet"tablet"]}},{"name": "pay",//付费版target名称"config": {"deviceType": [  //定义pay支持的设备类型为Tablet"tablet"]}}]
}

4、oh-package.json5

描述项目基础信息

位置:entry/oh-package.json5

{"name": "entry","version": "1.0.0","description": "Please describe the basic information.","main": "","author": "","license": "","dependencies": {}
}

三、组件

1、Image

image-20231123184752283
  • 网络图片

需要在module.json5 文件中添加网络访问权限

"module": {"requestPermissions": [{"name": "ohos.permission.INTERNET"}]
}
Image('https://gitcode.net/liuxingyuzaixian/csdn_img/-/raw/main/pictures/2023/11/17_10_51_42_image-20230518181509168.png').width(78).height(78).objectFit(ImageFit.Cover)//设置缩放类型
  • PixelMap 图片

代码生成的色块图片,需要创建PixelMap对象

@State myPixelmap?: PixelMap = nullonPageShow() {// 创建PixelMap图片const color = new ArrayBuffer(56);let opts = { editable: true, pixelFormat: 3, size: { height: 4, width: 6 } }image.createPixelMap(color, opts, (err, pixelmap) => {if (pixelmap != undefined) {this.myPixelmap = pixelmap;}})
}// 使用
if (this.myPixelmap != null)Image(this.myPixelmap).width(78).height(78)
  • Resource 图片

需要将图片添加到下面目录:/resources/base/media

// 使用
Image($r('app.media.icon')).width(78).height(78)

2、Text

Text($r('app.string.module_desc')).fontSize(50).fontWeight(FontWeight.Bold).fontColor(0xFF0000).maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })//单行....decoration({ type: TextDecorationType.Underline, color: Color.Black })//文本装饰线

1、数字默认单位: vp

2、vp :屏幕密度相关像素

3、sp:文本推荐

3、TextInput

单行文本输入

TextInput({ placeholder: "账号" }).maxLength(11).type(InputType.Number).onChange((value: string) => {})

4、Button

Button("登录", { type: ButtonType.Capsule }).onClick(() => {})

5、Column、Row

用法语 flutter 一样,仅仅多了space参数方便添加间距

Column({ space: 10 }) {Text("asdf")Text("asdf")
}.alignItems(HorizontalAlign.Start)

6、List

Screenshot_2023-11-24T134447

如果长度超过容器高度,就会滚动

private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]List({ space: 10 }) {ForEach(this.arr, (item: number) => {ListItem() {Text(`${item}`).width('100%').height(100).fontSize(20).fontColor(Color.White).textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0x007DFF)}}, item => item)
}
.height('100%')

7、Grid

构建如下不可滚动网格示例

image-20231124114208959
Grid() {ForEach(this.arr, (item: string) => {GridItem() {Text(item).fontSize(16).fontColor(Color.White).backgroundColor(0x007DFF).width('100%').height('100%').textAlign(TextAlign.Center)}}, item => item)
}
.columnsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局列的数量。
.rowsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局行的数量。
.columnsGap(10) // 设置列与列的间距。
.rowsGap(10) // 设置行与行的间距。
.height(300)

如果需要垂直方向滚动,则关闭掉rowsTemplate即可,如下:

Grid() {ForEach(this.arr, (item: string) => {GridItem() {Text(item).fontSize(16).fontColor(Color.White).backgroundColor(0x007DFF).width(50).height(50).textAlign(TextAlign.Center)}}, item => item)
}
.direction(Direction.Ltr)
.columnsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局列的数量。
// .rowsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局行的数量。
.columnsGap(10) // 设置列与列的间距。
.rowsGap(10) // 设置行与行的间距。
.height(300)
.onScrollIndex((first: number) => {console.info('first:' + first)
})

8、Tabs

使用系统自带的样式:不带图片

Kapture 2023-11-24 at 14.04.14
private controller: TabsController = new TabsController()Column() {Tabs({ barPosition: BarPosition.End, controller: this.controller }) {TabContent() {Column().width('100%').height('100%').backgroundColor(Color.Green)}.tabBar('首页')TabContent() {Column().width('100%').height('100%').backgroundColor(Color.Blue)}.tabBar('我的')}.barMode(BarMode.Fixed)//页签比较多的时候,可以设置滑动页签Scrollable.barWidth('100%') // 设置TabBar宽度.barHeight(60) // 设置TabBar高度.width('100%') // 设置Tabs组件宽度.height('100%') // 设置Tabs组件高度.backgroundColor(0xF5F5F5) // 设置Tabs组件背景颜色.vertical(false)//注意:这个表示底部的 tab 排列方向(与页面与 tab 的排列方向刚好相反)
}
.width('100%')
.height('100%')

自定义样式:带图片。tabBar组件支持@Builder装饰器修饰的函数

image-20231124141238336
struct Index {@State currentIndex: number = 0;private tabsController: TabsController = new TabsController();@Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {Column() {Image(this.currentIndex === targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 })Text(title).fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')}.width('100%').height(50).justifyContent(FlexAlign.Center).onClick(() => {this.currentIndex = targetIndex;this.tabsController.changeIndex(this.currentIndex);})}build() {Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {TabContent() {Column().width('100%').height('100%').backgroundColor('#00CB87')}.tabBar(this.TabBuilder('首页', 0, $r('app.media.icon'), $r('app.media.test')))TabContent() {Column().width('100%').height('100%').backgroundColor('#007DFF')}.c(this.TabBuilder('我的', 1, $r('app.media.icon'), $r('app.media.test')))}.barWidth('100%').barHeight(50).onChange((index: number) => {this.currentIndex = index;})}
}

9、Swiper

Swiper() {Image($r('app.media.video_list0')).borderRadius(12).objectFit(ImageFit.Contain)Image($r('app.media.video_list0')).borderRadius(12).objectFit(ImageFit.Contain)Image($r('app.media.video_list0')).borderRadius(12).objectFit(ImageFit.Contain)
}
.autoPlay(true)

10、Slider进度条

image-20231127105246602
@State slidingProgress: number = 0;// 样式 1
Slider({value: this.slidingProgress,style: SliderStyle.InSet,
}).onChange((value: number, mode: SliderChangeMode) => {this.slidingProgress = Math.floor(value);})
// 样式 2
Slider({value: this.slidingProgress,style: SliderStyle.OutSet,
}).onChange((value: number, mode: SliderChangeMode) => {this.slidingProgress = Math.floor(value);})

11、Video

1、加载本地

需要先在rawfile中添加videoTest.mp4文件

image-20231128165455140
Video({src: $rawfile('videoTest.mp4'),previewUri: $r('app.media.icon'),
})

效果图如下

Kapture 2023-11-28 at 16.56.29

2、加载网络视频

src换成网络视频即可,并且添加网络权限。

需要注意的是:

1、目前我使用鸿蒙模拟器对网络视频的加载体验并不好

2、网络加载器点击播放的时候需要一段下载时间,最好加上loading

Video({src: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",previewUri: $r('app.media.icon'),
}).objectFit(ImageFit.Contain)
image-20231128174112323

3、自定义Video

Kapture 2023-11-29 at 10.44.23
Button("dianji").onClick(()=>{router.pushUrl({url: 'pages/SimpleVideoPlay',params: { source: $rawfile('videoTest.mp4') }//添加视频资源});
})

自定义Video页面SimpleVideoPlay.ets

需要额外不上这些icon
ic_back.png
ic_pause.png
ic_play.png
ic_public_play.png

import router from '@ohos.router';
import { VideoPlayer } from './VideoPlayer';/*** 自定义Video页面*/
@Entry
@Component
struct Play {private source: string = (router.getParams() as Record<string, Object>).source as string;private startIconResource: Resource = $r('app.media.ic_public_play');private backIconResource: Resource = $r('app.media.ic_back');@Provide isPlay: boolean = false;@Provide isOpacity: boolean = false;controller: VideoController = new VideoController();@Provide isLoading: boolean = false;@Provide progressVal: number = 0;@Provide flag: boolean = false;aboutToAppear() {this.source;}onPageHide() {this.controller.pause();}build() {Column() {Row() {Image(this.backIconResource).width(24).height(24).margin({ left: 24 }).onClick(() => {router.back();})Text('返回').fontColor(Color.White).fontSize(24).fontWeight(500).margin({ left: 12 })}.width('100%').margin({left: 12,top: 12}).justifyContent(FlexAlign.Start)Stack() {if (!this.isPlay && !this.isLoading) {Image(this.startIconResource).width(50).height(50).zIndex(2)}if (this.isLoading) {Progress({value: 0,total: 100,type: ProgressType.ScaleRing}).color(Color.Grey).value(this.progressVal).width(80).style({strokeWidth: 15,scaleCount: 15,scaleWidth: 5}).zIndex(1)}VideoPlayer({source: this.source,controller: this.controller}).zIndex(0)}}.height('100%').backgroundColor(Color.Black)}
}

滑块VideoPlaySlider.ets

/*** video slider component*/
@Component
export struct VideoSlider {@Consume isOpacity: boolean;private controller: VideoController = new VideoController();@Consume currentStringTime: string;@Consume currentTime: number;@Consume durationTime: number;@Consume durationStringTime: string;@Consume isPlay: boolean;@Consume flag: boolean;@Consume isLoading: boolean;@Consume progressVal: number;build() {Row({ space: 12 }) {Image(this.isPlay ? $r('app.media.ic_pause') : $r('app.media.ic_play')).width(24).height(24).margin({ left: 12 }).onClick(() => {this.iconOnclick();})Text(this.currentStringTime).fontSize(16).fontColor(Color.White).margin({ left: 12 })Slider({value: this.currentTime,min: 0,max: this.durationTime,step: 1,style: SliderStyle.OutSet}).blockColor("#FFFFFF").width('46.7%').trackColor(Color.Gray).selectedColor("#FFFFFF").showSteps(true).showTips(true).trackThickness(this.isOpacity ? 2 : 4).onChange((value: number, mode: SliderChangeMode) => {this.sliderOnchange(value, mode);})Text(this.durationStringTime).fontSize(16).margin({ right: 12 }).fontColor(Color.White)}.opacity(this.isOpacity ? Number.parseFloat('0.2') : 1).width('100%').alignItems(VerticalAlign.Center).justifyContent(FlexAlign.Center)}/*** icon onclick callback*/iconOnclick() {if (this.isPlay === true) {this.controller.pause()this.isPlay = false;this.isOpacity = false;return;}if (this.flag === true) {this.controller.start();this.isPlay = true;this.isOpacity = true;} else {this.isLoading = true;// The video loading is not complete. The loading action is displayed.let intervalLoading = setInterval(() => {if (this.progressVal >= 100) {this.progressVal = 0;} else {this.progressVal += 10;}}, 100)// The scheduled task determines whether the video loading is complete.let intervalFlag = setInterval(() => {if (this.flag === true) {this.controller.start();this.isPlay = true;this.isOpacity = true;this.isLoading = false;clearInterval(intervalFlag);clearInterval(intervalLoading);}}, 100);}}/*** video slider component onchange callback*/sliderOnchange(value: number, mode: SliderChangeMode) {this.currentTime = Number.parseInt(value.toString());this.controller.setCurrentTime(Number.parseInt(value.toString()), SeekMode.Accurate);if (mode === SliderChangeMode.Begin || mode === SliderChangeMode.Moving) {this.isOpacity = false;}if (mode === SliderChangeMode.End) {this.isOpacity = true;}}
}

Video组件封装VideoPlayer.ets

import prompt from '@ohos.promptAction';
import { VideoSlider } from './VideoPlaySlider';
export function changeSliderTime(value: number): string {let second: number = value % 60;let min: number = Number.parseInt((value / 60).toString());let head = min < 10 ? `${'0'}${min}` : min;let end = second < 10 ? `${'0'}${second}` : second;let nowTime = `${head}${':'}${end}`;return nowTime;
}/*** video controller component*/
@Component
export struct VideoPlayer {private source: string | Resource = '';private controller: VideoController = new VideoController();private previewUris: Resource = $r('app.media.icon');@Provide currentTime: number = 0;@Provide durationTime: number = 0;@Provide durationStringTime: string = '00:00';@Provide currentStringTime: string = '00:00';@Consume isPlay: boolean;@Consume isOpacity: boolean;@Consume flag: boolean;@Consume isLoading: boolean;@Consume progressVal: number;build() {Column() {Video({src: this.source,previewUri: this.previewUris,controller: this.controller}).width('100%').height('88%').controls(false).autoPlay(false).objectFit(ImageFit.Contain).loop(false).onUpdate((event) => {if (event) {this.currentTime = event.time;this.currentStringTime = changeSliderTime(this.currentTime);}}).onPrepared((event) => {this.prepared(event?.duration);}).onFinish(() => {this.finish();}).onError(() => {prompt.showToast({duration: 5000,message: '请检查网络'});})VideoSlider({ controller: this.controller })}}/*** video component prepared callback*/prepared(duration: number) {this.durationTime = duration;let second: number = duration % 60;let min: number = Number.parseInt((duration / 60).toString());let head = min < 10 ? `${'0'}${min}` : min;let end = second < 10 ? `${'0'}${second}` : second;this.durationStringTime = `${head}${':'}${end}`;this.flag = true;}/*** video component finish callback*/finish() {this.isPlay = false;this.isOpacity = false;}
}

12、Web

1、Web组件使用

struct Index {controller: WebController = new WebController();build() {Column() {// 加载网页Web({ src: 'https://developer.harmonyos.com/', controller: this.controller })// 加载本地html// Web({ src: $rawfile('index.html'), controller: this.controller })}}
}

2、Web与js交互

下面示例中:

1、打开App,html回调confirm方法

2、点击按钮,app调用html的test方法

Kapture 2023-12-02 at 18.56.17

鸿蒙页面使用如下

struct Index {controller: WebController = new WebController();build() {Column() {// 鸿蒙调用html的方法Button("鸿蒙按钮").onClick(() => {this.controller.runJavaScript({script: 'test()',callback: (result: string) => {prompt.showToast({duration: 5000,message: result});} });})Web({ src: $rawfile('index.html'), controller: this.controller }).javaScriptAccess(true)// 鸿蒙对外方法.onConfirm((event) => {AlertDialog.show({title: 'title',message: event.message,confirm: {value: 'onAlert',action: () => {event.result.handleConfirm();}},cancel: () => {event.result.handleCancel();}})return true;})// 输出js的日志.onConsole((event) => {console.log('getMessage:' + event.message.getMessage());console.log('getMessageLevel:' + event.message.getMessageLevel());return false;})}}
}

html使用如下

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<body>
</body>
<script type="text/javascript"><!--js回调鸿蒙的方法-->confirm("confirm message from html")<!--js对外方法-->function test() {return "This value is from index.html"}</script>
</html>

四、鸿蒙api

1、UIAbility启动模式

UIAbility当前支持singleton(单实例模式)、multiton(多实例模式)和specified(指定实例模式)3种启动模式

  • singleton(单实例模式)
img

如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例.

在module.json5配置文件中的"launchType"字段配置为"singleton"即可。

{"module": {// ..."abilities": [{"launchType": "singleton",// ...}]}
}
  • standard(标准实例模式)
img

每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。

 "launchType": "standard",
  • specified(指定实例模式)
img

针对一些特殊场景使用(例如文档应用中每次新建文档希望都能新建一个文档实例,重复打开一个已保存的文档希望打开的都是同一个文档实例)

"launchType": "specified",

2、UIAbility组件生命周期

UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态

Ability-Life-Cycle

需要注意的是:UIAbility没有WindowStageCreate、WindowStageDestroy,这两个是WindowStage的生命周期。

UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中设置UI界面加载、设置WindowStage的事件订阅。

img

3、toast

import prompt from '@ohos.promptAction';Button("点击toast").onClick(() => {prompt.showToast({duration: 5000,message: '点击toast'});
})

4、Preferences存储

注意:初始化需要await,并且需要context参数,建议在EntryAbility的onCreate方法中

await sharePreferenceUtil.init(this.context);
import dataPreferences from '@ohos.data.preferences';const KEY_APP_FONT_SIZE = 'appFontSize';
/*** SP工具类*/
export class SharePreferenceUtil {preferences: dataPreferences.Preferences;// 初始化(注意:初始化是异步方法,需要await)async init(context: Context) {this.preferences = await dataPreferences.getPreferences(context, 'myPreferences');}// 存储saveDefaultFontSize(fontSize: number) {this.preferences.has(KEY_APP_FONT_SIZE).then(async (isExist: boolean) => {if (!isExist) {await this.preferences.put(KEY_APP_FONT_SIZE, fontSize);this.preferences.flush();}}).catch((err: Error) => {});}// 更新async saveChangeFontSize(fontSize: number) {await this.preferences.put(KEY_APP_FONT_SIZE, fontSize);this.preferences.flush();}// 获取async getChangeFontSize() {let fontSize: number = 0;fontSize = await this.preferences.get(KEY_APP_FONT_SIZE, fontSize) as number;return fontSize;}// 删除async deleteChangeFontSize() {let deleteValue = this.preferences.delete(KEY_APP_FONT_SIZE);deleteValue.then(() => {}).catch((err: Error) => {});}
}const sharePreferenceUtil = new SharePreferenceUtil();export default sharePreferenceUtil;

五、状态管理与数据同步

1、组件状态管理装饰器和@Builder装饰器:

组件状态管理装饰器用来管理组件中的状态,它们分别是:@State、@Prop、@Link。

  • @State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。
  • @Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但更改不会通知给父组件,即@Prop属于单向数据绑定。
  • @Link装饰的变量可以和父组件的@State变量建立双向数据绑定,需要注意的是:@Link变量不能在组件内部进行初始化。
  • @Builder装饰的方法用于定义组件的声明式UI描述,在一个自定义组件内快速生成多个布局内容。

组件内的状态管理:@State
从父组件单向同步状态:@Prop
与父组件双向同步状态:@Link
监听状态变化:@Watch
跨组件层级双向同步状态:@Provide和@Consume

1、父页面同步数据给子页面:@Prop

2、子页面同步数据给父页面:@Link

下面示例中

1、父组件把clickIndex通过 Props 传递给子页面

2、点击子组件后,通过 Link 把修改后的clickIndex值传递给页面

3、其余子组件 Watch 了clickIndex,并同时修改组件中的isExpanded值

Kapture 2023-12-03 at 10.04.34

页面Index.ets

import TestItem from './TestItem';@Entry
@Component
struct Index {private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]@State clickIndex: number = -1;build() {Column() {ForEach(this.arr, (item: number, index: number) => {TestItem({index: this.arr[index], //@Prop传递给子组件数据clickIndex: $clickIndex, //@Link双向绑定数据})}, item => item)}.width('100%').height('100%')}
}

组件TestItem.ets

@Component
export default struct TestItem {@Prop index: number; //当前 item 序号@State isExpanded: boolean = false; //当前是否展开// @Link修饰是为了同步数据到父组件,@Watch是为了监听回调给onClickIndexChanged@Link @Watch('onClickIndexChanged') clickIndex: number; //点击的序号onClickIndexChanged() {this.isExpanded = this.clickIndex == this.index;}build() {Button(this.index + '、是否展开:' + this.isExpanded).width('100%').height(this.isExpanded ? 80 : 40).fontSize(20).fontColor(Color.White).borderRadius(10).backgroundColor(0x007DFF).margin({ top: 10 }).onClick(() => {this.clickIndex = this.index;})}
}

2、子组件callback 回调父页面

子组件声明callback 方法

// 组件
@Component
export default struct TestItem {callback?: (index: number) => void;build() {Button('子组件').width('100%').height(40).fontSize(20).fontColor(Color.White).borderRadius(10).backgroundColor(0x007DFF).onClick(() => {// this.clickIndex = this.index;if (this.callback !== undefined) {this.callback(123)}})}
}

父页面传入callback方法

TestItem({callback: (index:number): void => {console.warn("index:",index)}
})

六、弹窗

1、警告弹窗AlertDialog

img
AlertDialog.show({title: '删除联系人', // 标题message: '是否需要删除所选联系人?', // 内容autoCancel: false, // 点击遮障层时,是否关闭弹窗。alignment: DialogAlignment.Bottom, // 弹窗在竖直方向的对齐方式offset: { dx: 0, dy: -20 }, // 弹窗相对alignment位置的偏移量primaryButton: {value: '取消',action: () => {}},secondaryButton: {value: '删除',fontColor: '#D94838',action: () => {}},cancel: () => { // 点击遮障层关闭dialog时的回调}}
)

2、文本选择弹窗TextPickerDialog

img
@State select: number = 2;
private fruits: string[] = ['苹果', '橘子', '香蕉', '猕猴桃', '西瓜'];TextPickerDialog.show({range: this.fruits, // 设置文本选择器的选择范围selected: this.select, // 设置初始选中项的索引值。onAccept: (value: TextPickerResult) => { // 点击弹窗中的“确定”按钮时触发该回调。// 设置select为按下确定按钮时候的选中项index,这样当弹窗再次弹出时显示选中的是上一次确定的选项this.select = value.index;console.info("TextPickerDialog:onAccept()" + JSON.stringify(value));},onCancel: () => { // 点击弹窗中的“取消”按钮时触发该回调。console.info("TextPickerDialog:onCancel()");},onChange: (value: TextPickerResult) => { // 滑动弹窗中的选择器使当前选中项改变时触发该回调。console.info("TextPickerDialog:onChange()" + JSON.stringify(value));}
})

3、日期滑动选择弹窗DatePickerDialog

image-20231129200521350
selectedDate: Date = new Date("2010-1-1")DatePickerDialog.show({start: new Date("1900-1-1"), // 设置选择器的起始日期end: new Date("2023-12-31"), // 设置选择器的结束日期selected: this.selectedDate, // 设置当前选中的日期lunar: false,onAccept: (value: DatePickerResult) => { // 点击弹窗中的“确定”按钮时触发该回调// 通过Date的setFullYear方法设置按下确定按钮时的日期,这样当弹窗再次弹出时显示选中的是上一次确定的日期this.selectedDate.setFullYear(value.year, value.month, value.day)console.info("DatePickerDialog:onAccept()" + JSON.stringify(value))},onCancel: () => { // 点击弹窗中的“取消”按钮时触发该回调console.info("DatePickerDialog:onCancel()")},onChange: (value: DatePickerResult) => { // 滑动弹窗中的滑动选择器使当前选中项改变时触发该回调console.info("DatePickerDialog:onChange()" + JSON.stringify(value))}
})

4、自定义弹窗

通过装饰器@CustomDialog定义的组件来实现,然后结合CustomDialogController来控制自定义弹窗的显示和隐藏。

image-20231127102353922

弹窗组件AddTargetDialog.ets绘制

@CustomDialog
export default struct AddTargetDialog {@State subtaskName: string = '';private controller?: CustomDialogController;onClickOk?: (value: string) => void;build() {Column() {Text('添加子目标').width('100%').fontSize('20fp').fontWeight(500).fontColor('#182431').textAlign(TextAlign.Start)TextInput({ placeholder: '请输入子目标名称'}).placeholderColor(Color.Grey).placeholderFont({ size: '16fp'}).caretColor(Color.Blue).backgroundColor('#0D182431').width('100%').height('40%').margin({ top: '6%' }).fontSize('16fp').fontColor("#182431").onChange((value: string) => {this.subtaskName = value;})Blank()Row() {Button('取消').dialogButtonStyle().onClick(() => {this.controller?.close();})Divider().vertical(true)Button('确定').dialogButtonStyle().onClick(() => {if (this.onClickOk !== undefined) {this.onClickOk(this.subtaskName);}})}.width('70%').height('10%').justifyContent(FlexAlign.SpaceBetween)}.padding('24vp').height('168vp').width('90.3%').borderRadius(32).backgroundColor(Color.White)}
}/*** Custom button style.*/
@Extend(Button) function dialogButtonStyle() {.fontSize('16fp').height('32vp').width('96vp').backgroundColor(Color.White).fontColor('#007DFF')
}

页面使用

@Entry
@Component
struct Index {dialogController: CustomDialogController = new CustomDialogController({builder: AddTargetDialog({onClickOk: (value: string): void => {console.warn("value:",value)this.dialogController.close();// 关闭}}),alignment: DialogAlignment.Bottom,offset: {dx: 0,dy: '-16vp'},customStyle: true,autoCancel: false});build() {Button("点击打开弹窗").onClick(()=>{this.dialogController.open()// 打开})}
}

七、动画

添加animation属性就好,由State驱动。

Kapture 2023-11-30 at 20.19.59
struct Index {@State iconWidth: number = 30;onPageShow() {this.iconWidth = 90;}build() {Column() {Image($r('app.media.icon')).width(this.iconWidth).margin(10).objectFit(ImageFit.Contain).animation({duration: 2000,tempo: 3.0, //动画的播放速度delay: 0,curve: Curve.Linear,playMode: PlayMode.Normal,iterations: -1, //播放次数,默认一次,设置为-1时表示无限次播放。})}}
}

八、网络请求

注意:多个请求可以使用同一个httpRequest对象,httpRequest对象不能复用,因为它支持request、destroy、on和off方法,例如取消网络请求httpRequest.destroy();

import http from '@ohos.net.http';let httpRequest = http.createHttp();
let promise = httpRequest.request("http://www.baidu.com",{// 请求方式method: http.RequestMethod.POST,// 请求的额外数据。extraData: {"param1": "value1","param2": "value2",},// 可选,默认为60sconnectTimeout: 60000,// 可选,默认为60sreadTimeout: 60000,// 开发者根据自身业务需要添加header字段header: {'Content-Type': 'application/json'}});
promise.then((data) => {if (data.responseCode === http.ResponseCode.OK) {console.info('Result:' + data.result);console.info('code:' + data.responseCode);}
}).catch((err) => {console.info('error:' + JSON.stringify(err));
});

九、路由

在如下目录下注册页面

/entry/src/main/resources/base/profile/main_pages.json

跳转代码

import router from '@ohos.router';router.pushUrl({url: 'pages/SecondPage',params: {src: 'Index页面传来的数据',}
}, router.RouterMode.Single)

鸿蒙参考资料

鸿蒙第一课视频,对应代码Codelabs

完整版的功能demo

官方文档

HarmonyOS点石成金

鸿蒙系统系列教程6-鸿蒙系统项目结构解析

鸿蒙开发者学习笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/194796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法】单调队列 滑动窗口最大值

文章目录 例题——239. 滑动窗口最大值相关练习1438. 绝对差不超过限制的最长连续子数组解法1——两个单调队列分别维护最大值和最小值解法2——有序集合TreeMap 2398. 预算内的最多机器人数目解法1——二分答案 单调队列解法2——双指针 单调队列 &#xff08;不固定大小的滑…

并发编程1:线程的基本概念

一、进程、线程、用户线程&原生线程、优先级、守护线程 什么是进程 是程序一次执行的过程&#xff0c;是系统运行程序的基本单位。系统运行一次程序&#xff0c;就是一个进程从创建到关闭的过程。Java 项目从 main 方法启动&#xff0c;就是启动了一个 JVM 进程&#xff…

C++面试宝典第1题:爬楼梯

题目 小乐爬楼梯&#xff0c;一次只能上1级或者2级台阶。楼梯一共有n级台阶&#xff0c;请问总共有多少种方法可以爬上楼&#xff1f; 解析 这道题虽然是一道编程题&#xff0c;但实际上更是一道数学题&#xff0c;着重考察应聘者的逻辑思维能力和分析解决问题的能力。 当楼梯只…

理解Zookeper

一、什么是Zookeper Zookeper是一个可以作为注册中心、配置中心、分布式锁的分布式解决方案 二、数据一致性 一致性分为强一致性、弱一致性、最终一致性 Zookeper可以保持数据的强一致性&#xff08;CP&#xff09; 主要是解决写、集群节点的同步、读之间的关系 强一致性&a…

nodejs微信小程序+python+PHP问卷调查系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

This详细用法

this的指向 this有5种指向&#xff0c;在普通函数中&#xff0c;this指向window&#xff1b;在构造函数中&#xff0c;this指向创建的对象&#xff1b;在方法声明中&#xff0c;this指向调用者&#xff1b;在定时器中&#xff0c; this指向window&#xff1b;在事件中&#xff…

超大规模集成电路设计----MOS器件原理(二)

本文仅供学习&#xff0c;不作任何商业用途&#xff0c;严禁转载。绝大部分资料来自----数字集成电路——电路、系统与设计(第二版)及中国科学院段成华教授PPT 超大规模集成电路设计----MOS器件原理&#xff08;二&#xff09; 半导体物理知识补充介绍1. 半导体材料2. 固体类型…

RK3568平台开发系列讲解(Linux系统篇)通过OF函数获取属性

🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍通过OF函数获取属性。 一、获取获取属性 ① of_find_property 函数 of_find_property 函数用于在设备树中查找节点 下具有指定名称的属性。 如果找到了该属性, 可以通过返回的属性结构体…

Shell循环:expect(二)

expect实战&#xff1a;公钥推送 一、准备工作&#xff1a;安装expect&#xff0c;装备公钥 二、通过shell循环判断主机在线 #!/bin/bash #脚本编写 #创建一个IP地址文件 >ip.txt #使用for循环ping测试主机是否在线 for i in {3..254} do{ip192.168.151.$iping -c1 -W…

【C++练级之路】【Lv.2】类和对象(上)(类的定义,访问限定符,类的作用域,类的实例化,类的对象大小,this指针)

目录 一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装4.1 访问限定符4.2 封装 五、类的作用域六、类的实例化七、类的对象大小的计算7.1 类对象的存储方式猜测7.2 如何计算类对象的大小 八、类成员函数的this指针8.1 this指针的引出8.2 this指…

【算法】单调栈题单(矩阵系列、字典序最小、贡献法)⭐

文章目录 题单来源经典题单496. 下一个更大元素 I&#xff08;单调栈模板题&#xff09;503. 下一个更大元素 II&#xff08;单调栈循环数组&#xff09;2454. 下一个更大元素 IV&#xff08;第二个更大的元素&#xff1a;两个单调栈&#xff09;456. 132 模式&#xff08;单调…

操作系统||经典进程同步问题之读者-写者问题

一个数据文件或记录可被多个进程共享 【问题描述】 有读者和写者两组并发进程&#xff0c;共享一个文件&#xff0c;当两个或两个以上的读进程同时访问共享数据时不会产生副作用&#xff0c;但若有某个写进程和其他进程&#xff08;读进程或写进程&#xff09;同时访问共享数…

Autosar COM通信PDU

文章目录 Autosar 中各个PDU所在示意图PDU的分类PDU 和 SDU 的关系I-PDUN-PDUL-PDU相关协议其他参考 Autosar 中各个PDU所在示意图 PDU的分类 在Autosar 中&#xff0c;主要有 I-PDU、N-PDU和 L-PDU 三种。 L-PDU&#xff1a;Data Link Layer PDU&#xff0c;数据链路层PDUN-…

[C/C++]数据结构 关于二叉树的OJ题(利用分治思想解决难题)

题目一: 单值二叉树 &#x1f6a9;⛲&#x1f31f;⚡&#x1f966;&#x1f4ac; &#x1f6a9;题目链接:力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 ⛲题目描述: 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。…

制作飞腾(arm)芯片架构的nexus镜像

nexus官方没有arm架构的镜像&#xff0c;下面介绍一种自己制作镜像的方式 1、事先准备 在一个arm架构机器上安装docker下载nexus的linux版(https://www.sonatype.com/download-oss-sonatype)下载centos的arm架构镜像(docker pull centos-centos8.4.2105)下载arm版本的java8(ht…

POJ 3734 Blocks 动态规划(矩阵的幂)

一、题目大意 我们要给排成一行的区块涂颜色&#xff0c;可以选择红、绿、蓝、黄四种&#xff0c;要求红和绿的块都必须是偶数个&#xff0c;求出最终的涂色方式&#xff0c;对10007取余。 二、解题思路 我们设三个数列A&#xff0c;B和C&#xff1a; 1、A代表红色和绿色都…

SpringSecurity 三更草堂 学习笔记

SpringSecurity从入门到精通 0. 简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的…

前端笔记(二):CSS 选择器与特性

CSS&#xff08;层叠样式表&#xff09;是一种样式表语言&#xff0c;用于描述HTML或XML文档的呈现方式。它定义了如何在屏幕、纸张或其他媒体上显示文档的样式、布局和外观。 里面的代码由 选择器 { } 组成 体验 CSS CSS 可以让我们界面变得更加美观&#xff0c;这是 CSS 的…

同旺科技 分布式数字温度传感器 -- Modbus Poll测试

内附链接 1、数字温度传感器 主要特性有&#xff1a; ● 支持PT100 / PT1000 两种铂电阻&#xff1b; ● 支持 2线 / 3线 / 4线 制接线方式&#xff1b; ● 支持5V&#xff5e;17V DC电源供电&#xff1b; ● 支持电源反接保护&#xff1b; ● 支持通讯波特率1200bps、2…

YOLOv8 区域计数 | 入侵检测 | 人员闯入

大家好,昨天的 YOLOv8 新增加了一个功能,区域计数,用这个功能我们能实现很多的任务, 比如入侵检测,流量统计,人员闯入等,使用方式也非常的方便,但是一定要使用最新版的 YOLOv8 代码(2023/12/03更新的代码)。 低版本是不具备这个功能的,上面是演示效果。 使用非常的方…