HarmonyOS知识点

HarmonyOS应用模型Stage&FA 、ArkTS、ArkUI
Stage模型:
Module - UIAbility - Page - component

装饰器@Entry、@Component、@CustomDialog、@State、关键字struct、函数build()

系统组件(Text Column Row Scroll)、自定义组件 、组件复用、组件事件、组件属性、组件状态(能够对对象深度响应式改变)

在arkUI中,我们的内容如果超过了屏幕显示,则不会显示滚动条,需要使用Scroll来包裹
需要注意的是: 该组件滚动的前提是主轴方向大小小于内容大小。子组件不要设置高度,否则不能滚动

样式(链式&枚举)
vp和适配(伸缩布局,网格系统,栅格系统)
vp 是鸿蒙默认单位,和屏幕像素有关,最终表现视觉大小在任何设备一致
1、伸缩 layoutWeight(flex: number) 占剩余空间多少份,可以理解成CSS的 flex: 1
2、内容等比例缩放-可以使用aspectRatio属性设置宽高比

Image的四种使用方式

1、使用本地图片  Image('/assets/test.jpg')  /src/main/ets/assets/test.jpg
2、使用资源引入 Resource 类型  Image(\$r("app.media.test"))  /src/main/resources/base/media/test.jpg
3、采用原始文件引入  Image(\$rawfile('test.jpg'))  /src/main/resources/rawfile/test.jpg
4、引用网络图片 Image("https://...")   在模拟器中需要在module.json5文件中配置网络权限才能显示

垂直水平居中
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)

@Styles复用、@Extend复用

StateStyle(正常态normal,按压态pressed,获焦态focused,禁用态disabled)样式抽离只能抽离到组件内,不能抽离到全局

if else 创建销毁,opacity隐藏后占位置,visibility: Visibility.Hidden占位置, visibility: Visibility.None不占位置

数据父传子,事件父传子实现数据子传父

// 把子组件的item传给父组件
// 子
public item: Partial<ReplyItem> = {} // 非响应式数据
public changeLike: (params: ReplyItem) => void = () => {}
.onClick(()=>{
 	this.changeLike(this.item as ReplyItem)
})
// 父
MainItem({item: item, changeLike: (obj: ReplyItem)=>{
  this.changeLike(obj)
}})
changeLike(obj: ReplyItem){
	......
}
// Partial 把需要传入的对象ReplyItem内的属性变为可选的(可传可不传)
public item: Partial<ReplyItem> = {} // 非响应式数据
// this.item 属性都是可选的,使用 as 规避类型错误,强制认为ReplyItem内有属性有值
this.changeLike(this.item as ReplyItem)

构建函数
@Builder - 自定义构建函数,复用单元,对于修改响应式数据只能修改按引用传递(obj)不能按值传递参数(string)

@BuilderParam 类似于Vue中slot插槽的作用,只能应用在Component组件中,不能使用Entry修饰的组件中使用。尾随闭包、

@Builder setBtn() {
  Button('你好')
}
Card({getContent: this.setBtn})
// Card
@BuilderParam getContent: () => void = () => {}
this.getContent()

@Prop单向数据流(父变子变,子变父不变)
@Link双向绑定 注意:Link修饰的数据必须得是最外层的 State数据,不能传变化后的,比如forEach里的参数item就不允许传

状态共享-后代组件
父@Provide定义 和 子@Consume接收

@Watch监听

@Observed修饰类与@ObjectLink接收,可以解决@Link弊端,如上面提到的forEach里的参数item就不允许传到@link,此时就可以传item了
@ObjectLink不能整体赋值,只能对里面的属性赋值

@ObjectLink upClass: UploadClass
@Observed
class UploadClass {
  images: ImageList[] = []
}
多层state修改
this.user.address.city = "廊坊"
this.user.address = new IAddressModel(this.user.address)
// border边框线
.border({
  width: 1,
  color: {
    left: Color.White,
    right: Color.White,
    top: Color.White,
    bottom: Color.Black
  },
})
// 渐变色
.linearGradient({
  angle: '94deg',
  colors: [[$r('app.color.color_FF852E'), 0], [$r('app.color.color_FF5F2E'), 99]]
})
// 最大宽 高
.constraintSize({
   maxWidth: '',
   maxHeight: ''
})
// 实现软键盘弹出后,整体布局不变
.expandSafeArea([SafeAreaType.KEYBOARD]) 
// 键盘避让
windowStage.getMainWindowSync().getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE)
// 关闭输入法软键盘
inputMethod.getController().stopInputSession()
// 主动调起软键盘
focusControl.requestFocus('input_id')
// 事件穿透
.hitTestBehavior(HitTestMode.None)

LocalStorage、AppStorage

持久化的数据
PersistentStorage.PersistProp<string>("user_cart", "[]")
页面生命周期
aboutToAppear: 页面初始化触发一次。
onPageShow: 页面每次显示时触发。
onPageHide: 页面每次隐藏时触发一次。
aboutToDisAppear: 页面销毁时触发一次。
onBackPress: 当用户点击返回按钮时触发。

自定义组件生命周期
aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
aboutToDisappear:在自定义组件即将析构销毁时执行

UIAbility生命周期
UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态
创建到销毁的过程
onCreate - onWindowStageCreate- onForeground - onBackground - onWindowStageDestroy - onDestroy

ability通信

// abilityA - 页面
const context = getContext(this) as common.UIAbilityContext
let want: Want = {
   bundleName: 'com.example.helloworld', // 包名 AppScope/app.json5 app.bundleName
   abilityName: 'HeimaPayAbility', // HeimaPay/src/main/module.json5
   moduleName: 'HeimaPay',
   parameters: {
     order_id: Date.now()
   }
}
const result = await context.startAbilityForResult(want) //拉起另外一个ability并接收另外一个ability的返回值
const params =   result.want.parameters as ResultParams
type  ResultParams = Record<string, boolean> // Record相当于Map,定义一个键值对类型
// abilityB
type AbilityParams = Record<string, number>
onCreate(want: Want, launchParam) {
  const params = want.parameters as AbilityParams
  // 将参数设置到AppStorage
  AppStorage.SetOrCreate<number>("order_id",  params.order_id )
}
// abilityB - 页面
// 销毁当前的ability 将支付结果告诉调用我的人
const context = getContext(this) as  common.UIAbilityContext
context.terminateSelfWithResult({
  resultCode: 1,
  want: {
    abilityName: 'EntryAbility',
    bundleName: 'com.example.helloworld',
    moduleName: 'entry',
    parameters: {
      paySuccess: true
    }
  }
})

通知发布,通知唤起ability

// 发起一个通知
notificationManager.publish({
  content: {
    contentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
    normal: {
      title: "温馨提示",
      text: "您有一个新订单",
      additionalText: "订单提醒"
    }
  },
  wantAgent: await wantAgent.getWantAgent({ // 点击通知拉起一个ability
    wants: [{
      bundleName: "com.example.mylogisticsapp01", //包名在最上面AppScope里 app.json5
      abilityName: "ListenAbility",
      parameters: {
        order_id: Date.now()
      }
    }],
    operationType: wantAgent.OperationType.START_ABILITY,
    requestCode: 0
  })
})

路由传值

路由模式
● Standard:标准实例模式,也是默认情况下的实例模式。每次调用该方法都会新建一个目标页,并压入栈顶。
● Single:单实例模式。即如果目标页的url在页面栈中已经存在同url页面,则离栈顶最近的同url页面会被移动到栈顶,并重新加载;如果目标页的url在页面栈中不存在同url页面,则按照标准模式跳转。
简单理解
1. Standard-只要你push,页面栈里面就会加一项,不管之前加没加过
2. Single- 你之前加过,不会加新的页面,会把你之前加过的页面加出来
router.pushUrl({
  url: 'pages/CarRecord/CarRecord',
  params: {
    id: this.taskDetailData.id
  }
})
// 接收
const params = router.getParams() as CommonRouterParams
if (params && params.id) {
  
}
// 类型
export class CommonRouterParams {
  id?: string = ''
}

震动

import vibrator from '@ohos.vibrator';
vibrator.startVibration({ duration: 300, type: 'time' }, { id: 0, usage: 'touch' })

声音播放

AVPlayerClass.playAudio("success.wav", getContext(this)) // 单例 音频播放都通过这一个单例 也不需要new
// 播放类  单例
export class AVPlayerClass {
  // 调用之前不希望创建avPlayer  所以给个联合类型null  默认是null
  static avPlayer: media.AVPlayer | null = null
  static audioName: string = "" //存储当前的音频的名称
  static isSettingBack: boolean = false // 是否设置过回调 因为针对某一个player只能设置一次回调
  // 提供一个只播放音乐的静态方法
  static async playAudio(audioName: string, context: Context) {
    if (!AVPlayerClass.avPlayer) {
      AVPlayerClass.avPlayer = await media.createAVPlayer()
    }
    //如果audio有值
    if (audioName) {
      // 将这个值对应的音频文件读过来给avPlayer的fdSrc
      AVPlayerClass.audioName = audioName
      AVPlayerClass.setAvPlayerAudio(context)
    }
  }
  // 注册avplayer回调函数
  static setAVPlayerCallback() {
    // seek操作结果回调函数
    AVPlayerClass.avPlayer.on('seekDone', (seekDoneTime: number) => {
      // console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);

      AVPlayerClass.avPlayer!.play()//回到0秒后 才播放   播完了就会走到 case 'completed': 需要重置

    })
    // error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程
    // 状态机变化回调函数
    AVPlayerClass.avPlayer!.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
      switch (state) {
        // case 'idle': // 成功调用reset接口后触发该状态机上报
        //   console.info('AVPlayer state idle called.');
        //   AVPlayerClass.avPlayer.release(); // 调用release接口销毁实例对象
        //   break;
        case 'initialized': // avplayer 设置播放源后触发该状态上报
          console.info('AVPlayer state initialized called.');
          AVPlayerClass.avPlayer!.prepare();//准备播放           ! 是类型的非空断言
          break;
        case 'prepared': // prepare调用成功后上报该状态机
          console.info('AVPlayer state prepared called.');
          AVPlayerClass.avPlayer!.seek(0); // 调用播放接口开始播放  //将音频的播放进度挪到0的位置
          break;
        case 'playing': // play成功调用后触发该状态机上报
          break;
        case 'paused': // pause成功调用后触发该状态机上报
          console.info('AVPlayer state paused called.');
          break;
        case 'completed': // 播放结束后触发该状态机上报
          console.info('AVPlayer state completed called.');
          AVPlayerClass.avPlayer.reset(); // 调用reset接口初始化avplayer状态  //重置了
          break;
        case 'stopped': // stop接口成功调用后触发该状态机上报
          console.info('AVPlayer state stopped called.');
          break;
        case 'released':
          console.info('AVPlayer state released called.');
          break;
        default:
          console.info('AVPlayer state unknown called.');
          break;
      }
    })
  }

  static async setAvPlayerAudio(context: Context) {
    //AVPlayerClass.audioName
    let fileDescriptor = await context.resourceManager.getRawFd(AVPlayerClass.audioName)
    let avFileDescriptor: media.AVFileDescriptor =
      { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
    if (!AVPlayerClass.isSettingBack) {
      // 设置回调 回调只能设置一次
      AVPlayerClass.setAVPlayerCallback()
    }
    if (AVPlayerClass.avPlayer) {
      // 拿到了资源 赋值给播放器的src属性  意味着会走回调
      AVPlayerClass.avPlayer.fdSrc = fileDescriptor;
    }
  }
}

动画
显式动画(animateTo)和属性动画(animation)

.onClick(() => {
  animateTo({
    duration: 500
  }, () => {
    this.showCom = !this.showCom
  })
})
Image($r("app.media.test"))
 .transition({
   type: TransitionType.Insert,
   rotate: {
     angle: -360
   }
 })
 .transition({
   type: TransitionType.Delete,
   rotate: {
     angle: 360
   }
 })

混合开发sdk通信

Web({ src: $rawfile('location.html'), controller: WebSdkClass.controller! })
	.height('100%')
	.width('100%')
	.javaScriptProxy({
	  object: this.webSdk,
	  name: 'shenling',
	  methodList: ['alert', 'toast', 'getLocation', 'receiveData'],
	  controller: WebSdkClass.controller!
	})
// location.html
shenling.receiveData("search_list",JSON.stringify(result?.poiList?.pois || []))
// web_sdk.ets
export class WebSdkClass {
  static controller: Webview.WebviewController | null = null
  static context: Context | null = null
  alert(msg: string) {
    AlertDialog.show({message: msg})
  receiveData(eventName: string, json: string){
    WebSdkClass.context?.eventHub?.emit(eventName, json)
  }
}
回答: 
鸿蒙原生和h5通信有大概两种方式
1. 通过消息机制推送,类似于前端的postMessage的跨域通信,首先通过原生端的webviewController进行创建通信端口, createMessagePorts, 该方法会创建两个端口,一个用作原生端,一个用作h5端,然后通过webviewController将其中一个端口通过postMessage发送到h5端,h5端用onmessage来接收该端口,并全局缓存,此时,h5端用接收的端口发送消息给原生端,原声端用自己剩余的端口进行onMessage接听消息,此时就可以实现双向通信
2. api调用的形式
    原生端可以通过javascriptProxy给h5页面注入可用的sdk应用方法集合,
    在h5完成初始化后,可以直接调用原生的方法,这是h5调用原生
   	原生端也可以直接调用h5端的方法 runJavascript,在这个方法里面传入方法调用传参数就可以
需要注意的点: 如果原生端的sdk方法是个异步方法,在h5端无法及时得到结果,
此时需要再用原生反调h5进行传递结果

坑点
watch不能写在修饰符前面
prop只支持数字、字符串、布尔三种类型

总结:
● Column/Row
● Text/Button/TextInput
● Flex-List-带滚动条-Scroll(不要对子组件的宽和高进行设置,内部只能放一个子组件)
● 条件渲染(if else)/循环渲染(ForEach)
● State修饰符(声明响应式数据)- 驱动UI的更新-只会监听第一层数据

● State组件内状态
● Prop子组件修饰符-4.0 boolean/number/string- 单向数据流
● Link 子组件修饰符-双向数据流,所有类型都支持- 必须通过$前缀-(循环数据就没有办法传入)
● Provide和Consume 双向数据流-所有结构均支持
● Watch 可以监听State Link Prop ObjectLink的数据变化
● Observed和ObjectLink

● Link-双向
● Prop-单向
● Observed 和 ObjectLink- (肯定不能用在entry修饰的组件中)
● LocalStorage(UIAbility)-AppStorage-PersionStorage(写入磁盘-不能每次都直接运行)
PersionStorage来声明一下属性就可以保证该属性会被写入缓存
● StorageProp-StorageLink
● Http
● 嵌套更新数据的方式-Next

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值