HarmonyOS 鸿蒙开发 学习笔记(一)
·
/*
* arkTS 书写的文件 .ets 文件
*
* 装饰器
* @Entry -- 独立的页面
* @Component + struct Index--为自定义组件名字 { } -- 组件 可复用的UI单元
* @State -- 标记为 响应式 变量
*
* build() { UI描述 其内部 以声明式的方式描述UI结构}
* 内置组件 -- 1 属性方法 2 事件方法
*
* 尺寸单位
* 100 数字 不写单位 默认 -- vp -- 虚拟像素 自适应
* '100%' 字符串
*
* 配置权限
* module.json5 文件中
* requestPermissions:[{ },{ }]
*
* zh_CN目录 为 限定词目录 即 与系统匹配的限定目录, base 为默认目录 必须要有
* $r('app.string.XX') -- 读取 string 信息时 先根据 系统环境 读取 en_US 或者 zh_CN 等 语言中的string
* 没有找到 才会 读取 base--element 中 的 string
*
* Image --
* Image($r('app.media.xxx')) 读取 media 中文件 -- 无需后缀
* Image($rawfile('xxx.png')) 读取 rawfile 中文件 -- 需要后缀
* interpolation 图片插值 -- 放大后 相对清晰点
*
* Text --
* Text("string") Text($r('app.string.XX'))
*
* Button --
* slider --
* Blank() -- 占用剩余空间 没有UI显示
* Stack -- 堆叠 后面 叠在 前面 依次入栈 配合 position 实现定位效果
*
* Column --
* Row --
* space -- 间距
* 主轴 -- justifyContent -- FlexAlign 默认 start
* 副轴 -- alignItems, Row -- VerticalAlign, Column -- HorizontalAlign 默认 center
*
* ForEach -- v-for
* ForEach(
* [数组],
* (item,index?) => { 这里生成UI },
* (item: any, index?: number) => { string } 数据 变化是否重新渲染 默认是 item+index 的拼接
* )
*
* if-else -- v-if
* if( true ){ 这里生成UI }
* else{ 这里生成UI }
*
* List List是容器 List内部跟着ListItem
* 必须设置高度才可以滚动 一般占据剩余空间, layoutWeight(1)
* 超出屏幕 会滚动 , 对比 -- 单纯的ForEach超出会隐藏
* 可以纵向 横向 listDirection(Axis.Vertical)
*
* ListItem
* ListItem不是容器,算是一个标记, 所以内部需要写 UI组件
* 内部只能有一个根组件
*
* List(){ ForEach-->
* ListItem(){ UI }
* }
*
* @Component--自定义组件 -- 通用组件的封装
* 位置 -- components --Header.ets -- export struct Header{}
* import {Header} from '../components/Header'
*
* @Component
* struct Header {
* private title: String
* build(){ }
* }
*
* 引用 -- Header( {title:"数据传递"} )
*
* @Builder--自定义构建函数 -- 当前页面自用组件的封装 作用相当于组件所以函数名大写
* 1 全局 在 struct 之外写
* @Builder function ItemCard(item:Item){ UI }
* 使用 -- ItemCard(item)
*
* 2 局部 -- 组件内部 -- 没有 function
* @Builder ItemCard(item:Item){ UI }
* 使用 -- this.ItemCard(item)
*
* @Styles--自定义样式函数 -- 小写开头 内部直接 .调用属性方法
* 1 全局 在 struct 之外写
* @Styles function fillScreen(){
* .width("100%")
* .padding(20)
* }
* 使用 -- Text().fillScreen()
*
* 2 局部 -- 组件内部 -- 没有 function
* @Styles fillScreen(){
* .width("100%")
* .padding(20)
* }
* 使用 -- Text().fillScreen()
*
* @Extend(组件) -- 继承模式 必须写在全局
* @Styles -- 全局样式 内 .css 必须是全局组件才有的样式 比如 宽 高 内外边距
* 像字体大小 这样的 部分组件的独有的样式 要想定义在全局 使用 @Extend(Text)
* @Extend(Text) 必须写在全局 内接非通用的组件 比如 Text
* @Extend(Text) function TextPrice(){
* .fontSize(36)
* }
*
* Text().TextPrice()
*
* 总结
* 全局 必须添加 function
* Styles 可以 用在 全局 或者 局部 必须是通用样式
* Extend(XXX) 只能用在全局 支持所有的样式
*
* 状态管理 -- 装饰器
* @State
* 1 必须初始化
* 2 Object Array string number boolean enum class == 不可以的类型 -> any 联合类型 复杂的数据类型
* 3 不支持 deep 的数据嵌套更改方式 比如 { { a:1 } ,b:2 } a改变无法触发视图更新 b可以
* 此状态下 别的数据触发视图更新 也会带动a对应的视图更新 因为a已经变了 整个页面在更新
*
* 父子组件 之间的数据同步
* @Prop 父传子 单向 父@State 子@Prop 父修改 子同步修改,子修改 父不变(父的数据拷贝到子,所以子不会修改父的原始数据)
* @Link 父传子 双向 父@State 子@Link 父修改 子同步修改,子修改 父同步(父的数据引用给到子,二者操作的都是原始数据)
*
* 1 二者不能初始化 其值由父组件决定
* 2 Link 传值时 传递的是 数据的引用 ({ data:$data }) -- $
* 3 Prop 只能是简单数据类型 string number boolean enum
* Props 父是对象 , 子 是 对象的一个简单属性 可以 this.obj.name 传递的也是简单数据
* 数组 对象 等 复杂类型都不可以传递
* 4 Link 与 State 差不多基本都可以 但是不支持 deep
*
* @Provide
* @Consume
* 二者 相当于 父组件 与 后代组件的 双向数据传递
* 父 与 后代 没有调用 关系 所以 是隐式传递 (provide inject) 与儿子有调用关系 也不需要传递
* 父 @Provide data = 1
* 子 @Consume date :number
* 使用的地方 直接 this.data 即可 没有传递 直接使用
*
* @Observed --
* @ObjectLink --
* 对象嵌套对象,数组中为对象 类似支持数据 deep
* 1 定义的数据 添加 @Observed 装饰器
* 2 使用嵌套数据的组件 也就是子组件 在 定义数据时 添加 @ObjectLink,因为使用 @Link 报错
* 无法添加就将在组件抽取为独立的组件 组件中定义所需要的 数据,添加 @ObjectLink 修饰
*
* 总数据 @Observed 修饰, 使用的数据处 @ObjectLink 修饰
* interface Item{ }
* @Observed
* class MyItem implements Item {
* constructor(item:Item){
* this.xx = item.xx
* }
* }
*
* 子组件要触发 父组件中的方法
* 子组件中定义参数 为 taskHandle:()=>void
* 父组件传递函数
* taskChange(){
* this.data = data
* }
* Sun({ data,taskHandle:taskChange.bind(this) })
* 因为函数中 有this 父组件中调用函数 this指向父组件
* 该函数传递到子组件后被调用,this指向 子组件 数据就会找不到
* 所以父组件在传递函数时需要绑定 this -- bind(this)
*
* 页面路由 -- router
* 页面栈 先入后出 ,最大容量为 32 个 页面,使用 router.clean() 可以清空页面栈,释放内存
* 1 router.pushUrl() 当前页入栈,新页面在栈顶 可以 router.back() 返回当前页,新页面出栈
* 2 router.replaceUrl() 新页面替换当前页,当前页销毁释放资源,无法返回当前页
*
* Router页面有两种实例模式
* 1 Standard 标准实例模式 默认模式 每一次都是重新创建 入栈
* 2 Single 单利模式,如果栈中已经存在该页面,该页面就会被移到栈顶 重新加载显示
*
* 二者根据场景结合使用
*
* 新页面需要在 src/main/resources/base/profile/main_pages.json 注册
* 创建 page 时会自动注册
*
/* import router from '@ohos.router'
router.pushUrl({
url: "", // 目标页路径
params: { id: 1 } // 传递参数 可选
},
router.RouterMode.Single, // 页面模式
err => {
*/
/*
异常回调
code
100001 内部错误可能是渲染失败
100002 路由地址错误
100003 页面栈超过32个
/*
}
)
const params = router.getParams()
// 返回前的弹窗提示信息
router.showAlertBeforeBackPage({ message: "返回前的弹窗提示信息" })
// 返回上一页
router.back()
// 返回指定页
router.back({ url: "", params: {} })
*
*
* 页面内动画
* 属性动画 与 显式动画 基本实现的是一种过渡效果 渐变效果
*
* 属性动画 .animation( { duration:1000,curve:Curve.EaseInout } ) 参考文档
* 该属性动画要放在最后面,其前面的属性发生变化才会触发动画 -- 属性值定义为 状态值
* 支持 width height Opacity backgroundColor Scale rotate translate 实现渐变效果
* 1 定义支持动画的 样式属性
* 2 后面添加 animation属性
* 3 触发之前定义的样式属性 --> 改变State
*
* 显式动画 animateTo()函数
* animateTo(
* { duration:1000,curve:Curve.EaseInout },参考文档
* ()=>{ 直接修改 跟样式关联的 @State的值 改变谁 谁对应的样式做动画 -->this.x +=1 }
* )
*
*
* 组件内转场动画 基本实现的是组件 插入 , 移除 时的过渡动画
* .transition({
* opacity:0 入场的起点和离场的重点 (根据if 逻辑判断是 入场还是离场)
* rotate:{angle:0}
* scale:{x:0,y:0}
* })
*
* 搭配 if(this.isShow){
* Text().transition()
* }
* animateTo({},()=>this.isShow = true)
* 必须使用 animateTo 改变 isShow 才会触发转场动画
*
* 页面间的动画
* 共享元素转场动画
* .sharedTransition(id: string, options?: sharedTransitionOptions)
* SharedTransitionEffectType 设置 Exchange(默认) 还是 Static
* Exchange类型的共享元素转场 -- 两个页面中,配置为相同id的组件,适用于两个页面间相同元素的衔接
* Static类型的共享元素转场 -- 一个页面中有Static的共享元素,通常用于页面跳转时,标题逐渐出现或隐藏的场景
*
* PageA
* Image($r('app.media.mountain')).width(50).height(50)
* .sharedTransition('sharedImage1', { duration: 1000, curve: Curve.Linear })
* PageB
* Image($r('app.media.mountain')).width(150).height(150)
* .sharedTransition('sharedImage1', { duration: 500, curve: Curve.Linear })
*
* Text("SharedTransition test page")
* .sharedTransition('text', { duration: 500, curve: Curve.Linear, type: SharedTransitionEffectType.Static })
* 总结:Exchange 一个组件 存在于俩页面中,设置相同的 sharedTransition(id: string) 页面跳转时 该组件做动画
* Static 一个组件,只存在于一个页面中,当跳转到该页面或者离开时 该组件做动画 一般显示或隐藏
*
* 页面转场动画
* 两个页面间发生跳转,一个页面消失,另一个页面出现,这时可以配置各自页面的页面转场参数实现自定义的页面转场效果
*
// page A
pageTransition() {
// 定义页面进入时的效果,从左侧滑入,时长为1200ms,无论页面栈发生push还是pop操作均可生效
PageTransitionEnter({ type: RouteType.None, duration: 1200 })
.slide(SlideEffect.Left)
// 定义页面退出时的效果,向左侧滑出,时长为1000ms,无论页面栈发生push还是pop操作均可生效
PageTransitionExit({ type: RouteType.None, duration: 1000 })
.slide(SlideEffect.Left)
}
*
* duration: 0 -- 禁用某页面的页面转场
* type: RouteType.Push
* type: RouteType.Pop
* A B 页面
* 新增 右侧滑入 左侧滑出
* Push -- PageTransitionEnter(.slide(SlideEffect.Right)) -- PageTransitionExit(slide(SlideEffect.Left))
* 返回 左侧滑入 右侧滑出
* Pop -- PageTransitionEnter(.slide(SlideEffect.Left)) -- PageTransitionExit(slide(SlideEffect.Right))
* 四个
*
*
* Stage 模型 舞台模型
* Ability Module (能力模块) -->打包--> HAP(Entry Feature 多个HAP)--Bundle--APP
* library Module (共享模块) -->打包--> HSP
*
* 配置文件
* 1 全局配置文件 AppScope--app.json5
* bundleName -- 唯一标识 -- 包名
* icon--label 对应的是设置里面--应用管理的 图标与描述
*
* 2 模块配置文件 entry--configuration--module.json5
* requestPermissions -- 权限添加
* type -- entry 入口类型, feature 功能类型, shared 共享类型
* mainElement -- 该模块的入口文件
* deviceTypes 每个模块对应的设备类型
* deliveryWithInstall 该模块是否跟随APP 安装(entry 肯定安装 feature 不一定)
* pages 该模块下所有页面的配置文件
* abilities -- 创建的所有模块
* abilities -- 内配置的 icon--label 对应的是桌面的 图标与描述
* skills 该模块的 功能 根跳转有关系
* 入口就是 entities -- entity.system.home
* actions -- action.system.home
*
* UIAbility 生命周期
* onCreate --> onWindowStageCreate --> onForeground (visible , active) --> (inActive inVisible)
* --> onBackground --> onWindowStageDestroy --> onDestroy
*
* onWindowStageCreate --> (visible , active, inActive inVisible)
*
* windowStage.loadContent('pages/Index' 默认加载的页面
*
* Ability 内 页面与组件 的 生命周期
* 页面 只有带 @Entry 的组件才可以用
* 展示页面 (onPageShow)
* 返回 (onBackPress)
* 隐藏页面 (onPageHide)
*
* 组件 -- 所有组件
* 1 创建组件实例 --> aboutToAppear(数据初始化) -->
* 2 执行 build 函数
*
* 销毁组件 (之前 aboutToDisappear)
*
* 页面销毁 其中所有组件必然销毁
* 页面不销毁 其中 组件可以单独创建或者销毁(if)
*
* 这里页面也是组件 只不过是 入口组件
* 页面
* aboutToAppear --> onPageShow --> onPageHide
* aboutToAppear --> onPageShow --> aboutToDisappear
* 组件
* aboutToAppear --> aboutToDisappear
*
*
* 感觉类似 Android 的 Activity 嵌套 Fragment
*
*
* UIAbility 启动模式 针对同一个 Ability 的实例
* "launchType": "singleton",
*
* 1 Singleton 每次启动UIAbility 任务列表中只存在唯一实例 (默认)
* home键 生命周期 不重复走 onCreate , onForeground -- onBackground 二者切换 ,任务栈只有一个实例
*
* 2 Standard 每次启动UIAbility 都会创建新实例,旧实例 也在
* home键 生命周期 重复走 onCreate -- onForeground -- onBackground ,任务栈有多个 实例共存
*
* 3 multition 每次启动UIAbility 都会创建新实例,旧实例 不在
* home键 生命周期 重复走 onCreate -- onForeground -- onBackground ,任务栈只有一个实例
*
* 4 Specified 每个UIAbility 实例 设置一个 Key,启动 UIAbility 时 需要指定 key ,有 key 对应的实例直接使用,没有新创建
*
* 需要新的 ability 实现某个功能,比如文档编辑器
* 点击 文档1 到 新界面 编辑1 ,点击 文档2 到新界面编辑 2,反复操作时
* Singleton 不重走 onCreate 无法刷新数据
* Standard 重复创建
* multition 重复创建
* Specified 已经存在 不创建 没有 才创建
*
* 一个 ability 调起 另一个 ability
* 1
* context = getContext(this) as common.UIAbilityContext
*
* let want = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.example.myapplication', // 可以跨应用
abilityName: 'FuncAbility', // 名字
moduleName: 'module1', // 模块名 非必选
parameters: { // 自定义信息 key
instanceKey: getInstance(生成不同key),
},
}
*
* // context为调用方UIAbility的AbilityContext
this.context.startAbility(want)
.then(() => {
// ...
}).catch((err) => {
// ...
})
*
* 2 目标ability
* import AbilityStage from '@ohos.app.ability.AbilityStage';
export default class MyAbilityStage extends AbilityStage {
onAcceptWant(want): string {
// 在被调用方的AbilityStage中,针对启动模式为specified的UIAbility返回一个UIAbility实例对应的一个Key值
// 当前示例指的是module1 Module的FuncAbility
if (want.abilityName === 'FuncAbility') {
// 返回的字符串Key标识为自定义拼接的字符串内容
return `ControlModule_EntryAbilityInstance_${want.parameters.instanceKey}`;
}
return '';
}
}
*
* 3 配置 module.json5
* name : "entry",
* type : "entry",
* srcEntry:"MyAbilityStage" // 所有的 ability 的舞台 根据 want.abilityName 做判断
*
* 直接跳转 省略 want 的 parameters 与 步骤 2,3 即可
*
*
* 网络请求
* Http
* WebSocket
* Socket
*
* 添加权限
*
* Http
* 1 http 模块原生请求
*
* OpenHarmony 三方库中心仓
*
* 2 Axios
* 1 ohpm 包安装管理工具
* 1 下载 --> cmd -- ohpm/bin/ --> init.bat --> 配置环境变量
* --> 用户环境变量 新建 OHPM_HOME:ohpm/bin/ --> 在 PAth 中 新建 %OHPM_HOME%
* --> ohpm -v
* 2 下载 axios
* studio 中 执行
* ohpm install @ohos/axios
* 3 使用 axios
* import axios from "@ohos/axios"
* axios.get(
* "url",
* {
* params:{ "k":"v" } // get 传参
* data:{ "k":"v" } // post 等 传参
* }.then()
* .catch()
* )
*
* 后面 写一个封装好的 axios 来用
*
*
* 数据持久化
*
* 用户首选项 -- Preference
* 轻量级
* key -- value (string number boolean 以及其数组)
* 以 Promise 的方式 操作
*
* 封装一个工具类
* 以 map 的形式存储 Preference
* 与 sharePreference 的区别
* put 之后 要刷到磁盘中
* await pre.put('name',value)
* await pre.flush()
*
*
* 关系型数据库 -- SQLite
* relationalStore
*
* 1 创建数据库
* // rdb 配置
* const config = {
* name:'MyApplication.db',
* securityLevel:relationalStore.SecurityLevel.S1
* }
*
* // 初始化 SQL 语句
* const sql = `CREATE TABLE IF NOT EXISTS TASK (
* ID INTEGER PRIMARY KEY AUTOINCREMENT,
* NAME TEXT NOT NULL,
* FINISHED bit
* )`
*
* // 获取 rdb
* relationalStore.getRDBStore(context,config,(err,rdbStore) =>{
* if(err) return "失败"
* // 执行 SQL 语句
* rdbStore.executeSql(sql)
* this.rdbStore = rdbStore
* })
*
*
* 2 增删改 类似 Room操作
* this.rdbStore.insert(tableName,{ id:1,name:'张三'} )
*
* // 条件谓词 不指定 就是全部
* const predicates = new relationalStore.RdbPredicates(tableName)
* predicates.equalTo("ID",id) // 删除指定 ID
*
* this.rdbStore.delete(predicates)
*
* const task = {name:'李四'}
* this.rdbStore.update(task,predicates)
*
* 3 查
* // 条件谓词 这里查询全部
* const predicates = new relationalStore.RdbPredicates(tableName)
* const result = await this.rdbStore.query(predicates,['id','name']) // 查询哪些字段
*
* 解析result
* const tasks:any[] = []
* // 遍历 result
* while(!result.isAtLastRow){
* // 指针移动到下一行
* result.goToNextRow()
* // 根据字段名 取出数据
* const id = result.getLong(result.getColumnIndex('ID'))
* const name = result.getString(result.getColumnIndex('NAME'))
* tasks.push({id,name})
* }
*
*
*
* 通知
* 1 基础通知
* notificationManager
* // 通知请求
* const request: notificationManager.NotificationRequest = {
* id: 10,
* content:{ // 通知内容
* contentType:
* 1 普通文本型 只有一行 文本多了 隐藏
* 2 长文本 只有一行 多了 可以通过折叠按钮打开显示 briefText:通知概要和总结 -->不起作用
* 3 多行文本 多行 可以通过折叠按钮打开 多行并列显示 briefText:通知概要和总结 -->不起作用
* 4 图片型 可以通过折叠按钮打开 显示图片 briefText:通知概要和总结 -->起作用
* picture:this.pixel
* }
* deliverTime: new Date().getTime() // 显示当前时间
* showDeliverTime:true
* groupName:'test' // 分组
* slotType: 通知显示的类型 顶部状态栏是否有图标 是否有提示音 是否弹出横幅框提示
* 1 社交类型 2 服务类型 3 内容类型 4 其他
* }
*
* 获取 图片的 pixel
* async aboutToAppear(){
* const rm = getContext(this).resourceManager
* const file = await rm.getMediaContent($r('app.media.XXX'))
* image.createImageSource(file.buffer).createPixelMap()
* .then(v=>this.pixel = v)
* .catch(e=>e)
* }
*
* 具体看 文档
*
*
* 2 进度条通知 -- 主要用于文件下载,长任务处理进度
*
* 1 判断当前系统是否支持进度条模版 可以放在初始化中
* this.isSupport = await notificationManager.isSupportTemplate('downloadTemplate')
* if(!this.isSupport) return
*
* 2 定义通知请求
* // 通知模版
* const template = {
* name:'downloadTemplate' ,// 固定写法
* data:{progressValue:this.progressValue,progressMaxValue:100}
* }
*
* // 通知请求
* const request:notificationManager.NotificationRequest = {
* id:999, // 一般是固定 id 进度条只有一个 重复覆盖
* template:template,
* content:{
* contentType:notify.ContentType>NOTIFICATION_CONTENT_BASIC_TEXT
* normal:{
* title:this,fileName + ': ' + this.state,
* text:'' // 一般用不上
* additionalText:`${this.progressValue}`
* }
* }
* }
*
* 3 发送通知 -- 请求需要循环发送 才会有进度
* notify.publish(request)
*
*
* 3 通知意图 (行为意图) -- 即通过通知 跳转对应的页面
* 也可以设置按钮 通过通知中的按钮 实现意图
*
* 1 意图行为信息
* const wantInfo = {
* wants:[
* {
* deviceId:'' // 不写就是本机
* bundleName:'包名'
* abilityName:'要启动的ability'
* action:''
* entities:[]
* }
* ]
* operationType:START_ABILITY // 启动
* requestCode:0
* wantAgentFlags:[CONSTANT_FLAG] // 常量
* }
*
* 2 创建 wantAgent 实例 初始化数据中创建
* this.wantAgentInstance = await wantAgent.getWantAgent(wantInfo)
*
* 3 通知请求
* const request:notificationManager.NotificationRequest = {
* id:999, // 一般是固定 id 进度条只有一个 重复覆盖
* template:template,
* wantAgent:this.wantAgentInstance // 设置通知意图
* content:{
* */
更多推荐
所有评论(0)