鸿蒙开发

项目结构

在pages同级可以创建一个文件夹,命名viewModel,用于存放类型

export default class ShopInfo {
  title: string
}

在pages统计可以创建一个文件夹,明明 model,用于编写 pages中的方法,比如要发送接口,可以这么写:

import http from '@ohos.net.http';
import ShopInfo from '../viewmodel/ShopInfo'
class ShopModel {
  baseUrl: string = 'http://localhost:3000'
  pageNo: number = 1
  getShopList(): Promise<ShopInfo[]> {
    return new Promise((resolve, reject) => {
      let httpRequest = http.createHttp()
      httpRequest.request(
        `${this.baseUrl}/shops?pageNo=${this.pageNo}&pageSize=3`,
        {
          method: http.RequestMethod.GET
        }
      )
        .then((response: http.HttpResponse) => {
          if(response.responseCode === 200) {
            resolve(JSON.parse(response.result.toString()))
          }else {
            console.log('查询失败')
          }
        })
        .catch((err: Error) => {
          console.log('查询失败')
        })
    })
  }
}
const shopmodel = new ShopModel()

语法

@State:自监控一个变量,有点像是vue的数据绑定,当这个数据改变时,页面上的内容也会同步更新

@Entry:代表这是一个入口型的组件,可以通过页面去访问,而不是只能用于别的页面调用(页面组件)

@Component:代表这是一个自定义组件

循环控制

ForEach

用法:

ForEach(
    arr: Array, // 要遍历的数组数据
    (item: any, index?: number) => { // 页面组件生成函数
      Row() {
        
      }
    },
    (item: any, index?: number): string => { 
      // 键生成函数,为数组每一项生成一个唯一标识,相当于 Vue 中 v-for 中的key
      return String(index)
    }
  )

自定义组件

在 etc 目录下新建 components 目录

使用 @Component 定义一个组件

导出:

@Component
export struct HeaderComponent {
  private title: string = '商品列表';
  build() {
    // 商品列表文字
    Row() {
      Image($r('app.media.back'))
        .width(30)
      Text(this.title)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Blank()
      Image($r('app.media.refresh'))
        .width(30)
    }
    .margin(10)
    .width('100%')
    .height(30)
  }
}

导入:

import {HeaderComponent} from '../components/HeaderComponent'
HeaderComponent({ title: '收藏信息'})

传递参数

  • 在子组件中,使用 private 定义一个变量,并可以赋值,使用 this.属性 读取该属性

  • 在调用子组件时,在括号中传递一个对象,对象就是这个属性,传递什么就是什么

自定义构建函数

使用关键词 @Builder定义,这个就相当于 React 中的把渲染单独拎出来,弄成一个函数,返回值就是元素。

⚠注意:这里不需要返回,不需要return。

用法1:

这样用需要写在 struct 外部,可以在任何地方使用

// 全局自定义构建函数
@Builder function ItemCard(item: goodItem) {
    Row({ space: 10}) {
      Image($rawfile(item.imgUrl))
        .height(80)
      Column({space: 4}) {
        Text(item.name)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        if(item.discount !== 0) {
          Text('¥' + item.price)
            .fontSize(18)
            .fontColor('gray')
            .decoration({type: TextDecorationType.LineThrough})
          Text('折扣价:¥' + (item.price - item.discount))
            .fontSize(18)
            .fontColor('red')
        }else {
          Text('¥' + item.price)
            .fontSize(18)
            .fontColor('red')
        }
      }
      .alignItems(HorizontalAlign.Start)
    }
    .shadow({radius: 10, color: 'black', offsetX: 0, offsetY: 0})
    .padding(10)
    .borderRadius(10)
    .height(120)
    .backgroundColor('white')
    .width('100%')
}

然后在遍历中使用:

List({ space: 8 }) {
  ForEach(
    this.goodsList,
    (item, index) => {
      ListItem() {
        ItemCard(item)
      }
    },
    (item, index) => {
      return String(index)
    }
  )
}

用法2:

写在 struct 内部,不需要加 function

使用时,需要使用this.

@Builder ItemCard(item: goodItem) {}

自定义公共样式

可以把样式抽取出来,抽取为公共样式,使用关键词@Styles

⚠注意:抽取出来的公共样式,必须是全部可以使用的属性,如果是 fontSize、fontColor 这种,会报错 ①

用法1:

写在 struct 外部,作为全局使用

@Styles function fillScreen() {
  .width('100%')
  .padding(10)
  .backgroundColor('#ffcacaca')
}
Row() {
}
  .fillScreen()

用法2:

写在 struct 内部,作为当前组件局部使用

@Styles fillScreen() {
  .width('100%')
  .padding(10)
  .backgroundColor('#ffcacaca')
}

①:如果想要解决由于公共样式(类似 fontSize、fontColor 这种不同步产生的问题),需要使用@Extend关键词,用法:

@Extend(Text) function priceText() {
  .fontColor('gray')
  .fontSize(18)
}

状态管理

@State

  • 使用@State装饰器标记的变量必须进行初始化,不能为空值

  • ⚠如果是对象中嵌套对象,修改其中对象的属性值,不会触发渲染

  • ⚠数组中的元素更改,不会造成重新渲染;重新赋值可以,但更改不会造成重新渲染

  • 使用@State装饰的数据,可以传递给子组件,但当父组件改变数据时,子组件无法感知到

@Prop

✅ 数据的单向传递

使用@Prop在子组件中定义数据,父组件使用@State定义的数据传递给子组件,子组件可以感知到父组件传递数据的变化。

  • ⚠️注意:@Prop为单向传递,仅能从父组件传递子组件。

  • ⚠️注意:@Prop只支持传递string、number、boolean、enum类型。

  • 父组件是对象类型,子组件可以是对象中的属性。

  • 不可以传递数组、any类型。

用法:

@Entry
@Component
struct father {
  @State finishTask: number = 0;
  @State totalTask: number = 0;
  TaskStatistics({
    finishTask: this.finishTask,
    totalTask: this.totalTask
  })
}
@Component
struct TaskStatistics {
  @Prop finishTask: number;
  @Prop totalTask: number;
}

✅ 数据双向传递

使用@Link在子组件中定义数据,父组件使用@State定义的数据传递给子组件,子组件可以感知到父组件传递数据的变化,同时子组件修改该数据,父组件也会感知到进行重新渲染。

使用

  • ⚠️注意:@Link传递的数据,必须使用$进行装饰,不需要使用this。

  • ⚠️注意:不同于@Prop,@Link可以传递大多数类型,string、number、boolean、enum、object、class、以及数组。

  • 数组中的元素增、删、改也会引起刷新。

  • 嵌套类型以及数组中的对象属性改变不会触发视图更新。

@Entry
@Component
struct father {
  @State finishTask: number = 0;
  @State totalTask: number = 0;
  TaskStatistics({
    finishTask: $finishTask,
    totalTask: $totalTask
  })
}
@Component
struct TaskStatistics {
  @Link finishTask: number;
  @Link totalTask: number;
}

@Provide 和 @Consume

✅ 数据双向传递

类似于发布订阅模式,父组件直接将需要传递给后代组件的数据使用 @Provide 进行装饰;哪个子组件需要这个数据,就使用 @Consume 进行接收。

class StatInfo {
  finishTask: number = 0
  totalTask: number = 0
}
@Entry
@Component
struct father {
  @Provide stat: StatInfo = new StateInfo();
  TaskStatistics()
}
@Component
struct TaskStatistics {
  @Consume stat: number;
  @Link totalTask: number;
}

✅ 双向数据传递

@ObjectLink和@Observed用于在嵌套对象或数组元素为对象的场景中进行双向数据同步。

  • 在上边的内容中,当修改的数据为数组中的对象属性时,无法监测到变化,这个就是用于解决问题的。

  • 如果在一个类中引入了另外一个类,则需要将每个类都添加上@Observed。

@Observed
class Person {
  name: string
  age: number
  gf: number
  constructor(name: string, age: number, gf?: Person){
    this.name = name 
    this.age = age 
    this.gf = gf
  }
}
@Entry
@Component
struct Parent {
  @State p: Person = new Person('Jack', 21, new Person('Rose', 18))
  build() {
    Column() {
      Child({p: this.p.gf})
        .onClick(() => this.p.gf.age++) 
    }
  }
}
@Component
struct Child {
  @ObjectLink p: Person
  build() {
    Column() {
      Text(`${this.p.name} : ${this.p.age}`)
    }
  }
}

⚠️注意:

在开发过程中,我们可能你会遇到这个场景:

  • 一个方法在父组件中(比如计算父组件中的数据)

  • 现在希望在子组件中调用这个父组件的方法

那么,我们可以将父组件中的这个方法,传递给子组件

父组件:

TaskItem({item: item, onTaskChange: this.handleTaskChange.})

子组件:

onTaskChange: () => void

但是这样,可能会出现一个问题,当父组件这个方法中使用 this 时,由于父组件中的 this 指向的为父组件,如果直接传递给子组件,那饿这个 this 为子组件,可能会出现 undefined 的报错

这种情况下,需要绑定方法 this 指向后,再传递:

TaskItem({item: item, onTaskChange: this.handleTaskChange.bind(this)})

✅ 使用 bind 改变指向后,传递。

路由

基本路由

在鸿蒙中,使用router进行路由管理。

  • 页面栈的上限为 32 个页面,使用 router.clear() 方法可以清空页面栈

  • Router有两种页面跳转模式:

  • router.pushUrl():压入页面栈,可以使用 router.back() 返回当前页。

  • router.replaceUrl():替换当前页。

  • Router有两种页面实例模式:

  • Standard:标准实例模式,每次跳转都会新建一个目标页并压入栈顶(默认模式)

  • (推荐)Single:单实例模式,如果目标页已经在栈中,则离栈顶最近的同Url页面会被移动到栈顶并重新加载。

跳转用法:

router.pushUrl(
  // RouterOptions
  {
    // url: 目标页面路径
    url: 'pages/ImagePage',
    // params: 传递的参数(可选)
    params: {id: 1}
  },
  // 页面模式:RouterMode枚举
  router.RouterMode.Single,
  // 异常毁掉函数,错误码:
  // - 100001: 内部错误,可能是渲染失败
  // - 100002: 路由地址错误
  // - 100003: 路由栈数量超过32
  err => {
    if(err) {
      console.log('路由跳转失败')
    }
  }
)

✅ 获取页面路由参数:router.getParams()返回上一页:router.back()

✅ 返回到指定页,并携带参数:

router.back(
  {
    url: 'pages/Index',
    params: {id: 10}
  }
)

路由配置

main/resources/profile/main_pages.json中配置页面路由。

将pages下的文件都配置到当前文件 。

动画

属性动画

可以通过添加 .animate 添加动画。

哪个元素,后续状态会出现变化,就添加上这个属性。

用法:

Text('^_^')
  .postion({
    x: 10, 
    y: 0
  })
  .rotate({
    angle: 0, // 旋转角度
    centerX: '50%', // 旋转中心横坐标
    centerY: '50%'  // 旋转中心纵坐标
  })
  .animation({
    duration: 1000, // 动画持续时间
    curve: Curve.EaseInOut // 变化曲线
  })

属性:

显式动画

显式动画是通过全局animateTo函数来修改组件属性,实现属性变化时的渐变过渡效果。

用法:

@State postionX: number = 100
@State postionY: number = 10
Text('^_^')
  .postion({
    x: this.positionX, 
    y: this.positionY
  })
  .rotate({
    angle: 0, // 旋转角度
    centerX: '50%', // 旋转中心横坐标
    centerY: '50%'  // 旋转中心纵坐标
  })
// 显式调用animateTo函数触发动画
animateTo(
  {duration: 1000},
  () => {
    // 修改组件属性关联的状态变量
    // 这样就会自动进行更新,并添加动画
    this.positionX -= 10
  }
)

属性同上

组件转场动画

组件转场动画是在组件插入或移除时的过渡动画,通过组件的transition属性来配置。

注意⚠️:transition只是设置一种离场方式,还是需要使用animateTo进行调用。

用法:

@State isShow: boolean = true
...
if(this.isShow){
  Text('^_^')
    .transition({
      opcity: 0,
      rotate: {angle: -360},
      scale: {x: 0, y: 0}
    })
}
...
// 显式调用 animateTo 函数触发动画
animateTo(
  {duration: 1000},
  () => {
    this.isShow = false
  }
)

属性:

Stage模型

Stage模型概述

对于一个鸿蒙应用,分为两个模块

  • Ability Module:会被编译为 HAP 模块,每一个都可以一个独立的运行。

  • Library Module:会被编译为 HSP 模块

Stage应用配置文件

  • 在全局项目中,AppScope/app.json5(应用的全局配置信息)

  • 在每个模块中,在resources/module.json5(Module的配置信息)

UIAbility生命周期

  • Foreground:前台,指当前手机打开此页面

  • Background:后台,只手机切换应用,已不在此应用

  • Destory:用户退出应用

页面及组件生命周期

  • 页面生命周期钩子仅能在页面中使用,也就是加了 @Entry 的组件

  • 使用router.push不会造成页面销毁(页面显示隐藏),使用 router.replace会造成页面销毁

UIAbility的启动模式

✅ Singleton模式:一个 UIAbility 只存在一个实例,任务列表不会出现多个

✅ standard模式:一个 UIAbility 会存在多个实例,每次都会重新创建

以上两种模式,只需要在 module.json5 文件中,配置 lunchType 关键词即可

✅ specified模式:每个 UIAbility 会有一个key标识,如果启动的是这个 key 的,直接拉起,如果没有这个 key 的实例,则创建。

❓ 如何实现第三种模式(specified)?
答:比如说我们要实现这样一个功能,一个文档列表页面,当我们点击新建文档时,我们希望跳转到新建文档的Abliity,这时我也可以通过任务列表返回当前的文档列表Ability。

分为三步:

  1. 在当前文档列表页面编辑(编写跳转Ability)

  1. 在 ets 文件夹下右键,新建 Ability,并起名

  2. 在 class 中获取上下文

private context = getContext(this) as common.UIAbilityContext
  1. 在新建文档页面按钮点击事件上绑定方法:

// 指定要跳转到的UIAbility
let want: Want = {
  deviceId: '', // deviceId为空表示本设备
  bundleName: 'com.example.myapplication', // 在 AppScope/app.json5 中的 bundleName
  moduleName: 'entry', // 跳转到哪个模块,在 module.json5 中的 name
  abilityName: '', // 跳换到哪个Ability去,在 module.json5 中a bilities 数组中,可以找到name
  parameters: {
    instanceKey: `idx_${this.index++}` // 唯一的key
  }
}
  1. 跳转到 Ability 去

this.context.startAbility(want)
  1. 创建AbilityStage

  1. 在 ets 文件夹下,创建文件夹,起名 myabilitystage

  2. 在新建文件夹下,创建 MyAbilityStage.ts文件

  3. 编写 stage 代码:

import AbilityStage from '@ohos.app.ability.AbilityStage'
import Want from '@ohos.app.ability.Want'
export default class MyAbilityStage extends AbilityStage {
  onAcceptWant(want: Want): string {
    if(want.abilityName === 'DocumentAbility') {
      return `DocAbilityInstance_${want.parameters.instanceKey}`
    }
    return ''
  }
}
  1. 在 module.json5 中配置,指定舞台

{
  "moudle": {
    ...
    "srcEntry": './ets/myabilitystage/MyAbilityStage.ts'
    ...
  }
}

网络请求

http网络请求

  1. 导入http模块

import http from '@ohos.net.http

  1. 使用http模块发送请求,处理响应

let httpRequest = http.createHttp()
httpRequest.request(
  'http://localhost:3000/users,
  {
    // 请求方法
    method: http.RequestMethod.GET,
    // 请求参数
    extraData: {'param1': 'value1'}
  }
)
// 请求成功
.then((resp: http.HttpResponse) => {
  if(resp.responseCode === 200) {
  }
})
// 请求失败
.catch((err: Error) => {
})

HttpRequestOptions:

  • method: RequestMethod:请求方式

  • extraData: string | Object:请求参数(当传递string时,将会拼接到路径上,相当于get请求)

  • header: Object:请求头字段

  • connectTimeOut: number:连接超时时间,单位毫秒,默认是60000ms

  • readTimeout: number:读取超时间,同上

axios第三方网络请求

  1. 下载和安装ohpm:鸿蒙中的包管理工具,相当于 npm

  2. 下载和安装axios

  1. 下载axios:ohpm install @ohos/axios

  2. 开放网络权限

"requestPermissions": [{
  "name": "ohos.permission.INTERNET"
}]

所有的三方库:

OpenHarmony三方库中心仓

所有下载的依赖,将会显示在oh-package.json5文件中(相当于package.json文件)

‼️安装后出现一个问题:ERROR: The compatibleSdkVersion 9 cannot be smaller than version 12 declared in library [:library] as the library might be using APIS not available in 9

这是因为版本不匹配,如果下载的是 2.2.2 ,则在oh-package.json5文件中修改成 2.2.0

使用时,首先导入 axios(import axios from '@ohos/axios),其余和平时使用一样

数据持久化

用户首选项

用户首选项(Preference)为应用提供 key-value 键值型的数据处理能力,支持应用持久化轻量级数据。

使用:

  1. 导入首选项模块

import dataPreference from '@ohos.data.preferences'
  1. 获取首选项实例,读取指定文件

dataPreference.getPreferences(this.context, 'MyAppPreferences')
  .then(preferences => {
    // 获取成功
  })
  .catch(reason => {
    // 获取失败
  })
  1. 数据操作

// 3.1 写入数据,如果已经存在则会覆盖,可利用 .has() 查看是否存在
preferences.put('key', val)
  .then(() => preferences.flush())
  .catch(reason => {})
// 3.2 删除数据
preferences.delete('key')
  .then(() => {})
  .catch(reason => {})
// 3.3 查询数据
preferences.get('key', 'defaultValue')
  .then(value => console.log('查询成功')
  .catch(reason => console.log('查询失败')

说明:

  • Key 为 string 类型, 要求非空且长度不超过80字节

  • Value 可以是 string、number、boolean 及以上类型数组,大小不超过 8192 字节

  • 数据量建议不超过一万条

详细使用:

  1. 在 common/utils 文件夹中新建 PreferencesUtil.ts 文件,编写代码

import preferences from '@ohos.data.preferences';
class PreferencesUtil {

  prefMap: Map<string, preferences.Preferences> = new Map()

  async loadPreference(context, name: string) {
    try {
      let pref = await preferences.getPreferences(context, name);
      this.prefMap.set(name, pref)
      console.log('testTag', `加载Preferences[${name}]成功`)
    }catch (e) {
      console.log('testTag', '失败')
    }
  }
  // 存
  async putPreferenceValue(name: string, key: string, value: preferences.ValueType) {
    if(!this.prefMap.get(name)) {
      console.log('testTag', `Preferences[${name}]未初始化`)
      return
    }
    try {
      let pref = this.prefMap.get(name)
      // 写入操作
      await pref.put(key, value)
      // 刷盘
      await pref.flush()
      console.log('testTag', `Preferences[${name}]保存成功`)
    }catch (e) {
      console.log('testTag', `Preferences[${name}]保存失败`)

    }

  }

  // 读
  async getPreferenceValue(name: string, key: string, defaultValue: preferences.ValueType) {
    if(!this.prefMap.get(name)) {
      console.log('testTag', `Preferences[${name}]未初始化`)
      return
    }
    try {
      let pref = this.prefMap.get(name)
      // 读数据
      let value = await pref.get(key, defaultValue)
      // 刷盘
      await pref.flush()
      console.log('testTag', `Preferences[${name}]读取成功`)
      return value;
    }catch (e) {
      console.log('testTag', `Preferences[${name}]读取失败`)

    }
  }
}
const preferencesUtil = new PreferencesUtil()

export default preferencesUtil as PreferencesUtil
  1. 在入口文件 EntryAbility.ts 中,在 onCreate 钩子中,编写代码

async onCreate(want, launchParam) {
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  // 加载Preferences
  await PreferencesUtil.loadPreference(this.context, 'MyPreferences')
}
  1. 在需要存入的地方编写:

preferencesUtil.putPreferenceValue('MyPreferences', 'sliderFontSize', value)
  1. 在需要取出的地方:

await preferencesUtil.getPreferenceValue('MyPreferences', 'sliderFontSize', 40) as number

关系型数据库

初始化数据库:

增、删、改数据:

查询数据:

使用代码:

  1. 编写初始化、增删改查等操作

import relationalStore from '@ohos.data.relationalStore';
import TaskInfo from '../viewmodel/TaskInfo';
class TaskModel {
  private rdbStore: relationalStore.RdbStore
  private tableName: string = 'TASK'
  /**
  * 初始化任务表
  */
  initTaskDB(context) {
    // 1. rdb 配置
    const config = {
      name: 'MyApplication.db',
      securityLevel: relationalStore.SecurityLevel.S1
    }
    // 2. 初始化SQL语句
    const sql = `CREATE TABLE IF NOT EXISTS ${this.tableName}(
                  ID INTEGER PRIMARY KEY AUTOINCREMENT,
                  NAME TEXT NOT NULL,
                  FINISHED bit
                 )`
    // 3. 获取rdb
    relationalStore.getRdbStore(context, config, (err, rdbStore) => {
      if(err) {
        console.log('testTag', '获取rdbStore失败')
        return
      }
      // 执行sql
      rdbStore.executeSql(sql)
      console.log('testTag', '创建task表成功')
      this.rdbStore = rdbStore
    })

  }

  /**
   * 查询任务列表
   */
  async getTaskList() {
    // 1. 构建查询条件
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    // 2. 查询
    let result = await this.rdbStore.query(predicates, ['ID', 'NAME', 'FINISHED'])
    // 3. 解析查询结果
    // 3.1 定义一个数组,组装最终结果
    let tasks: TaskInfo[] = []
    // 3.2 封装
    while(!result.isAtLastRow) {
      // 3.3 指针移动到下一行
      result.goToNextRow()
      // 3.4 获取数据
      let id = result.getLong(result.getColumnIndex('ID'))
      let name = result.getString(result.getColumnIndex('NAME'))
      let finished = result.getLong(result.getColumnIndex('FINISHED'))
      // 3.5 封装到数组
      tasks.push({id, name, finishFlag: !!finished})
    }
    console.log('testTag', '查询到数据', JSON.stringify(tasks))
    return tasks
  }

  /**
   * 添加一个新任务
   */
  addTask(name: string): Promise<number> {
    return this.rdbStore.insert(this.tableName, {name, finishFlag: false})
  }

  /**
   * 更新任务
   */
  updateTaskStatus(id: number, finished: boolean) {
    // 1. 要更新的数据
    let data = {finishFlag: finished}
    // 2. 更新的条件
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('ID', id)
    // 3. 更新操作
    return this.rdbStore.update(data, predicates)

  }

  /**
   * 删除一个任务
   */
  deleteTaskById(id: number) {
    // 1. 删除的条件
    let predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo("ID", id)
    // 2. 删除操作
    return this.rdbStore.delete(predicates)
  }
}

let taskModel = new TaskModel();
export default TaskModel
  1. 在 entryAbility 中进行初始化操作

async onCreate(want, launchParam) {
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  // 加载Preferences
  await PreferencesUtil.loadPreference(this.context, 'MyPreferences')
  taskModel.initTaskDB(this.context)
}

通知

消息通知

应用可以通过通知接口发送通知消息,提醒用户关注应用中的变化,用户可以在通知栏查看和操作通知内容。

用法:

通知四种类型:

  1. 普通文本通知:

  1. 长文本通知:

  1. 多行文本通知:

  1. 图片通知:

其他的一些属性:

进度条通知

进度条通知会展示一个动态的进度条,主要用于文件下载、长任务处理的进度显示

通知意图

我们可以给通知添加或其中的按钮设置行为意图(Want),从而实现拉起应用组件或发布公共事件等能力。

其实就是当用户点击通知时,拉起我们的应用

用法:

ArkTs中组件

Image

  • 通过网络请求获取数据

Image('https://www4.bing.com//th?id=OHR.RegistanUzbekistan_ZH-CN7850329702_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp')
  .width(250)
  .borderRadius(10)
  // ImageInterpolation.High 处理图片虚焦、让图片更加圆滑
  .interpolation(ImageInterpolation.High)
  • 通过$r获取本地 resources/base/media 下的图片内容

Image($r('app.media.mate60'))
    .width(250)
    .borderRadius(10)
    .interpolation(ImageInterpolation.High)
  • 通过$rawfile获取本地 resources/rawfile 下的图片内容

Image($rawfile('mate60.png'))
    .width(250)
    .borderRadius(10)
    .interpolation(ImageInterpolation.High)

List

作用:当内容信息超出高度时,自动添加滚动条,可进行滚动,相当于 React Native 中的 ScrollView

⚠注意:该组件只能拥有一个父组件

用法:

List({ space: 8 }) {
  ForEach(
    arr,
    (item, index) => {
    },
  )
}
// 相当于 Flex 属性 ,当其中传入 1 时,会自动平分余下空间
// 传入 2 时,将会占两份
.layoutWeight(1)
// 当触底的时候,将会调用此函数
.onReachEnd(() => {
  console.log("触底了 ")
})

Blank

作用:当我们想要实现左右布局时,需要在左元素和右元素之间添加空白,直接使用此元素即可

添加前:

添加后:

用法:

Row() {
      Image($r('app.media.back'))
        .width(30)
      Text("商品列表")
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
      Blank()
      Image($r('app.media.refresh'))
        .width(30)
    }

Stack

作用:堆叠容器,子组件按照顺序依次入栈,后一个子组件覆盖前一个子组件。

使用前:

使用后:

用法:

Stack() {
  Progress({
    value: this.finishTask,
    total: this.totalTask,
    type: ProgressType.Ring
  })
    .width(100)
  Text(`${this.finishTask} / ${this.totalTask}`)
    .fontSize(30)
}

弹窗组件

新建一个弹窗组件,页面中组件写在 ets/view文件夹下,这里写一个

// 对于没有入口的组件类型的,添加@preview即可预览
import { CommonConstants } from '../../common/constants/CommonConstants'
@CustomDialog
export default struct UserPrivacyDialog {
  controller: CustomDialogController
  // 点击确定时调用
  confirm: () => void
  // 点击取消时调用
  cancel: () => void
  build() {
    Column({space: CommonConstants.SPACE_10}) {
      // 1. 标题
      Text($r('app.string.user_privacy_title'))
        .fontSize(20)
        .fontWeight(CommonConstants.FONT_WEIGHT_700)
      // 2. 内容
      Text($r('app.string.user_privacy_content'))
        .fontSize(16)
      // 3. 按钮
      Button($r('app.string.agree_label'))
        .width(150)
        .backgroundColor($r('app.color.primary_color'))
        .onClick(() => {
          this.confirm()
          this.controller.close()
        })
      Button($r('app.string.refuse_label'))
        .width(150)
        .backgroundColor($r('app.color.lightest_primary_color'))
        .fontColor($r('app.color.light_gray'))
        .onClick(() => {
          this.cancel()
          this.controller.close()
        })
    }
    .width('100%')
    .padding(10)
  }
}
  • 添加 @Preivew装饰器即可进行预览

在需要使用的页面组件中调用

import UserPrivacyDialog from '../view/welcome/UserPrivacyDialog'

@Entry
@Component
struct WelcomePage {
   controller: CustomDialogController = new CustomDialogController({
    builder: UserPrivacyDialog({
      confirm: () => this.onConfirm(),
      cancel: () => this.exitApp()
    })
  })

  aboutToAppear() {
    this.controller.open()
    // 也可以进行关闭
    this.controller.close()
  }
}

Tabbar

@Entry
@Component
struct Index {
  @State message: string = 'Hello World'

  build() {
    Tabs({barPosition: BarPosition.End}) {
      TabContent() {
        Text('内容1')
      }
      .tabBar('标题1')
      TabContent() {
        Text('内容2')
      }
      .tabBar('标题2')
    }
  }
}

ArkTs中的杂项

退出App

import common from '@ohos.app.ability.common'
exitApp() {
  // 退出App
  this.context.terminateSelf()
}