Published on

初探 HarmonyOS

Authors
  • avatar
    Name
    MissTree
    Twitter

HarmonyOS

HarmonyOS Next 开发语言是 ArkTS,类似于TypeScript,对于学习过前端开发人员来说,上手难度较低。 项目结构有点像 Java 、Kotlin ,但是 ArkTS 是基于 TypeScript 的,所以 TypeScript 的语法都可以使用。

DevEco Studio 安装

DevEco Studio官网

项目开发

页面的开发和前端的移动端开发很像,在单位使用尽量使用vp而不是px,使各个设备都适配UI px转vp的方式为:vp2px(100) 表示 100px转vp

@Entry
@Component
struct Index {
  @State message: string = 'HarmonyOS';
  @State search: string = '请输入搜索内容';

  build() {
    RelativeContainer() {
      Stack({alignContent:Alignment.Bottom}){
        // 顶部搜索
        Stack({alignContent:Alignment.Top}){
          Row(){
            TextInput({
              placeholder:this.search
            })
              .onFocus(()=>{
                console.log('onFocus')
              })
          }
          .height(80)
          .width('100%')
          .backgroundColor("blue")
        }
        .zIndex(1)
        .position({
          top:0,
          left:0
        })
        // 滚动区域
        Scroll(){
          Column(){
            Row(){
              Text(this.message)
                .id('HelloWorld')
                .fontSize($r('app.float.page_text_font_size'))
                .fontWeight(FontWeight.Bold)
                .alignRules({
                  center: { anchor: '__container__', align: VerticalAlign.Center },
                  middle: { anchor: '__container__', align: HorizontalAlign.Center }
                })
                .onClick(() => {
                  this.message = this.message== 'Welcome'?'HarmonyOS':'Welcome';
                })
            }
            .height('100%')

            Row(){
              Text('滚动内容1').height(500)
            }
            Row(){
              Text('滚动内容2').height(500)
            }
          }

        }
        // 底部 tab
        Row(){
          Text('tab1')
            .textAlign(TextAlign.Center)
            .layoutWeight(1)
            .fontWeight(FontWeight.Bold)
          Text('tab2')
            .layoutWeight(1)
            .textAlign(TextAlign.Center)
          Text('tab3')
            .layoutWeight(1)
            .textAlign(TextAlign.Center)
          Text('tab4')
            .layoutWeight(1)
            .textAlign(TextAlign.Center)
            .onClick(()=>{
              console.log('点击了tab4')
            })
        }
        .width('100%')
        .height(70)
        .position({
          bottom:0,
          left:0
        })
      }
    }
    .height('100%')
    .width('100%')
  }
}

页面展示

页面组件

  • Text:文本组件
  • Image:图片组件
    • 网络路径:Image('https://baidu.com/images/1.png') 表示图片的路径
    • 本地路径:Image($('app.images.avatar')) 图片默认读取名称,不需要加后缀(不要设置相同名称的不同文件)
    • fillColor:填充颜色
  • Button:按钮组件
  • Scroll:滚动组件
  • Stack:层叠组件
    • alignContent: Alignment.Center
  • CheckBox:复选框组件
  • Span:文本组件
  • Column:列组件 不具备可滚动效果
  • Row:行组件 不具备可滚动效果
  • Divider:分割线组件
  • AlertDialog:弹窗组件
  • Navigator:导航组件
  • Tab:标签组件
  • List:列表组件
  • Card:卡片组件
  • Badge:角标组件
  • Dialog:对话框组件
  • Toast:提示框组件
  • Loading:加载组件
  • Grid:网格组件
  • Picker:选择器组件
  • DatePicker:日期选择器组件
  • TimePicker:时间选择器组件
  • Switch:开关组件
  • Slider:滑块组件

Swiper- 轮播图组件

文档

@Entry
@Component
struct SwiperSample {
  build() {
   Column() {
    Swiper() {
      Image('https://baidu.com/images/1.png')
      Image('https://baidu.com/images/2.png')
      Image('https://baidu.com/images/3.png')
    }
    .indicator(true) // 显示指示器
    .indicatorSize(10) // 指示器大小
    .indicatorPosition(IndicatorPosition.Bottom) // 指示器位置
    .indicatorAlign(IndicatorAlign.Center) // 指示器对齐方式
    .indicatorShape(IndicatorShape.Rect) // 指示器形状
    .interval(3000)  // 轮播间隔时间
    .duration(500)
    .loop(true)  //开启循环
    .height(300)
    .autoplay(true)  // 自动播放
   }
  }
}

Scroll- 滚动组件

文档


@Entry
@Component
struct Index {
 myScroll:Scroll = new Scroll()

 build() {
  // 滚动区域
  Scroll(this.myScroll){
    Column(){
      Row(){
        Text('滚动内容1').height(500)
      }
    }
  }
  .height(500)
  .scrollable(ScrollDirection.Horizontal) // 水平滚动  默认纵向
  .scrollBar(true) // 显示滚动条
  .scrollBarColor('#ff2787d9') // 滚动条颜色
  .scrollBarWidth(10) // 滚动条宽度
  .scrollBarHeight(10) // 滚动条高度
  .scrollBarRadius(5) // 滚动条圆角
  .onScroll((x,y)=>{
    // 可以监听滚动事件,设置回到顶部按钮的显示与隐藏
  })
 }
 .Button('按钮')
 .onClick(()=>{
   const y= this.myScroll.currentOffset().yOffset // 获取当前滚动位置
   this.myScroll.scrollEdge(100,0,0) // 滚动到指定位置
 })
}

Tabs- 组件

文档


@Entry
@Component
struct Index {
 titles:string[]=['首页','关注',"热门',"财经",'美食','旅行']
 private controller: TabsController = new TabsController();

 build() {
  Tabs({barPosition: BarPosition.Start, index: 1, controller: this.controller }){
   ForEach(this.titles,(item:string,index)=>{
    Tabcontent(){
      Text(`${item}内容`)
    }
    .tabBar(item)
   }
  }
  .vertical(true)
  .scrollable(true) // 
  .onChange((x,y)=>{
    // 切换事件,可以设置tabbar的样式
  })
 }
}

tabs属性

  • barPosition :调整位置 开头 或 结尾 (参数)
  • vertical:调整导航 水平 或 垂直
  • scrollable:调整是否 手势滑动 切换
  • animationDuration:点击滑动动画时间

页面属性设置

  • layoutweight:类似css的弹性盒子的flex属性,设置权重,使页面元素可以自适应
  • justifyContent:类似css的flex-start、flex-end、center、space-between、space-around,设置元素在主轴上的对齐方式
  • margin:margin({top: 10, left: 10, right: 10, bottom: 10}) 设置外边距
  • alignItems: alignItems({VerticalAlign.Center}) 设置元素在交叉轴上的对齐方式
  • backgroundImage:backgroundImage('https://baidu.com/images/1.png') 设置背景图片
  • backgroundImageSize:backgroundImageSize({ImageSize.Cover}) 设置背景图片大小
  • Flex:
    • direction:flexDirection({FlexDirection.Row}) 设置主轴方向
    • justifyContent:FlexAlign.SpaceAround
    • alignItems:AlignItems.Center
  • Padding:
  • flexDirection:flexDirection({FlexDirection.Column}) 设置主轴方向

多态样式

@Entry
@Component
struct StateStylesSample {
  build() {
    Column() {
      Button('Button1')
        .stateStyles({
          focused: {
            .backgroundColor('#ffffeef0')
          },
          pressed: {
            .backgroundColor('#ff707070')
          },
          normal: {
            .backgroundColor('#ff2787d9')
          }
        })
        .margin(20)
      Button('Button2')
        .stateStyles({
          focused: {
            .backgroundColor('#ffffeef0')
          },
          pressed: {
            .backgroundColor('#ff707070')
          },
          normal: {
            .backgroundColor('#ff2787d9')
          }
        })
    }.margin('30%')
  }
}

页面变量

页面变量分为三种:全局变量(文件组件外变量)、组件变量、组件状态变量 全局变量和组件变量在页面初始化的时候只渲染一次,组件状态变量在页面渲染的时候会根据状态值进行渲染

// 全局变量
const globalVar = '全局变量'

@Entry
@Component
struct Index {
  // 组件变量
  const componentVar = '组件变量'

  // 组件状态变量
  @state componentStateVar = '组件状态变量'

  build() {}
}

@state 的变量若是嵌套的太深是无法触发渲染的, 只有第一层属性赋值的变化,即Object.keys(observedObject)返回的属性才渲染

@state p:person={
  name:'张三',
  age:18,
  attr:{
    height:180,
    weight:70
  }
}

// 只修改一个属性
p.attr.height = 120  // 不会触发渲染
// 直接修改了指针方向
p.attr={
  height:120,
  weight:70
} // 会触发渲染

组件传值

组件传值

父子组件双向传值

// 父组件
@Entry
@Component
struct Index {
  @State count: string = '父组件数据'
  build() {
    Column() {
      Text('父组件')
      ChildComponent(
       childData: this.count
      )
    }
  }
}

// 子组件
@Component
struct ChildComponent {
  @Link childData: string = ''
  build() {
    Column() {
      Text('子组件')
      Text(this.childData)
    }
  }
}

@Prvide|@Consume

父子组件单向传值

// 父组件
@Entry
@Component
struct Index {
  @Provide provideData: number = 3
  build() {
    Column() {
      Text('父组件')
      ChildComponent()
    }
  }
}

// 子孙组件
@Component
struct ChildComponent {
  @Consume provideData: string = ''
  build() {
    Column() {
      Text('子组件')
      Text(this.provideData)
    }
  }
}

@Observed & @ObjectLink

ObjectLink 主要针对的是数组对象的更新,比如一个数组对象,当数组对象中的某个属性发生变化时,ObjectLink可以监听到这个变化,然后更新对象组件,但是不想更新整个对象组件,只想更新组件内关联的页面元素,这时候用ObjectLink就可以实现这个功能

页面装饰器

  • @Entry:入口组件
  • @Component:组件
  • @Builder:构建函数
  • @Extend:扩展组件
  • @Styles:样式
  • @Preview:预览组件
  • @Prop: 父子组件属性传递
  • @Link:父子组件双向传值
  • @Provide:父组件向子组件传值
  • @Consume:子组件向父组件传值
  • @State:组件状态变量
  • @ObjectLink:对象组件监听
  • @Observed:监听对象组件

样式|结构重用

@Extend

扩展组件(样式、事件)

<!-- 定义 -->
@Extend(Text)  // 继承组件类型:Text、Button、Image等
function TextFont(){
  .fontSize(20)
  .layoutWeight(1)
  .textAlign(TextAlign.Center)
}

<!-- 使用 -->
 Text('tab1')
   .TextFont()
   .fontWeight(FontWeight.Bold)
 Text('tab2')
   .TextFont()

<!-- 不仅支持样式,还支持在定义的继承传递参数渲染不同数据 -->
@Extend(Text)
function TextFont(index: number){
  .fontSize(20)
  .layoutWeight(1)
  .textAlign(TextAlign.Center)
  .onClick(()=>{
    console.log('点击了tab1'+index)
  })
}

@Styles

抽取通用属性、事件,定义的函数不可以传递参数

<!-- 全局定义 -->
@Styles CommonStyle(){
  .fontSize(20)
  .layoutWeight(1)
  .textAlign(TextAlign.Center)
}

// 直接在UI组件链式调用即可

<!-- 组件定义 -->
@Styles UIStyle(){
  .fontSize(20)
  .onClick(()=>{
   // 访问组件内的变量
   console.log(this.message)
  })
}

@Builder

自定义构建函数(结构、样式、事件)

<!-- 全局定义 -->

@Builder
function GlobalUI(msg:string){
  Row(){
    Text('tab1')
      .TextFont()
      .fontWeight(FontWeight.Bold)
    Text('tab2')
      .TextFont()
    Text('tab3')
      .TextFont()
    Text('tab4')
      .TextFont()
      .onClick(()=>{
        console.log(msg)
      })
  }
  .width('100%')
  .height(70)
  .position({
    bottom:0,
    left:0
  })
}

<!-- 使用 -->
GlobalUI('点击了tab4')

<!-- 在组件内定义 -->
@Entry
@Component
struct Index {
  @Builder
  GlobalUI(msg:string){
    // code
  }
  build() {
    RelativeContainer() {
      // 底部 tab
      GlobalUI('点击了tab4')
    }
    .height('100%')
    .backgroundColor(Color.Orange)
    .width('100%')
  }
}

@Component

自定义组件,可以直接将页面的部分内容直接封装成一个组件,在页面引入使用。也可以直接在页面内定义自定义组件,然后直接使用,不需要export或者export default导出

// 组件文件夹文件定义 /components/GlobalUI.ets
@Component
export struct GlobalUI{
 // 定义组件变量和函数
 title:string //可以参数覆盖
 @state name:string='' //可以参数覆盖
 publicMethod = ()=>{} //可以参数覆盖
 sayHi(){} //不可以参数覆盖
 build(){
  Row(){
   
  }
 }
}

// 或者直接导出单个组件
export default GlobalUI

// 然后在页面引入使用
import {GlobalUI} from '../components/GlobalUI.ets'

@Entry
@Component
struct Index {
  build() {
    GlobalUI(

     publicMethod(){}
    )
  }
}

若是要预览自定义组件,需要在@Component 上添加 @Preview

组件插槽 @Builder、@BuilderParam 可以让自定义组件 允许外部传递 UI。

@Component
export struct BuildUI{
 // 定义组件变量和函数
 title:string
 @BuilderParam ContentBuilder:()=>void=this.defaultContentBuilder //可以参数覆盖
 publicMethod = ()=>{}
 @Builder
 defaultContentBuilder(){
  Text('默认内容')
 } 
 
 build(){
  Row(){
   // 使用 @BuilderParam 装饰的成员变量
   this.ContentBuilder()
  }
 }
}

// 然后在页面引入使用
import {GlobalUI} from '../components/GlobalUI.ets'

@Entry
@Component
struct Index {
  build() {
    BuildUI(
     publicMethod(){}
    ){
     Text('插槽内容')
    }
  }
}

多个插槽

页面渲染

ForEach-渲染控制

对于重复的元素,可以使用ForEach进行渲染控制

ForEach(this.list, (item,index) => {
 Row(){
   Text(item)
     .fontSize(20)
})

页面路由

路由文档 创建路由页面的方式有两种:

  • 直接在pages文件夹下创建页面arkts文件,或者在pages文件夹下创建文件夹,然后在文件夹下创建页面arkts文件,这种方式需要手动在路由中注册。即在resources/base/proofile文件夹下的main_pages.json文件中添加路由配置。
  • 直接在pages文件夹下右键创建页面文件的时候选择New=>Page,或者在pages文件夹下创建文件夹,然后在文件夹下创建页面文件选择New=>Page,文件夹下创建的页面文件会自动在路由中注册。
// Navigation(推荐)
@Entry
@Component
struct Index {
  // 创建一个页面栈对象并传入Navigation
  pageStack: NavPathStack = new NavPathStack();
  build() {
    Navigation(this.pageStack) {
     Text('首页').onClick(()=> {
        this.pageStack.pushPath({
          url: 'pages/second_page',
          params: {
            name: 'second_page'
          }
     })
    }
    .title('Main')
  }
}
// 普通跳转
this.pageStack.pushPath({ name: "PageOne", param: "PageOne Param" });
this.pageStack.pushPathByName("PageOne", "PageOne Param");
// 带返回回调的跳转,跳转时添加onPop回调,能在页面出栈时获取返回信息,并进行处理。
this.pageStack.pushPathByName('PageOne', "PageOne Param", (popInfo) => {
  console.log('Pop page name is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result));
});
// 带错误码的跳转,跳转结束会触发异步回调,返回错误码信息。
this.pageStack.pushDestination({name: "PageOne", param: "PageOne Param"})
  .catch((error: BusinessError) => {
    console.error(`Push destination failed, error code = ${error.code}, error.message = ${error.message}.`);
  }).then(() => {
    console.info('Push destination succeed.');
  });
this.pageStack.pushDestinationByName("PageOne", "PageOne Param")
  .catch((error: BusinessError) => {
    console.error(`Push destination failed, error code = ${error.code}, error.message = ${error.message}.`);
  }).then(() => {
    console.info('Push destination succeed.');
  });
// 删除栈中name为PageOne的所有页面
this.pageStack.removeByName("PageOne");
// 删除指定索引的页面
this.pageStack.removeByIndexes([1, 3, 5]);
// 删除指定id的页面
this.pageStack.removeByNavDestinationId("1");
// 移动栈中name为PageOne的页面到栈顶
this.pageStack.moveToTop("PageOne");
// 移动栈中索引为1的页面到栈顶
this.pageStack.moveIndexToTop(1);
// 获取栈中所有页面name集合
this.pageStack.getAllPathName();
// 获取索引为1的页面参数
this.pageStack.getParamByIndex(1);
// 获取PageOne页面的参数
this.pageStack.getParamByName("PageOne");
// 获取PageOne页面的索引集合
this.pageStack.getIndexByName("PageOne");

// router (不推荐)

// 导入 两种方式都可以
import router from@ohos .router';
import {router} from "@kit.ArkUI"

//1.调用方法-普通跳转(可以返回)
router.pushUrl({
 url:页面地址
})

//2.调用方法-替换跳转(无法返回)
router.replaceUrl({
 url:'页面地址
})

//3.调用方法-(返回)
router.back()

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

页面栈跳转最长 32 个页面记录

路由模式

  • standard:标准模式,页面栈跳转,跳转的路由不管之前是否存在都在页面栈中添加
  • single:单任务模式,跳转时,如果栈中存在目标页面,则将目标页面以上的页面全部出栈,并跳转到目标页面
//1.调用方法-普通跳转(可以返回)
router.pushUrl({
 url:页面地址
 mode:'standard'
})

//2.调用方法-替换跳转(无法返回)
router.replaceUrl({
 url:'页面地址
 mode:'single'
})

获取路由参数

  • aboutToAppear:在创建自定义组件后,执行其build()函数之前执行(NavDestination创建之前),允许在该方法中改变状态变量,更改将在后续执行build()函数中生效。
  • onWillAppear:NavDestination创建后,挂载到组件树之前执行,在该方法中更改状态变量会在当前帧显示生效。
  • onAppear:通用生命周期事件,NavDestination组件挂载到组件树时执行。
  • onWillShow:NavDestination组件布局显示之前执行,此时页面不可见(应用切换到前台不会触发)。
  • onShown:NavDestination组件布局显示之后执行,此时页面已完成布局。
  • onActive:NavDestination处于激活态(处于栈顶可操作,且上层无特殊组件遮挡)触发。
  • onWillHide:NavDestination组件触发隐藏之前执行(应用切换到后台不会触发)。
  • onInactive:NavDestination组件处于非激活态(处于非栈顶不可操作,或处于栈顶时上层有特殊组件遮挡)触发。
  • onHidden:NavDestination组件触发隐藏后执行(非栈顶页面push进栈,栈顶页面pop出栈或应用切换到后台)。
  • onWillDisappear:NavDestination组件即将销毁之前执行,如果有转场动画,会在动画前触发(栈顶页面pop出栈)。
  • onDisappear:通用生命周期事件,NavDestination组件从组件树上卸载销毁时执行。
  • aboutToDisappear:自定义组件析构销毁之前执行,不允许在该方法中改变状态变量。
aboutToAppear():void{
 console.log('aboutToAppear:路由传递参数',this.pageStack.params)
}

onActive():void{
 console.log('onActive:路由传递参数',this.pageStack.params)
}

onShown():void{
 console.log('onShown:页面组件渲染完成')
}

onDisappear():void{
 console.log('onDisappear:页面销毁')
}

页面生命周期

路由拦截

在willShow回调中通过修改路由栈来实现路由拦截重定向的能力。

this.pageStack.setInterception({
  willShow: (from: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar",
    operation: NavigationOperation, animated: boolean) => {
    if (typeof to === "string") {
      console.log("target page is navigation home page.");
      return;
    }
    // 将跳转到PageTwo的路由重定向到PageOne
    let target: NavDestinationContext = to as NavDestinationContext;
    if (target.pathInfo.name === 'PageTwo') {
      target.pathStack.pop();
      target.pathStack.pushPathByName('PageOne', null);
    }
  }
})

部署

项目解构

  • entry:HarmonyOS工程模块,编译构建生成一个HAP包。
    • src>main>ets:用于存放ArkTS源码,
    • src>main>ets>entryability:应用/服务的入口,
    • src>main>ets>pages:应用/服务包含的页面。
    • src>main>resources:用于存放应用/服务所用到的资源文件。
    • src>main>module.json5:模块应用配置文件。
    • build-profile.json5:当前的模块信息、编译信息配置项,包括buildOption、targets配置等
    • hvigorfile.ts:模块级编译构建任务脚本,开发者可以自定义相关任务和代码实现。
    • obfuscation-rules.txt:混淆规则文件。。
  • oh-modules:用于存放三方库依赖信息。
  • build-profile.json5:应用级配置信息,包括签名signingConfigs、产品配置products等。。
  • hvigorfile.ts:应用级编译构建任务脚本。

[项目名称]/entry/src/main/ets/entryability/EntryAbility.ets

在当前文件内配置了应用的各种生命配置

  • onCreate:应用创建时调用
  • onDestroy:应用销毁时调用
  • onWindowStageCreate:应用窗口创建时调用
    • 在windowStage.loadContent可以设置App默认启动页
  • onWindowStageDestroy:应用窗口销毁时调用
  • onForeground:应用进入前台时调用
  • onBackground:应用进入后台时调用

AppScope/app.json5

{
  "app": {
    // 包名 不可省略
    "bundleName": "com.example.myapplication", 
    //  应用开发厂商描述 不可省略
    "vendor": "example",
    //  应用版本号
    "versionCode": 1000000,
    //  应用版本名称
    "versionName": "1.0.0",
    //  应用图标
    "icon": "$media:layered_image",
    //  应用名称
    "label": "$string:app_name"
  }
}

src/main/module.json5

配置

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet",
      "2in1"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image", //应用图标
        "label": "$string:EntryAbility_label",//应用名称
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ],
    "extensionAbilities": [
      {
        "name": "EntryBackupAbility",
        "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
        "type": "backup",
        "exported": false,
        "metadata": [
          {
            "name": "ohos.extension.backup",
            "resource": "$profile:backup_config"
          }
        ],
      }
    ]
  }
}

Ability

一个手机可以同时运行多个应用,每个应用可以包含多个Ability,每个Ability负责完成一个特定的功能。Ability是应用的基本组成单元,一个应用可以包含一个或多个Ability。支持应用内的交互(微信和微信小程序或者说是启动了微信和打开微信的任务(视频语音聊天、小程序))、应用间的交互、数据共享、远程服务调用等。 更多介绍

创建Ability

每个项目都可以由多个Ability组成,每个Ability都对应一个页面,每个页面都对应一个Ability。在module.json5中配置了Ability,可以设置APP默认启动的Ability。在ets中创建对应的Ability文件,可以设置启动的Ability的默认启动页。 例如:打开微信,可以打开微信的首页,也可以打开微信的某个内置小程序。

创建Ability

{
  "module": {
    "name": "entry",
    ....
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      },
      {
        "name": "twoAbility",
        "srcEntry": "./ets/twoability/twoAbility.ets",
        "description": "$string:twoAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:twoAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background"
      }
    ],
    "extensionAbilities": []
  }
}

在上面的 abilities 属性可以设置应用启动的默认Ability(直接将exported和skills属性剪切到另一个对象即可)。icon、label、startWindowIcon、startWindowBackground属性可以设置应用启动页面的图标、名称、启动页面的图标、启动页面的背景色。

Ability的交互

Ability 同模块的交互(即打开内置小程序)

示例

Ability 应用间交互(淘宝或者京东打开微信和支付宝支付)

示例

webview

webview相关