0
点赞
收藏
分享

微信扫一扫

在HarmonyOS中, API引入从@kit与@ohos有哪些不同 ?

唯米天空 2024-11-04 阅读 11

HarmonyOS基础

页面跳转

import { router } from '@kit.ArkUI'

Button('去首页').onClick(()=>{
		router.pushUrl({        // 普通跳转,可以返回
        url:'pages/Index'
    })
})
Button('登录').onClick(()=>{
		router.replaceUrl({     // 替换跳转,无法返回,不追加页面栈
    		url:'pages/Index'
    })
})

返回
router.back()

页面栈

存储运行时的页面,先进后出

页面栈的最大容量为32个页面

在这里插入图片描述

// 获取页面栈的长度
router.getLength()
// 清空页面栈
router.clear()

路由模式

Standard:无论之前是否添加过,一直添加到页面栈(默认使用)

Single:如果目标页面已存在,会将已有的最近同url页面移到栈顶(看情况使用)

 router.pushUrl({
            url:'pages/Index'
          },router.RouterMode.Single)

路由传参

import { router } from '@kit.ArkUI'

@Entry
@Component
struct Parent {
  @State
  username:string=''
  build() {
    Column() {
      Text('登录页面')
        .fontSize(40)
      TextInput({placeholder:'请输入用户名~',text:$$this.username})
      Button('登录')
        .onClick(()=>{
          router.pushUrl({
            url:'pages/Index',
            params:{
              username:this.username,
              msg:'测试消息'
            }
          })
        })
    }
  }
}
import { router } from '@kit.ArkUI'
interface ParamsObj{
  username:string
  msg:string
}
@Entry
@Component
struct Index {
  @State
  myName: string = ''
  // 一进入页面,就会执行的函数  =>  生命周期函数
  aboutToAppear(): void {
    console.log('传递过来的数据', JSON.stringify(router.getParams()))
    const params = router.getParams() as ParamsObj
    this.myName = params.username
  }
  build() {
    Column() {
      Text('首页')
        .fontSize(40)
      Text('姓名' + this.myName)
    }
    .height('100%')
    .width('100%')
  }
}
传递过来的数据 {"username":"cnyunos","msg":"测试消息"}

时间

安装

ohpm install dayjs

使用


生命周期

组件和页面在创建、显示、销毁的这一整个过程中,会自动执行的一系列函数(生命周期钩子)

让开发者有机会在特定的阶段运行自己的代码

带@Entry的就是页面,不带的就是组件

页面和组件都有,进入的时候,先执行页面的,再执行组件的。退出就会先执行组件的,再执行页面的

aboutToAppear(): void {}    //创建组件实例后执行,可以修改状态变量
aboutToDisappear(): void {} // 组件实例xiao前执行,不允许修改状态变量

仅@Entry修饰的页面组件生效

onPageShow(): void {}  // 页面每次显示触发(路由过程、应用进入前后台)
onPageHide(): void {}  // 页面每次隐藏触发(路由过程、应用进入前后台)
onBackPress(): boolean|void {} //点击返回触发(return true 阻止返回键默认返回效果)
onBackPress(): boolean|void {
		return true  // 会导致,点了返回也不返回
} 

Stage模型

官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/application-component-configuration-stage-V5

应用包名配置

应用需要在工程的AppScope目录下的app.json5配置文件中配置bundleName标签,该标签用于标识应用的唯一性。推荐采用反域名形式命名(如com.example.demo,建议第一级为域名后缀com,第二级为厂商/个人名,第三级为应用名,也可以多级)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如果需要在桌面显示UIAbility图标,除了需要配置icon与label字段,还需要在skills标签下面的entities中添加"entity.system.home"、actions中添加"ohos.want.action.home"。

"icon": "$media:my_app",
"label": "$string:EntryAbility_label",

icon 可以直接修改,图片放在 entity > src > main > resources > base > media

label 需要修改 entity > src > main > resources > base > element > string.json

在这里插入图片描述

在这里插入图片描述

如果没有 Open editor 就鼠标右键当前文件 列选择模式

在这里插入图片描述

Ability之间的跳转

let want:Want = {
      deviceId:'',      //  跳转到哪部手机,为空表示当前手机
      bundleName:'com.example.myapplication',   // 当前程序包名
      abilityName:'EntryAbility1'  // 目标ability
    };(getContext() as common.UIAbilityContext).startAbility(want)
'bundleName': 在AppScope > app.json5里面
'abilityName': 在src > main > module.json5里面

Stage模型 - UIAbility 组件

在这里插入图片描述

// 如果有多个Ability,谁的后面加了这个,谁就默认展示,然后再配置下面的来觉得展示哪个页面
"exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
export default class EntryAbility extends UIAbility {
     ......
     onWindowStageCreate(windowStage: window.WindowStage): void {
          ......
          //                           默认加载页面
          windowStage.loadContent('pages/Index', (err) => {......})
     }
}

新建 Ability

在这里插入图片描述

新建模块

在这里插入图片描述

Stage模型 - UIAbility 组件的生命周期

当用户打开、切换和返回到对应应用时,应用中的UIAbility实例会在其生命周期的不同状态之间转换。

onCreate:Ability创建时回调,执行初始化业务逻辑操作。
onDestory: Ability销毁时回调,执行资源清理等操作。
onForeground:当应用从后台转到前台时触发。
onBackground:当应用从前台转到后台时触发

LocalStorage - UIAbility内状态

官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-localstorage-V5

一个UIAbility有多个页面需要共享数据,就用LocalStorage

两种数据定义方式:

  • 自己定义一个ets文件,定义好数据之后导出

  • 将数据定义到 EntryAbility 文件,再共享到各个页面

const data: Record<string, ResourceColor> = {
  'name': '陈丰芸',
  'picture': $r('app.media.s9'),
  'colour': Color.Yellow
}
export  const localInfo = new LocalStorage(data)
import { localInfo } from './LocalStorageDataClass';
import { router } from '@kit.ArkUI';
@Entry(localInfo)
@Component
struct Index {
  @LocalStorageLink('name')
  name:ResourceColor = ''
  @LocalStorageLink('picture')
  picture:ResourceColor = ''
  @LocalStorageLink('colour')
  colour:ResourceColor = ''
  build() {
    Column(){
      Text('默认首页')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Image(this.picture as Resource)
        .width('100%')
      Row(){
        Text('姓名:')
        TextInput({text:$$this.name})
      }
      .backgroundColor(this.colour)
      Button('去自定义页面')
        .onClick(()=>{
          router.pushUrl({
            url:'pages/DemoPage'
          })
        })
    }
    .height('100%')
    .width('100%')
  }
}
import { localInfo } from './LocalStorageDataClass';
import { router } from '@kit.ArkUI';
@Entry(localInfo)
@Component
struct DemoPage {
  @LocalStorageLink('name')
  name:ResourceColor = ''
  @LocalStorageLink('picture')
  picture:ResourceColor = ''
  @LocalStorageLink('colour')
  colour:ResourceColor = ''
  build() {
    Column(){
      Text('自定义首页')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Image(this.picture as Resource)
        .width('100%')
      Row(){
        Text('姓名:')
        TextInput({text:$$this.name})
      }
      .backgroundColor(this.colour)
      Button('回到首页')
        .onClick(()=>router.back())
    }
    .height('100%')
    .width('100%')
  }
}

第二种

  onWindowStageCreate(windowStage: window.WindowStage): void {
    const data: Record<string, ResourceColor> = {
      'name': '陈丰芸',
      'picture': $r('app.media.s9'),
      'colour': Color.Yellow
    }
    const localInfo = new LocalStorage(data)
    windowStage.loadContent('pages/Index',localInfo);
  }
import { router } from '@kit.ArkUI';
let  localInfo = LocalStorage.GetShared()
@Entry(localInfo)
@Component
struct Index {
  @LocalStorageLink('name')
  name:ResourceColor = ''
  @LocalStorageLink('picture')
  picture:ResourceColor = ''
  @LocalStorageLink('colour')
  colour:ResourceColor = ''
  build() {
    Column(){
      Text('默认首页')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Image(this.picture as Resource)
        .width('100%')
      Row(){
        Text('姓名:')
        TextInput({text:$$this.name})
      }
      .backgroundColor(this.colour)
      Button('去自定义页面')
        .onClick(()=>{
          router.pushUrl({
            url:'pages/DemoPage'
          })
        })
    }
    .height('100%')
    .width('100%')
  }
}
import { router } from '@kit.ArkUI';
@Entry(LocalStorage.GetShared())
@Component
struct DemoPage {
  @LocalStorageLink('name')
  name:ResourceColor = ''
  @LocalStorageLink('picture')
  picture:ResourceColor = ''
  @LocalStorageLink('colour')
  colour:ResourceColor = ''
  build() {
    Column(){
      Text('自定义首页')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Image(this.picture as Resource)
        .width('100%')
      Row(){
        Text('姓名:')
        TextInput({text:$$this.name})
      }
      .backgroundColor(this.colour)
      Button('回到首页')
        .onClick(()=>router.back())
    }
    .height('100%')
    .width('100%')
  }
}

AppStorage - 应用状态

import { router } from '@kit.ArkUI'
import { common, Want } from '@kit.AbilityKit'
@Entry
@Component
struct DemoPage {
  @State
  username: string = ""
  @State
  password: string = ""
  login(){
    const userInfo:Record<string,string>={
      'name':'陈丰芸',
      'age':'18'
    }
    AppStorage.setOrCreate('userInfo',userInfo)
    // router.pushUrl({
    //   url:'pages/Index'
    // })
    let want:Want = {
      deviceId:'',
      bundleName:'com.example.myapplication',
      abilityName:'EntryAbility1'
    };(getContext() as common.UIAbilityContext).startAbility(want)
  }
  build() {
    Column({space:20}){
      TextInput({placeholder:'请输入用户名',text:$$this.username})
      TextInput({placeholder:'请输入密码',text:$$this.password}).type(InputType.Password)
      Button('登录').width('100%')
        .onClick(()=>{
          this.login()
        })
    }
    .height('100%')
    .width('100%')
  }
}
import { router } from '@kit.ArkUI'
@Entry
@Component
struct Index {
  // 用法一
  @StorageLink('userInfo')
  userInfo:Record<string,string>={}
  // 用法二
  // @State
  // userInfo:Record<string,string>={}
  // aboutToAppear(): void {
  //   const userInfo = AppStorage.get<Record<string,string>>('userInfo')
  //   this.userInfo = userInfo!
  // }
  build() {
    Column({space:20}){
      Row(){
        Text('姓名:')
        TextInput({text:this.userInfo.name})
      }
      Row(){
        Text('年龄')
        TextInput({text:this.userInfo.age})
      }
      Button('退出').onClick(()=>{
        // AppStorage.set('userInfo',null)
        router.back()
      })
    }
    .height('100%')
    .width('100%')
  }
}

AppStorage.setOrCreate(“”, T) // 创建或者设置某个字段的属性
AppStorage.get(“”) // 获取的全局状态类型
如果遇到获取数据的类型为空,可以用if判断,也可以用非空断言来解决
StorageLink . - 直接修改-自动同步到全局状态
StorageProp- 可以改,只会在当前组件生效,只是改的全局状态的副本,不会对全局状态产生影响

PersistentStorage - 持久化存储UI状态

PersistentStorage.PersistProp(‘属性名’, 值)

在这里插入图片描述

PersistentStorage.persistProp('message','666666')
@Entry
@Component
struct Index {
  @StorageLink('message')
  message: string = '默认首页';

  build() {
    Column(){
      Text(this.message)
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Button('按钮')
        .onClick(()=>{
          AppStorage.setOrCreate('message','123456')
        })
    }
    .height('100%')
    .width('100%')
  }
}

程序执行先取PersistentStorage的值,如果没有再取AppStorage的值,最后才是默认值

在取PersistentStorage值过程中,先去磁盘上读,没有读到,就展示默认值

第一次,磁盘里面肯定是没有的,于是就展示默认值,点击按钮之后,会修改message的值,写入磁盘,下次再打开应用,磁盘有内容,于是就展示123456

案例(后面追加)

在 EntryAbility 里面加,页面一启动的时候就会执行
windowStage.loadContent('/pages/Index'.slice(1), (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
//   持久化
PersistentStorage.persistProp('isHidePrivacy', false)
})
------------------------------------------------------------------------
页面中使用
@StorageLink('isHidePrivacy') isHidePrivacy: boolean = false
----
@StorageLink 可以双向绑定,需要给默认值

preferences - 状态持久化

import preferences from '@ohos.data.preferences'
import { Context } from '@kit.AbilityKit'


export class PreferencesClass {
  // 默认存储仓库
  static defaultStore: string = 'DEFAULT_STORE'
  //   字段名
  static tokenkey: string = 'TOKEN_KEY'

  //   给字段添加set方法            仓库名字由外面传递,没有传就用默认值
  static setToken(content: Context, token: string, storeName: string = PreferencesClass.defaultStore) {
    //   先拿到仓库
    const store = preferences.getPreferencesSync(content, { name: storeName })
    // 再拿到key
    store.putSync(PreferencesClass.tokenkey, token)
    // 写入磁盘
    store.flush()
  }

  //   给字段添加get方法
  static getToken(content: Context, storeName: string = PreferencesClass.defaultStore) {
    // 先拿到仓库名称
    const store = preferences.getPreferencesSync(content, { name: storeName })
    // 通过key查找vel,有可能查不到,需要给一个默认值
    return store.getSync(PreferencesClass.tokenkey, '404')
  }

  //   删除token 不建议的做法,这样会把Key也删除,建议使用set方法覆盖一个空值
  static removeToken(content: Context, storeName: string = PreferencesClass.defaultStore) {
    // 先拿到仓库名称
    const store = preferences.getPreferencesSync(content, { name: storeName })
    // 删除
    store.deleteSync(PreferencesClass.tokenkey)
    // 写入磁盘
    store.flush()
  }
}
  onWindowStageCreate(windowStage: window.WindowStage): void {
    const token = PreferencesClass.getToken(this.context)
    console.log('Ability页面:',token)
    if (token === '404') {
      windowStage.loadContent('pages/logon');
    } else {
      windowStage.loadContent('pages/Index');
    }
  }
import { PreferencesClass } from './PreferencesClass'
import { router } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  build() {
    Column({ space: 20 }) {
      Text('首页-登录成功')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Button('退出')
        .onClick(() => {
          PreferencesClass.setToken(getContext(), '404')

          router.back()
        })
    }
    .height('100%')
    .width('100%')
  }
}
import { PreferencesClass } from './PreferencesClass'
import { router } from '@kit.ArkUI'

@Entry
@Component
struct Logon {
  @State
  password: string = '123456'

  build() {
    Column({ space: 20 }) {
      Text('登录页面')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      TextInput({ placeholder: '请输入用户名' })
      TextInput({ placeholder: '请输入密码', text: $$this.password })
      Button('登录').width('100%')
        .onClick(() => {
          PreferencesClass.setToken(getContext(), this.password)
          router.pushUrl({
            url: 'pages/Index'
          })
        })
    }
    .height('100%')
    .width('100%')
  }
}
   Button('查看持久化')
        .onClick(()=>{
          const store = preferences.getPreferencesSync(getContext(), { name:'yunOS'})
          // 通过key查找vel,有可能查不到,需要给一个默认值
          const  dddd = store.getSync('name', '没有内容')
          AlertDialog.show({message:JSON.stringify(dddd)})
      })
      Button('写数据进去')
        .onClick(()=>{
            const store = preferences.getPreferencesSync(getContext(), { name:'yunOS'})
            // 再拿到key
            store.putSync('name', '陈丰芸')
            // 写入磁盘
            store.flush()
        })

权限

网络权限

官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/permissions-for-all-V5

这种是系统权限,直接写就行,这个时候就可以加载网络图片了

在这里插入图片描述

用户权限,需要通过用户的授权的权限

// module.json5 文件    
    "requestPermissions": [
//      允许联网
      {
        "name": "ohos.permission.INTERNET"
      },
//      获取地理位置
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:permission_location",
        "usedScene": {
          "abilities": ["EntryAbility"], // 在哪个应用开启
          "when": "always"               // 什么时候开启
        }
      }
    ],
import { geoLocationManager } from '@kit.LocationKit'
@Entry
@Component
struct Index {
  @State
  result: geoLocationManager.Location = {} as geoLocationManager.Location
  build() {
    Column({ space: 20 }) {
      Text('首页')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Image('https://res.vmallres.com/uomcdn/CN/cms/202409/5a82eb94567d4bc2821fcc9f2991c0ec.jpg.webp')
        .width('100%')
      Text('获取经纬度')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
      Button('获取').width('100%')
        .onClick(async () => {
          this.result = await geoLocationManager.getCurrentLocation()
        })
      Text('经度:' + this.result.latitude)
      Text('纬度:' + this.result.longitude)
    }
    .height('100%')
    .width('100%')
  }
}
// EntryAbility.etx 文件内容  
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam):void{
    // 创建程序管理控制器
    const manager = abilityAccessCtrl.createAtManager()
    manager.requestPermissionsFromUser(this.context,
      [
        "ohos.permission.APPROXIMATELY_LOCATION"
      ]
    )
  }

在这里插入图片描述

除了要授权,模拟器的位置信息也需要打开

Http请求

同步异步相关
await是等待的意思,使用这个方法需要在函数前面加async

耗时任务加await,函数前面加async

oh-package.json5 这个文件,分为全局的和模块的,全局的就整个模块都能用,模块的就当前模块可以用
“dependencies”:{ 这里面是下载的依赖包 }

OpenHarmony三方库中心仓:https://ohpm.openharmony.cn/#/cn/home

ohpm install @ohos/axios
import { http } from '@kit.NetworkKit'
import axios, { AxiosResponse } from '@ohos/axios'
@Entry
@Component
struct Index {
  async sendHttp(){
    const req = http.createHttp()
    // 请求结果是字符串
    const res = await req.request('https://zhousg.atomgit.net/harmonyos-next/takeaway.json')
    AlertDialog.show({
      message: res.result as string
    })
  }
  async  sendAxios(){
    const res = await axios.get<object,AxiosResponse<object,null>,null>('https://zhousg.atomgit.net/harmonyos-next/takeaway.json')
   return res
  }
  build() {
    Column({space:20}) {
      Button('处理Http请求')
        .onClick(()=>{
          this.sendHttp()
        })
      Button('处理Axios请求')
        .onClick(async ()=>{ // 上面等待,底下重新赋值,也需要等待
          const res = await this.sendAxios()
          AlertDialog.show({
            message:JSON.stringify(res.data)
          })
        })
    }
    .height('100%')
    .width('100%')
  }
}

压缩图片

  //   照片瘦身
  async  compressImage(url:string){
  //   创建图片打包器
    const imagePacker  = image.createImagePacker()
    // fd 打开文件后的唯一标识
    const file =  fileIo.openSync(url)
    const imageSource = image.createImageSource(file.fd)
    const arrayBuffer = await imagePacker.packing(imageSource,{format:'image/jpeg',quality:10})
    
    AlertDialog.show({message:'压缩后图片大小'+formatByteLength(arrayBuffer.byteLength)})
  }
{format:'image/jpeg',quality:10}
image/jpeg   // 格式的写法
quality      // 0 ~ 100  值越大越,照片质量越高

packing方法是对图片进行重新编码,得到的结果是一个文件流‘

创建图片:createAsset( )
参数1,传入什么类型的数据(比如图片:photoAccessHelper.PhotoType.IMAGE)
参数2,后缀名(jpg)
createAsset(photoAccessHelper.PhotoType.IMAGE,'jpg')

图案锁

// 图案密码锁
// xxx.ets
import { LengthUnit, promptAction } from '@kit.ArkUI'

@Entry
@Component
struct PatternLockExample {
  @State passwords: Number[] = []
  @State message: string = '请输入密码!'
  private patternLockController: PatternLockController = new PatternLockController()

  build() {
    Column() {
      Text(this.message).textAlign(TextAlign.Center).margin(20).fontSize(20)
      PatternLock(this.patternLockController)
        .sideLength(200)           // 调整图案锁大小
        .circleRadius(9)           // 圆点大小
        .pathStrokeWidth(18)       // 连线,线的宽度
        .activeColor('#B0C4DE')    // 点击时圆点的颜色
        .selectedColor('#ff4400')  // 选中后圆点的颜色
        .pathColor('#90EE90')      // 连线,线条的颜色
        .backgroundColor('#F5F5F5')// 背景灰色
        .autoReset(true)           // 输入完成是否复原
        .activateCircleStyle({
          color: Color.Pink,
          radius: { value: 16, unit: LengthUnit.VP },
          enableWaveEffect: true
        }) // 圆点外面发光圈的颜色
        .onDotConnect((index: number) => {
          promptAction.showToast({message:'点击了:'+index})
        })
        .onPatternComplete((input: Array<number>) => {
          promptAction.showToast({message:'输入完成:'+input})
          // 输入的密码长度小于5时,提示重新输入
          if (input.length < 5) {
            this.message = '密码长度需要大于5,请重新输入。'
            return
          }
          // 判断密码长度是否大于0
          if (this.passwords.length > 0) {
            // 判断两次输入的密码是否相同,相同则提示密码设置成功,否则提示重新输入
            if (this.passwords.toString() === input.toString()) {
              this.passwords = input
              this.message = '设置密码成功: ' + this.passwords.toString()
              this.patternLockController.setChallengeResult(PatternLockChallengeResult.CORRECT)
            } else {
              this.message = '密码不一致,请重新输入.'
              this.patternLockController.setChallengeResult(PatternLockChallengeResult.WRONG)
            }
          } else {
            // 提示第二次输入密码
            this.passwords = input
            this.message = "请重新输入."
          }
        })
      Button('重置').margin(30).onClick(() => {
        // 重置密码锁
        this.patternLockController.reset()
        this.passwords = []
        this.message = '请输入密码'
      })
    }.width('100%').height('100%')
  }
}

在这里插入图片描述

生物识别和密码

// 允许应用使用生物特征识别能力进行身份认证
import { userAuth } from '@kit.UserAuthenticationKit'
import { promptAction } from '@kit.ArkUI'

@Entry
@Component
struct UserAuthTestPage {
  build() {
    Column() {
      Button('查询支持的认证能力')
        .onClick(() => {
          try {
            userAuth.getAvailableStatus(userAuth.UserAuthType.PIN, userAuth.AuthTrustLevel.ATL1)
            promptAction.showToast({ message: '有能力' })
          } catch (e) {
            AlertDialog.show({ message: JSON.stringify(e, null, 2) })
          }
        })

      Button('发起认证')
        .onClick(() => {
          // 获取认证对象
          const UserAuthInstance = userAuth.getUserAuthInstance(
            {
              challenge: new Uint8Array([1, 2, 33, 3]),
              authType: [userAuth.UserAuthType.PIN, userAuth.UserAuthType.FINGERPRINT, userAuth.UserAuthType.FACE],
              authTrustLevel: userAuth.AuthTrustLevel.ATL3
            },
            { title: '请验证用户身份' }
          )
          // 订阅认证结果
          UserAuthInstance.on('result', {
            onResult(result) {
              AlertDialog.show({ message: JSON.stringify(result, null, 2) })
            }
          })
          // 发起认证
          UserAuthInstance.start()
        })

      Button('查询支持的认证能力')
        .onClick(() => {
          //   鸿蒙中支持的认证类型
          const userAuthTypeList: userAuth.UserAuthType[] = [
            userAuth.UserAuthType.PIN,
            userAuth.UserAuthType.FINGERPRINT,
            userAuth.UserAuthType.FACE
          ]
          const res = userAuthTypeList.map((item) => {
            try {
              userAuth.getAvailableStatus(item, userAuth.AuthTrustLevel.ATL3)
              return true
            } catch {
              return false
            }
          })
          const isSupport = res.some(v => v === true)
          AlertDialog.show({ message: JSON.stringify(isSupport, null, 2) })
        })
    }
    .padding(10)
    .height('100%')
    .width('100%')
  }
}
new Promise((resolve,reject)=>{

})
resolve  执行成功
reject   执行失败

持久化

https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-data-relationalstore-V5

在这里插入图片描述

// 数据库
import { relationalStore } from '@kit.ArkData'
import { promptAction } from '@kit.ArkUI'
interface PrivacyNote{
  id:number,
  title:string,
  content:string,
  date_added:number,
}
@Entry
@Component
struct Database {
  sqlCreate: string = `CREATE TABLE IF NOT EXISTS privacy_note (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        content TEXT NOT NULL,
        date_added INTEGER NOT NULL
      )`
  store: relationalStore.RdbStore | null = null

  build() {
    Column({ space: 20 }) {
      Button('创建数据库')
        .onClick(async () => {
          this.store = await relationalStore.getRdbStore(getContext(), {
            name: 'cnyunos.db',
            // 数据库等级,等级越低,可以共享的数据就越多
            securityLevel: relationalStore.SecurityLevel.S1
          })
          promptAction.showToast({ message: '数据库创建成功' })
        })
      Button('创建数据表')
        .onClick(async () => {
          await this.store?.executeSql(this.sqlCreate)
          promptAction.showToast({ message: '数据表创建成功' })
        })
      Button('查询表信息')
        .onClick(async () => {
          // 查表,谓词传入表名
          const predicates = new relationalStore.RdbPredicates('privacy_note')
          const resultSet = await this.store?.query(predicates)
          promptAction.showToast({ message: '字段名称' + resultSet?.columnNames })
        })
      Button('删除数据库')// .enabled(false)
        .onClick(() => {
          relationalStore.deleteRdbStore(getContext(), 'cnyunos.db')
          promptAction.showToast({ message: '数据库已经删除' })
        })
    }
    .height('100%')
    .width('100%')
    .padding(10)
  }
}
this.store?.query(predicates)
用打开的数据库(数据库操作对象)文件,根据谓词查询

向表中插入一条数据

Date.now()获取当前时间戳
// 数据库
import { relationalStore } from '@kit.ArkData'
import { promptAction } from '@kit.ArkUI'

@Entry
@Component
struct Database {
  store: relationalStore.RdbStore | null = null
  @State
  isStore: boolean = false

  build() {
    Column({ space: 20 }) {
      Button('创建数据库')
        .onClick(async () => {
          this.store = await relationalStore.getRdbStore(getContext(), {
            name: 'cnyunos.db',
            // 数据库等级,等级越低,可以共享的数据就越多
            securityLevel: relationalStore.SecurityLevel.S1
          })
          this.isStore = true
          promptAction.showToast({ message: '数据库创建(打开)成功' })
        })
      Button('插入一条数据')
        .enabled(this.isStore)
        .onClick(async () => {
          // 查表,谓词传入表名
          const id = await this.store?.insert('privacy_note', {
            id: null,
            title: '测试标题',
            content: '测试内容',
            date_added: Date.now()
          })
          promptAction.showToast({ message: '新增数据成功,id:' + id })
        })
    }
  }
}

查询数据

 Button('查询数据')
        .enabled(this.isStore)
        .onClick(async () => {
          // 查表,谓词传入表名
          const predicates = new relationalStore.RdbPredicates('privacy_note')
          const resultSet = await this.store?.query(predicates)
          promptAction.showToast({ message: '数据总条数:' + resultSet?.rowCount })
        })

移动指针

根据类型获取列

 Button('查询数据')
        .enabled(this.isStore)
        .onClick(async () => {
          // 查表,谓词传入表名
          const predicates = new relationalStore.RdbPredicates('privacy_note')
          const resultSet = await this.store?.query(predicates)
          const list:PrivacyNote[] = []
          // goToNextRow 移动指针到下一行,存在下一行就返回true
          while(resultSet?.goToNextRow()){
              // 按列提取数据
              const item:PrivacyNote ={
                  id:resultSet.getLong(0),
                  title:resultSet.getString(1),
                  content:resultSet.getString(2),
                  date_added:resultSet.getLong(3),
              }
              // 追加到数组中
              list.push(item)
          }
     	  resultSet.close() // 释放资源
     	  promptAction.showToast({ message:JSON.stringify(list,null,2)})
        })
API11直接使用
直接可以拿到当前一行的数据
gitRow()代替下面的
id:resultSet.getLong(0),
title:resultSet.getString(1),
content:resultSet.getString(2),
date_added:resultSet.getLong(3),

在谓词里面有排序的方式

onderByAsc('字段名')	// 正序(从小到大)
onderByDesc('字段名')	// 到序(从大到小)
.in('id',[1,2,3])     // 指定哪几条
.offsetAs(1)          // 偏移
.and()                // &&
.limitAs(3)           // 提取数值
// 查表,谓词传入表名
const predicates = new relationalStore.RdbPredicates('privacy_note')
predicates.orderByDesc('id')    // 排序倒序
predicates.offsetAs(1)          // 偏移
          .and()                // &&
          .limitAs(3)           // 提取数值

删除

  Button('删除')
        .enabled(this.isStore)
        .onClick(async ()=>{
        const predicates = new relationalStore.RdbPredicates('privacy_note')
        // 不传条件,和删库没什么区别
        predicates.in('id',[2,3,4])// 一次删除多条
          // predicates.equalTo('id',1) // 一次删除1条
        await this.store?.delete(predicates)
        promptAction.showToast({ message:'删除成功'})
      })

更新

   Button('修改数据')
        .enabled(this.isStore)
        .onClick(async ()=>{
          const predicates = new relationalStore.RdbPredicates('privacy_note')
          predicates.equalTo('id',1) // 不加条件,改的是全部
          await this.store?.update({
            title:'我是新标题',
            content:'今天是1011下午'
          } ,predicates)
          promptAction.showToast({ message:'修改数据成功'})
        })

封装

contructor(){
	this.getStoreInstance()
	.then(store=>{
	store.executeSql(this.sqlCreate)
	})
}
import { relationalStore, ValuesBucket } from "@kit.ArkData"

// 隐私笔记的类型
export interface PrivacyNoteDBInfo extends ValuesBucket {
  id: number | null // 新增时 id 设置为 null ,可实现 id 自增
  title: string
  content: string
  date_added: number
}
// 隐私笔记数据库封装
class PrivacyNoteDB {
  // 操作数据库的实例
  private store: relationalStore.RdbStore | null = null
  // 数据库表名
  private tableName = 'privacy_note'
  // 创建数据库的语句
  private sqlCreate = `CREATE TABLE IF NOT EXISTS ${this.tableName} (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        content TEXT NOT NULL,
        date_added INTEGER NOT NULL
      )`

  // 获取数据库操作的实例
  async getStoreInstance() {
    // 如果数据库实例已存在,直接返回,没有才创建实例
    if (this.store) { return this.store }
    // 获取操作数据库的实例
    const store = await relationalStore.getRdbStore(getContext(), {
      name: 'yunos_data.db', // 数据库名称
      securityLevel: relationalStore.SecurityLevel.S1 // 安全等级
    })
    // 存储起来方便下次直接获取
    this.store = store
    // 返回 store 实例
    return this.store
  }


  // 类的构造器,new 的时候会自动触发
  constructor() {
    // 创建/打开数据库文件
    this.getStoreInstance()
      .then(store => {
        // 执行创建语句,用于创建数据库的表
        store.executeSql(this.sqlCreate)
      })
  }

  async insert(value:PrivacyNoteDBInfo){
    // 创建或打开数据库
    const store= await this.getStoreInstance()
    // 新增
    return store.insert(this.tableName,value)
  }
}

// 通过小写 p 开头的类实例操作数据库,创建数据库,建表,增、删、查、改
export const privacyNoteDB = new PrivacyNoteDB()

调用

 Button('新增').onClick(async ()=>{
        const  id  = await privacyNoteDB.insert({
          id:null,
          title:'我爱',
          content:'中华人民共和国',
          date_added:Date.now()
        })
        promptAction.showToast({message:id.toString()})
      })
.enabled( )  // 其他地方也可以用
return Promise.reject()

华为分享(需要真机)

systemShare

utd:设置分享类型

隐私录音

AudioCapturer 更专业的音频录制开发

AVRecorder 支持跟多的编码格式

import { abilityAccessCtrl } from '@kit.AbilityKit'
import { audio } from '@kit.AudioKit';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct AudioCapturer {
  @State isGrant: string = ''
  @State isCreate: boolean = false

  audioCapturer?:audio.AudioCapturer

  aboutToAppear() {
    this.requestPermissionsFromUser()
  }

  // 请求用户授权
  async requestPermissionsFromUser() {
    //   访问控制管理:获取访问控制模块对象
    let atManager = abilityAccessCtrl.createAtManager()
    let permissionRequestResult =
      await atManager.requestPermissionsFromUser(getContext(), ['ohos.permission.MICROPHONE'])
    AlertDialog.show({ message: JSON.stringify(permissionRequestResult, null, 2) })
    this.isGrant = JSON.stringify(permissionRequestResult.dialogShownResults)
  }
  build() {
    Column() {
      Text('结果:' + this.isGrant)
      Button('开始录音')
        .onClick(async () => {
          // 音频流信息
          try {
            const audioStreamInfo: audio.AudioStreamInfo = {
              samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
              channels: audio.AudioChannel.CHANNEL_2, // 通道
              sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
              encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
            };
            //   音频采集器信息
            const audioCapturerInfo: audio.AudioCapturerInfo = {
              source: audio.SourceType.SOURCE_TYPE_MIC, // 声音来源
              capturerFlags: 0 // 0 代表普通音频采集器,1 代表低延时音频采集器
            };
            const audioCapturerOptions: audio.AudioCapturerOptions = {
              streamInfo: audioStreamInfo,// 音频流信息
              capturerInfo: audioCapturerInfo// 音频采集器信息
            };
            // AudioCapturer实例的创建
            const  audioCapturer = await audio.createAudioCapturer(audioCapturerOptions)
            this.audioCapturer  = audioCapturer
            this.isCreate = true
            // 订阅音频数据读入
            audioCapturer.on('readData',(buffer)=>{
              console.log('音频流大小',buffer.byteLength)
            })
            // 开始录制音频
            await audioCapturer.start()
            // 如果上面报错,就不会运行到这里
            promptAction.showToast({ message: '调用createAudioCapturer成功.' })

          } catch (error) {
            promptAction.showToast({ message: `异常:${error}` })
          }
        })
      Button('停止录音')
        .enabled(this.isCreate)
        .onClick(async () => {
          await this.audioCapturer?.stop()
          promptAction.showToast({ message: '停止成功.' })
        })
      Button('继续录音')
        .enabled(this.isCreate)
        .onClick(async () => {
          await this.audioCapturer?.start()
          promptAction.showToast({ message: '继续录音成功.' })
        })
      Button('释放资源')
        .enabled(this.isCreate)
        .onClick(async () => {
          await this.audioCapturer?.release()
          promptAction.showToast({ message: '释放资源成功.' })
        })
    }
    .height('100%')
    .width('100%')
  }
}

文件介绍

在这里插入图片描述

cache 缓存文件
files 持久化文件
temp  临时文件
在持久化创建文件
const context = getContext()
// 通过应用上下文,获取到应用的files路径
const filePath = context.filesDir + '/' + 'test.wav'  
fileIo.openSync(filePath,fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
context.filesDir  // 表示files目录
'/'               // 表示files目录的下一级
'test.wav'        // 文件名加后缀
fileIo.OpenMode.CREATE  // 文件不存在就创建
fileIo.OpenMode.READ_WRITE  // 该文件的权限是可读可写
| 位运算符

buffer 数据流
// 获取文件信息(大小,创建时间等)
const fileStat = fileIo.statSync(file.fd)

将音频写到文件

Button('开始录音')
	.enabled(!this.isCreate)
    .onClick(async () => {
    // 音频流信息
    	try {
            const audioStreamInfo: audio.AudioStreamInfo = {
              samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
              channels: audio.AudioChannel.CHANNEL_2, // 通道
              sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
              encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
            };
            //   音频采集器信息
            const audioCapturerInfo: audio.AudioCapturerInfo = {
              source: audio.SourceType.SOURCE_TYPE_MIC, // 声音来源
              capturerFlags: 0 // 0 代表普通音频采集器,1 代表低延时音频采集器
            };
            const audioCapturerOptions: audio.AudioCapturerOptions = {
              streamInfo: audioStreamInfo,// 音频流信息
              capturerInfo: audioCapturerInfo// 音频采集器信息
            };
            // AudioCapturer实例的创建
            const  audioCapturer = await audio.createAudioCapturer(audioCapturerOptions)
            this.audioCapturer  = audioCapturer
            this.isCreate = true
            // ----------------文件系统--------------------
            const context = getContext()
            // 通过应用上下文,获取到应用的files路径
            const filePath = context.filesDir + '/'+ Date.now() + '.wav'
            const file = fileIo.openSync(filePath,fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
            // 订阅音频数据读入
            audioCapturer.on('readData',(buffer)=>{
              fileIo.writeSync(file.fd,buffer)
              console.log('音频流大小',buffer.byteLength)
            })
            // 开始录制音频
            await audioCapturer.start()
            // 如果上面报错,就不会运行到这里
            promptAction.showToast({ message: '调用createAudioCapturer成功.' })
          } catch (error) {
            promptAction.showToast({ message: `异常:${error}` })
          }
        })

播放音频

@State filePath:string = ''
audioRenderer?: audio.AudioRenderer

this.filePath = context.filesDir + '/'+ Date.now() + '.wav'
Button('创建音频渲染器-播放音频')
	.enabled(this.filePath!=='')
        .onClick(async ()=>{
          try {
            const  audioStreamInfo: audio.AudioStreamInfo = {
              samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
              channels: audio.AudioChannel.CHANNEL_2, // 通道
              sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
              encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
            };

            const  audioRendererInfo: audio.AudioRendererInfo = {
              usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION,
              rendererFlags: 0
            };
            // 音频渲染器配置
            const  audioRendererOptions: audio.AudioRendererOptions = {
              streamInfo: audioStreamInfo,//音频流信息
              rendererInfo: audioRendererInfo// 音频渲染器信息
            };
            // 1. 创建音频渲染器
            const audioRenderer = await audio.createAudioRenderer(audioRendererOptions)
            // 保存起来
            this.audioRenderer = audioRenderer
            // 根据路径打开文件
            const file = fileIo.openSync(this.filePath)
            // 获取文件信息(大小,创建时间等)
            const  fileIoStat =fileIo.statSync(file.fd)
            // 准备一个累加值,用于自动停止渲染
            let bufferSize = 0
            // 2. 订阅(写入数据到音频渲染器中,就能发出声音)
            audioRenderer.on('writeData',(buffer)=>{
              // 读取打开的 buffer文件,写到渲染器中
              fileIo.readSync(file.fd,buffer)
              console.log('音频渲染器播放',buffer.byteLength)
              bufferSize += buffer.byteLength
              // 累加的结果是否超过文件大小
              if (bufferSize >= fileIoStat.size) {
                audioRenderer.stop()
              }
            })
            // 3. 开始渲染
            audioRenderer.start()
            promptAction.showToast({message:'音频渲染器正常'})
     } catch (error) {
            promptAction.showToast({message:'音频渲染器错误:'+JSON.stringify(error)})
     }
})
Button('停止渲染')
     .onClick(async ()=>{
          await this.audioRenderer?.stop()
          promptAction.showToast({message:'停止成功'})
     })
Button('销毁实例,释放资源')
     .onClick(async ()=>{
          await this.audioRenderer?.release()
          promptAction.showToast({message:'销毁实例,释放资源成功'})
})

录音+播放,综合

import { abilityAccessCtrl } from '@kit.AbilityKit'
import { audio } from '@kit.AudioKit';
import { promptAction } from '@kit.ArkUI';
import fileIo from '@ohos.file.fs';

@Entry
@Component
struct AudioCapturer {
  @State isGrant: string = ''
  @State isCreate: boolean = false
  @State filePath: string = ''
  audioCapturer?: audio.AudioCapturer
  audioRenderer?: audio.AudioRenderer

  aboutToAppear() {
    this.requestPermissionsFromUser()
  }

  // 请求用户授权
  async requestPermissionsFromUser() {
    //   访问控制管理:获取访问控制模块对象
    let atManager = abilityAccessCtrl.createAtManager()
    let permissionRequestResult =
      await atManager.requestPermissionsFromUser(getContext(), ['ohos.permission.MICROPHONE'])
    AlertDialog.show({ message: JSON.stringify(permissionRequestResult, null, 2) })
    this.isGrant = JSON.stringify(permissionRequestResult.dialogShownResults)
  }

  audioStreamInfo: audio.AudioStreamInfo = {
    samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
    channels: audio.AudioChannel.CHANNEL_2, // 通道
    sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
    encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
  };
  //   音频采集器信息
  audioCapturerInfo: audio.AudioCapturerInfo = {
    source: audio.SourceType.SOURCE_TYPE_MIC, // 声音来源
    capturerFlags: 0 // 0 代表普通音频采集器,1 代表低延时音频采集器
  };
  audioCapturerOptions: audio.AudioCapturerOptions = {
    streamInfo: this.audioStreamInfo, // 音频流信息
    capturerInfo: this.audioCapturerInfo// 音频采集器信息
  };
  audioRendererInfo: audio.AudioRendererInfo = {
    // usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION,//用听筒播放
    // usage: audio.StreamUsage.STREAM_USAGE_MOVIE,//用外放喇叭播放
    usage: audio.StreamUsage.STREAM_USAGE_MUSIC, // 音乐
    rendererFlags: 0
  };
  // 音频渲染器配置
  audioRendererOptions: audio.AudioRendererOptions = {
    streamInfo: this.audioStreamInfo, //音频流信息
    rendererInfo: this.audioRendererInfo// 音频渲染器信息
  };

  build() {
    Column() {
      Text('结果:' + this.isGrant)
      Text('音频文件的路径' + this.filePath)
      Button('开始录音')
        .enabled(!this.isCreate)
        .onClick(async () => {
          // 音频流信息
          try {

            // AudioCapturer实例的创建
            const audioCapturer = await audio.createAudioCapturer(this.audioCapturerOptions)
            this.audioCapturer = audioCapturer
            this.isCreate = true
            // ----------------文件系统--------------------
            const context = getContext()
            // 通过应用上下文,获取到应用的files路径
            this.filePath = context.filesDir + '/' + Date.now() + '.wav'
            const file = fileIo.openSync(this.filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
            // 订阅(读取音频采集器的数据流,写入到到开的文件中)
            audioCapturer.on('readData', (buffer) => {
             // 写入到打开的文件中
              fileIo.writeSync(file.fd, buffer)
              console.log('音频流大小', buffer.byteLength)
            })
            // 开始录制音频
            await audioCapturer.start()
            // 如果上面报错,就不会运行到这里
            promptAction.showToast({ message: '调用createAudioCapturer成功.' })

          } catch (error) {
            promptAction.showToast({ message: `异常:${error}` })
          }
        })
      Button('停止录音')
        .enabled(this.isCreate)
        .onClick(async () => {
          await this.audioCapturer?.stop()
          promptAction.showToast({ message: '停止成功.' })
        })
      Button('继续录音')
        .enabled(this.isCreate)
        .onClick(async () => {
          await this.audioCapturer?.start()
          promptAction.showToast({ message: '继续录音成功.' })
        })
      Button('释放资源')
        .enabled(this.isCreate)
        .onClick(async () => {
          await this.audioCapturer?.release()
          promptAction.showToast({ message: '释放资源成功.' })
        })
      Divider()
      Button('创建音频渲染器-播放音频')
        .enabled(this.filePath !== '')
        .onClick(async () => {
          try {

            // 1. 创建音频渲染器
            const audioRenderer = await audio.createAudioRenderer(this.audioRendererOptions)
            // 保存起来
            this.audioRenderer = audioRenderer
            // 根据路径打开文件
            const file = fileIo.openSync(this.filePath)
            // 获取文件信息(大小,创建时间等)
            const fileIoStat = fileIo.statSync(file.fd)
            // 准备一个累加值,用于自动停止渲染
            let bufferSize = 0
            // 2. 订阅(写入数据到音频渲染器中,就能发出声音)
            audioRenderer.on('writeData', (buffer) => {
              // 读取打开的 buffer文件,写到渲染器中
              fileIo.readSync(file.fd, buffer)
              console.log('音频渲染器播放', buffer.byteLength)
              bufferSize += buffer.byteLength
              // 累加的结果是否超过文件大小
              if (bufferSize >= fileIoStat.size) {
                audioRenderer.stop()
              }
            })
            // 3. 开始渲染
            audioRenderer.start()

            promptAction.showToast({ message: '音频渲染器正常' })
          } catch (error) {
            promptAction.showToast({ message: '音频渲染器错误:' + JSON.stringify(error) })
          }
        })

      Button('停止渲染')
        .onClick(async () => {
          await this.audioRenderer?.stop()
          promptAction.showToast({ message: '停止成功' })
        })
      Button('销毁实例,释放资源')
        .onClick(async () => {
          await this.audioRenderer?.release()
          promptAction.showToast({ message: '销毁实例,释放资源成功' })
        })
    }
    .height('100%')
    .width('100%')
  }
}
// 释放变量,对象重新赋值为null,可以自动被 垃圾回收机制清理
this.audioCapturee = null

文件管理(文件介绍续)

@ohos.file.fs

创建目录
const context = getContext()
// 通过应用上下文,获取到应用的files路径
const dirPath = context.filesDir + '/' + 'mydir'
if(fileIo.accessSync(dirPath)===false){  // 不存在就创建
	fileIo.mkdirSync(dirPath)
}
fileIo.accessSync( )   检测文件或目录是否已存在
fileIo.mkdirSync( )    创建目录

方法的返回

demo():string{
	return '*****'
}
如果这个方法前面有async就不能这样写
async demo():Promise<AudioInfo>{
	await ......   	
    return 
}

计时

// 开始录音计时
startRecordingCount() {
  this.recordingTime = 0
  clearInterval(this.recordingTimerId)
  this.recordingTimerId = setInterval(() => {
    this.recordingTime++
  }, 1000)
}
// 停止录音计时
stopRecordingCount() {
  clearInterval(this.recordingTimerId)
}

// 展示
Text(
    dayjs(this.recordingTime*1000)
    	.format(this.recordingTime > 1000 * 60 * 60 ? 'HH:mm:ss' : 'mm:ss'
)

进度条Progress

官方文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-common-components-progress-indicator-V5

// 创建一个进度总长为100,初始进度值为24的线性进度条
Progress({ value: 24, total: 100, type: ProgressType.Linear }) 
介绍ProgressType类型
线性样式ProgressType.Linear
环形无刻度样式ProgressType.Ring
环形有刻度样式ProgressType.ScaleRing
圆形样式ProgressType.Eclipse
胶囊样式ProgressType.Capsule
// 播放时间计时开始
startPlayingCount(duration: number) {
  this.isPlaying = true
  // 定义变量来保存当前的时间
  this.playingTime = 0
  // 定义更新间隔,单位为毫秒
  const interval = 200;
  clearInterval(this.playingTimerId)
  this.playingTimerId = setInterval(() => {
    // 累加进度条
    this.playingTime += interval
    if (this.playingTime > duration) {
      this.stopPlayingCount()
    }
  }, interval)
}

拍照

// 调用手机摄像头拍照
import { camera, cameraPicker } from '@kit.CameraKit'

@Entry
@Component
struct PickerIndex {
  @State imageUrl:string = ''
  build() {
    Column() {
      Button('拍照')
        .onClick(async ()=>{
          const pickerResult = await cameraPicker.pick(
            getContext(),
            // 是拍照还是录像
            [cameraPicker.PickerMediaType.PHOTO],
            {
              // 默认调用的摄像头
              cameraPosition:camera.CameraPosition.CAMERA_POSITION_BACK
            }
          )
          // 如果存在图片路径,或者拍照成功
          if (pickerResult.resultUri && pickerResult.resultCode === 0) {
            this.imageUrl = pickerResult.resultUri
          }
        })
    }
    .height('100%')
    .width('100%')
  }
}
// 隐私拍照,图片不出现在媒体库(相册)
Button('拍照')
  .onClick(async ()=>{
    const context = getContext()
    // 文件路径
    const filePath = context.filesDir + '/'+ 'test.jpg'
    // 打开文件用于写入
    fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
    const pickerResult = await cameraPicker.pick(
    getContext(),
    // 是拍照还是录像
    [cameraPicker.PickerMediaType.PHOTO],
    {
       // 默认调用的摄像头
       cameraPosition:camera.CameraPosition.CAMERA_POSITION_BACK,
       // 把 path 转换为 uri 路径
       saveUri:fileUri.getUriFromPath(filePath)
     })
 })
// 把 path 转换为 uri 路径
saveUri:fileUri.getUriFromPath(filePath)
// 把 uri 转换为 path
const file = fileIo.openSync(cri).path

错误上报

import { FaultLogger } from '@kit.PerformanceAnalysisKit'
@Entry
@Component
struct ErrorPage {
  build() {
    Column() {
      Button('查询故障日志')
        .onClick(async ()=>{
          const logs = await FaultLogger.query(FaultLogger.FaultType.JS_CRASH)
          AlertDialog.show({message:JSON.stringify(logs,null,2)})
        })
      Button('抛出异常')
        .onClick(()=>{
          throw new Error('my error')
        })
    }
    .height('100%')
    .width('100%')
  }
}
异常捕获(防止没有捕获到的异常,让程序闪退)
let observerId = -1
export default class EntryAbility extends UIAbility {
  // 创建时
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
    // 注册错误管理器
    observerId = errorManager.on('error',{
        async  onUnhandledException(){
        const logs =  await FaultLogger.query(FaultLogger.FaultType.JS_CRASH)
        const currentFaultLog = logs[0]
        console.log('errorManager',JSON.stringify(currentFaultLog))
      },
    })
  }
  // 销毁时
  onDestroy(): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
    errorManager.off('error',observerId)
  }

报错处理

在这里插入图片描述

在这里插入图片描述

模拟器的问题,模拟器读取不到文件导致,关了,重新启动

举报

相关推荐

0 条评论