2021-03-04

                                                                                                                                             DevEcoStudio 个人记录备份 1        

部分转载自HarmonyOS技术社区,初学者-Study                                           

2020年9月10日,HarmonyOS 2.0的发布对于IT界来说又是一件翘首以盼的大事。发布会上,华为消费者业务软件部总裁王成录为开发者带来了一个全面升级的华为鸿蒙版本,各方面能力的大幅提升,完整的应用开发生态,超级终端的全场景生态,即将开启移动生态的下一下新纪元。

 在参加完鸿蒙2.0的发布会后,又周转于9月11日华为联合南北向业务合作伙伴以及应用生态合作伙伴共同主持的各技术分论坛活动。作为一名从事移动应用相关产业近十余年的工作者,自然是对华为鸿蒙2.0提供的完整的应用开发生态更为感兴趣。

 《从零开始开发HarmonyOS应用》是华为展示的第一个鸿蒙应用示例,示例简单的介绍了多设备自适应布局、输入事件处理、和跨设备流转。2021-03-04

1 创建应用工程


使用 DevEco Studio 创建应用工程,在菜单栏打开“File>New>New Project”,在弹出的对话框中选择java工程模板

2021-03-04

选择好模板之后点击“Next”,在下一个界面中指定“Project Name”和“Package Name”,然后点击“Finish”即可建立工程2021-03-04

一个建立好的工程包含以下目录2021-03-04

2 设计用户界面


在Java UI框架中,提供了两种编写布局的方式:在XML中声明UI布局和在代码中创建布局。这两种方式创建出的布局没有本质差别,此例主要通过XML的方式编写应用的页面

2.1 XML编写页面


2.1.1 新建XML页面

在“Project”窗口,打开“entry > src > main > resources > base”,右键点击“base”文件夹,选择“New > Directory”,命名为“layout”。2021-03-04

右键点击“layout”文件夹,选择“New > File”命名为“main_layout.xml”。2021-03-04

在“layout”文件夹下可以看到新增了“main_layout.xml”文件。2021-03-04

2.1.2 编写XML代码


这个示例中自适应布局的实现,主要是基于AdaptiveBoxLayout新的布局样式,它可以将布局在横竖屏设备上进行自适应分布排列。开发者可以将布局代码写在“main_layout.xml”文件,关于布局代码怎样实现可以参考https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-component-layout-xml-0000000000500395

如果布局中引用到图片资源,需要将图片放到 “resources>base>media”2021-03-04

2021-03-04

2.2加载XML布局

在“Project”窗口中,选择“entry > src > main > java > com.huawei.example.helloworld > slice”,打开“MainAbilitySlice.java”文件。重写onStart()方法加载XML布局,示例代码如下:

1.package com.huawei.example.helloworld.slice;
2.import com.huawei.example.helloworld.ResourceTable;
3.import ohos.aafwk.ability.AbilitySlice;
4.import ohos.aafwk.content.Intent;
5. 
6.public class MainAbilitySlice extends AbilitySlice {
7. 
8.    @Override
9.    public void onStart(Intent intent) {
10.        super.onStart(intent);
11.        super.setUIContent(ResourceTable.Layout_main_layout); // 加载XML布局
12.    }
13. 
14.    @Override
15.    public void onActive() {
16.        super.onActive();
17.    }
18. 
19.    @Override
20.    public void onForeground(Intent intent) {
21.        super.onForeground(intent);
22.    }
23.}

应用运行效果如图所示:2021-03-04

2.3基础UI组件解释

创建xml的时候默认的是DirectionalLayout,这个布局表用于单一方向排列,你可以理解为AS的线性布局,还有五种布局分别是什么呢?
AdaptiveBoxLayout(自适应盒式布局)
DependentLayout(相关布局,你可以理解为是AS的相对布局)
PositionLayout(位置布局,相当于绝对布局)
StackLayout(堆叠布局)
TableLayout(表格布局)

3 输入事件处理


输入事件包括两种,一种是触控事件,一种是按键事件。

3.1 触控事件

组件的一个重要作用是当用户点击组件时,会执行相应的操作或者界面出现相应的变化。用户点击组件时,组件对象将收到一个点击事件。开发者可以自定义响应点击事件的方法。例如,通过创建一个Component.ClickedListener对象,然后通过调用setClickedListener将其分配给组件。示例代码如下

1. //1.先导入ohos.agp.components.Image
2. //2.从定义的xml中获取Image对象
3. Image remote = (Image) findComponentById(ResourceTable.Id_remote);
4. //3.为Image设置点击事件回调
5. remote.setClickedListener(component -> {
6.     //4.此处添加点击按钮后的事件处理逻辑
7.      migrateAbility();
8. });​

3.2 按键点击事件


当设备按键按下时,相应的Ability对象将收到一个KeyEvent事件。开发者可以自定义响应点击事件的方法

一个示例代码如下

1.public boolean onKeyEvent(Component component, KeyEvent keyEvent) {
2.    if (keyEvent.isKeyDown()
3.            && keyEvent.getKeyCode() == KeyEvent.KEY_DPAD_CENTER
4.            && component.getId() == ResourceTable.Id_remote) {
5.        continueAbility();
6.        return true;
7.    }
8.    return false;
9.}

4 跨设备流转


4.1 申请权限

权限即系统用于控制第三方应用或服务访问用户敏感个人数据或操作敏感能力的授权方式,当第三方应用或服务尝试访问用户个人数据或操作敏感能力时,系统通过弹窗的形式请求用户授权。用户可授权此应用使用相关权限,后续也可取消此授权。例如打车类应用为了定位,在获取用户的地理位置信息前需向用户申请,用户同意此应用使用,后续也可取消此授权。

当使用“流转”功能需要申请迁移权限DISTRIBUTED_DATASYNC

4.1.1 在config.json中声明权限

1. "reqPermissions": [
2.  {
3.   "name": "ohos.permission.DISTRIBUTED_DATASYNC"
4.  }
5. ]

4.1.2 在MainAbilitySlice.java中请求权限

1.private void migrateAbility() {
2.    if (verifySelfPermission(SystemPermission.DISTRIBUTED_DATASYNC)
3.            == IBundleManager.PERMISSION_GRANTED) {
4.        this.continueAbility();
5.    } else {
6.        requestPermission(SystemPermission.DISTRIBUTED_DATASYNC);
7.    }
8.}

4.2 跨设备流转


仅continueAbility一行代码即可完成应用在设备之间流转,依托的还是鸿蒙OS的分布式软总线、分布式数据库的能力。在此基础上将这些能力通过接口的形式提供开发者。

事实上流转并非“一行代码可以完成”,如果开发者想在流转过程中做更多的事情,那么可以通过IAbilifyContinuation接口中的方法进行实现。

IAbilityContinuation接口方法说明如下

接口名称 说明
onStartContinuation Page请求迁移后,系统首先回调此方法,开发者可以在此回调中决策当前是否可以执行迁移,比如,弹框让用户确认是否开始迁移。
onSaveData 如果onStartContinuation()返回true,则系统回调此方法,开发者在此回调中保存必须传递到另外设备上以便恢复Page状态的数据。
onRestoreData 源侧设备上Page完成保存数据后,系统在目标侧设备上回调此方法,开发者在此回调中接受用于恢复Page状态的数据。注意,在目标侧设备上的Page会重新启动其生命周期,无论其启动模式如何配置。且系统回调此方法的时机在onStart()之前。
onCompleteContinuation 目标侧设备上恢复数据一旦完成,系统就会在源侧设备上回调Page的此方法,以便通知应用迁移流程已结束。开发者可以在此检查迁移结果是否成功,并在此处理迁移结束的动作,例如,应用可以在迁移完成后终止自身生命周期。

Ability以及AbilitySlice都需要实现IAbilityContinuation接口,并实现其中的方法。示例代码如下

1. @Override
2. public boolean onStartContinuation() {
3.     return true;
4. }
5. 
6. @Override
7. public boolean onSaveData(IntentParams intentParams) {
8.     return true;
9. }
10. 
11. @Override
12. public boolean onRestoreData(IntentParams intentParams) {
13.     return true;
14. }
15. 
16. @Override
17. public void onCompleteContinuation(int i) {
18. 
19. }​

首先来看一张图,很普通,我相信每一个人一开始新建使用Java语言的鸿蒙项目都是这样的目录结构,前面我们说到HUAWEI DevEco Studio是基于IntelliJ IDEA Community开源版本所打造出来的。那么它就会跟IDEA 和Android Studio 会有点像,那么同样的项目结构也比较像,同样的我在AS中也新建了一个项目,下面是对比图。
打开我之前创建的鸿蒙HelloWorld项目

2021-03-042021-03-04

左边的是DS,右边是AS。基本上差不太多,但是还有点点差点,下面说一下
DS的主模块是entry,AS的主模块是app,对于功能中两个build.gradle的功能是类似的,entry/app下面的build.gradle对当前模块进行控制,而工程根目录下的工程级build.gradle用于工程的全局设置。其中我们对AS中AndroidManifest.xml是很熟悉的,但是对于DS中用了另一种文件,那就是config.json,这是一个应用清单文件,用于描述应用的全局配置信息、在具体设备上的配置信息和HAP的配置信息。它的地位就相当于AS中的AndroidManifest.xml。

2021-03-04

打开这个文件来看看里面有什么内容:截图截不全,所以我就直接把里面的代码贴出来了:

1. config.json (配置文件)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

{

 "app": {

  "bundleName": "com.llw.helloworld",

  "vendor": "llw",

  "version": {

   "code": 1,

   "name": "1.0"

  },

  "apiVersion": {

   "compatible": 3,

   "target": 3

  }

 },

 "deviceConfig": {},

 "module": {

  "package": "com.llw.helloworld",

  "name": ".HelloWorld",

  "reqCapabilities": [

   "video_support"

  ],

  "deviceType": [

   "wearable"

  ],

  "distro": {

   "deliveryWithInstall": true,

   "moduleName": "entry",

   "moduleType": "entry"

  },

  "abilities": [

   {

    "skills": [

     {

      "entities": [

       "entity.system.home"

      ],

      "actions": [

       "action.system.home"

      ]

     }

    ],

    "orientation": "landscape",

    "formEnabled": false,

    "name": "com.llw.helloworld.MainAbility",

    "icon": "$media:icon",

    "description": "$string:mainability_description",

    "label": "HelloWorld",

    "type": "page",

    "launchType": "standard"

   }

  ]

 }

}

它的数据结构是JSON格式的,这和我们平时通过接口请求返回的数据比较的类,然后收缩一下,就比较清晰了。

2021-03-04

可以看到主要的分为三个部分:appdeviceConfigmodule,你可以理解为三个对象。

① app 表示应用的全局配置信息。同一个应用的不同HAP包的“app”配置必须保持一致。(简单说就是你所有模块里面的config.json中的app对象都要一模一样)

② deviceConfig 表示应用在具体设备上的配置信息。(目前的设备有TV、智能手表、运动手表)

③ module 表示HAP包的配置信息。该标签下的配置只对当前HAP包生效。(简单说就是你当前模块的配置信息)

2021-03-04

是不是觉得清晰了那么一丢丢,好继续往里面看,下面我们展开这个app对象,看一下里面可以配置哪些属性。

2021-03-04

下面也是一个个说明:

  • bundleName 表示应用的包名,用于标识应用的唯一性。采用反域名形式的字符串表示(例如,com.llw.helloworld)。建议第一级为域名后缀“com”,第二级为厂商/个人名,第三级为应用名,也可以采用多级。支持的字符串长度为7~127字节。不可省缺。
  • vendor 表示对应用开发厂商的描述。字符串长度不超过255字节。可以省缺,省缺值为空。
  • version 这是一个对象,表示应用的版本信息。它里面有两个参数,codenamecode 表示应用的版本号,仅用于HarmonyOS管理该应用,对用户不可见。取值为大于零的整数。name表示应用的版本号,用于向用户呈现。取值可以自定义。两个参数都不可省缺。
  • apiVersion 这也是一个对象,表示应用依赖的HarmonyOS的API版本。它里面也有两个参数,compatible 和 target ,compatible 表示应用运行需要的API最小版本。取值为大于零的整数。不可省缺target 表示应用运行需要的API目标版本。取值为大于零的整数。可缺省,缺省值为应用所在设备的当前API版本。

app对象就说完了,下面说deviceConfig

2021-03-04

  从图片上来看,里面是个空的,空的你说个锤子啊!冷静、冷静,现在是空的不代表以后就是空的,其实它是由参数的,这个需要看官网上的文档了。

  介绍是这样的deviceConfig包含在具体设备上的应用配置信息,可以包含default、car、tv、wearable、liteWearable、smartVision等属性。default标签内的配置是适用于所有设备通用,其他设备类型如果有特殊的需求,则需要在该设备类型的标签下进行配置。虽然它目前支持的模拟设备只有tv、wearable、liteWearable,但是具体的设备还有一些其他的。下面也来介绍一下,先来看一下里面的内容有哪些

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

"deviceConfig": {

  "default": {

   "process": "com.llw.helloworld.hiworld",

   "directLaunch": false,

   "supportBackup": false,

   "network": {

    "usesCleartext": true,

    "securityConfig": {

     "domainSettings": {

      "cleartextPermitted": true,

      "domains": [

       {

        "subDomains": true,

        "name": "example.ohos.com"

       }

      ]

     }

    }

   }

  }

 }

然后收缩一下,如下:

2021-03-04

之前提到deviceConfig中有defaultcartvwearableliteWearablesmartVision。这六个对象,但是图片上只有一个,你仿佛你在骗我!我翻译一下

2021-03-04

default代表的就是所有设备,而其他的五个,和这个default对象里面的内容时一模一样的,只是名字不一样,比如你把这个default改成car,那就是对应汽车的设备配置,改成tv就是对应电视的设备配置。比如:

2021-03-04

所以*度很高,可以根据自己的实际需求,你想怎么玩就怎么玩。下面讲一下里面具体的对象属性:

  • process 表示应用或者Ability的进程名。如果在“deviceConfig”标签下配置了“process”标签,则该应用的所有Ability都运行在这个进程中。 如果在“abilities”标签下也为某个Ability配置了“process”标签,则该Ability就运行在这个进程中。该标签仅适用于智慧屏、智能穿戴、车机。可缺省,缺省为应用的软件包名。
  • directLaunch 表示应用是否支持在设备未解锁状态直接启动。如果配置为“true”,则表示应用支持在设备未解锁状态下启动。使用场景举例:应用支持在设备未解锁情况下接听来电。可缺省,缺省为“false”。该标签仅适用于智慧屏、智能穿戴、车机。
  • supportBackup 表示应用是否支持备份和恢复。如果配置为“false”,则不支持为该应用执行备份或恢复操作。该标签仅适用于智慧屏、智能穿戴、车机。可缺省,缺省为“false”。
  • compressNativeLibs 表示libs库是否以压缩存储的方式打包到HAP包。如果配置为“false”,则libs库以不压缩的方式存储,HAP包在安装时无需解压libs,运行时会直接从HAP内加载libs库。该标签仅适用于智慧屏、智能穿戴、车机。可缺省,缺省为“true”。
  • network 表示网络安全性配置。该标签允许应用通过配置文件的安全声明来自定义其网络安全,无需修改应用代码。

先看看network里面有什么

2021-03-04

  • usesCleartext 表示是否允许应用使用明文网络流量(例如,明文HTTP)。true:允许应用使用明文流量的请求。false:拒绝应用使用明文流量的请求。可缺省,缺省为“false”。
  • securityConfig 表示应用的网络安全配置信息。可缺省,缺省为空。

然后再展开securityConfig,如下

2021-03-04

  • domainSettings 表示自定义的网域范围的安全配置,支持多层嵌套,即一个domainSettings对象中允许嵌套更小网域范围的domainSettings对象。可缺省,缺省为空。它里面有cleartextPermitted属性和domains对象。
  • cleartextPermitted表示自定义的网域范围内是否允许明文流量传输。当useCleartextsecurityConfig同时存在时,自定义网域是否允许明文流量传输以cleartextPermitted的取值为准。true:允许明文流量传输。false:拒绝明文流量传输。不可省缺。
  • domains 表示域名配置信息,包含两个参数:subDomainsnamesubDomains(布尔类型):表示是否包含子域名。如果为 “true”,此网域规则将与相应网域及所有子网域(包括子网域的子网域)匹配。否则,该规则仅适用于精确匹配项。
  • name(字符串):表示域名名称。不可省缺。

说是说deviceConfig里面没有东西,但是这么一顿操作下来,我人都傻了。好了,清醒一点,还有一个module没说呢?

下面来看module对象的内部结构。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

"module": {

  "package": "com.llw.helloworld",

  "name": ".HelloWorld",

  "reqCapabilities": [

   "video_support"

  ],

  "deviceType": [

   "wearable"

  ],

  "distro": {

   "deliveryWithInstall": true,

   "moduleName": "entry",

   "moduleType": "entry"

  },

  "abilities": [

   {

    "skills": [

     {

      "entities": [

       "entity.system.home"

      ],

      "actions": [

       "action.system.home"

      ]

     }

    ],

    "orientation": "landscape",

    "formEnabled": false,

    "name": "com.llw.helloworld.MainAbility",

    "icon": "$media:icon",

    "description": "$string:mainability_description",

    "label": "HelloWorld",

    "type": "page",

    "launchType": "standard"

   }

  ]

 }

妈耶,这个module里面的东西比appdeviceConfig两个对象里面加起来还要多。(555555~ 我不想学鸿蒙了,我放弃了,现在下车还来得及吗?别想了,车门我已经焊死了,雅蠛蝶!!!)
收缩一下,这么一看好像内容也不是很多,对吧!画外音:对个屁!

2021-03-04

  • package 表示HAP的包结构名称,在应用内应保证唯一性。采用反向域名格式(建议与HAP的工程目录保持一致)。字符串长度不超过127字节。该标签仅适用于智慧屏、智能穿戴、车机。不可省缺。
  • name 表示HAP的类名。采用反向域名方式表示,前缀需要与同级的package标签指定的包名一致,也可采用“.”开头的命名方式。字符串长度不超过255字节。该标签仅适用于智慧屏、智能穿戴、车机。不可省缺。
  • reqCapabilities 表示需要的技术支持,这是一个字符串数组,里面可以看到有一个video_support,表示视频支持。
  • deviceType 表示允许Ability运行的设备类型。系统预定义的设备类型包括:tv(智慧屏)、car(车机)、wearable(智能穿戴)、liteWearable(轻量级智能穿戴)等。不可省缺。

2021-03-04

distro 表示HAP发布的具体描述。这是一个对象,该标签仅适用于智慧屏、智能穿戴、车机。不可省缺。它里面还有

  • deliveryWithInstall 表示当前HAP是否支持随应用安装。true:支持随应用安装。false:不支持随应用安装。不可省缺。
  • moduleName 表示当前HAP的名称。不可省缺。
  • moduleType 表示当前HAP的类型,包括两种类型:entry和feature。不可省缺。

abilities 表示当前模块内的所有Ability。采用对象数组格式,其中每个元素表示一个Ability对象。可缺省,缺省值为空。下面来看看里面有哪些属性

2021-03-04

可以看到这个里面有一个对象数组skills,其余就是就是一些配置的属性了。skills 里面还有两个字符串数组entitiesactiions,下面先从这个skills 来分析。

skills 打过游戏的都知道,这是技能的意思。不过在这里是表示Ability能够接收的Intent的特征。 这个Intent的用法其实和Android里面是比较像的,比如我们跳转到手机的短信页面就可以通过Intent,还有打电话也可以通过Intent,类似这样的特征。可缺省,缺省值为空。从上图我们知道里面还有两个字符串对象。

  • actions 表示能够接收的Intent的action值,可以包含一个或多个action。取值通常为系统预定义的action值,可缺省,缺省值为空。
  • entities 表示能够接收的Intent的Ability的类别(如视频、桌面应用等),可以包含一个或多个entity。取值通常为系统预定义的类别,可缺省,缺省值为空。

看到这里你会很奇怪,因为你知道Android里面除了有Intent帮我们实现之外还有Uri可以实现,那么上面的截图中也没有这个属性,是为什么?答案就是这个属性属于开发者自行去做配置。这里就补充一下这个属性,它其实一个对象数组。它就是uris

uris 表示能够接收的Intent的uri,可以包含一个或者多个uri。可缺省,缺省值为空。它里面的数据如下:

1

2

3

4

5

6

7

8

9

"uris": [

      {

         "scheme": "http",

         "host": "www.hefeng.com",

         "port": "8080",

         "path": "query/goodweather/today",

         "type": "text"

       }

     ]

  里面的对象属性值就是字面意思,都是可以省缺的,省缺值为空。你可以根据不同的Uri配置不同的对象,都放到这个对象数组里面就可以了。下面看abilities中的其他属性。

2021-03-04

orientation 表示该Ability的显示模式。该标签仅适用于page类型的Ability。取值范围如下:unspecified:由系统自动判断显示方向。landscape:横屏模式。portrait:竖屏模式。followRecent:跟随栈中最近的应用。可缺省,缺省值为“unspecified”。

formEnabled 表示FA类型的Ability是否提供卡片(form)能力。该标签仅适用于page类型的Ability。true:提供卡片能力。false:不提供卡片能力。可缺省,缺省值为“false”。

name 表示Ability名称。取值可采用反向域名方式表示,由包名和类名组成,如“com.llw.helloworld.MainAbility”;也可采用“.”开头的类名方式表示,如“.MainAbility”。该标签仅适用于智慧屏、智能穿戴、车机。不可省缺。

icon 表示Ability图标资源文件的索引。取值示例:$media:ability_icon。如果在该Ability的“skills”属性中,“actions”的取值包含 “action.system.home”,“entities”取值中包含“entity.system.home”,则该Ability的icon将同时作为应用的icon。如果存在多个符合条件的Ability,则取位置靠前的Ability的icon作为应用的icon。可缺省,缺省值为空。

description 表示对Ability的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。可缺省,缺省值为空。

label 表示Ability对用户显示的名称。取值可以是Ability名称,也可以是对该名称的资源索引,以支持多语言。如果在该Ability的“skills”属性中,“actions”的取值包含 “action.system.home”,“entities”取值中包含“entity.system.home”,则该Ability的label将同时作为应用的label。如果存在多个符合条件的Ability,则取位置靠前的Ability的label作为应用的label。可缺省,缺省值为空。

type 表示Ability的类型。取值范围如下:page:表示基于Page模板开发的FA,用于提供与用户交互的能力。service:表示基于Service模板开发的PA,用于提供后台运行任务的能力。data:表示基于Data模板开发的PA,用于对外部提供统一的数据访问抽象。不可省缺。

launchType 表示Ability的启动模式,支持“standard”和“singleton”两种模式:standard:表示该Ability可以有多实例。“standard”模式适用于大多数应用场景。singleton:表示该Ability只可以有一个实例。例如,具有全局唯一性的呼叫来电界面即采用“singleton”模式。该标签仅适用于智慧屏、智能穿戴、车机。可缺省,缺省值为“standard”。

至此,这个config.json的基本信息我们就都知道了,不得不说看起来比AS 的AndroidManifest.xml要复杂一些,看得我是头皮发麻。可能用的熟悉了就好了。你说呢?

2. entry (应用的主模块)

应用的主模块,类似与AS的app模块,一个APP中,对于同一设备类型必须有且只有一个entry类型的HAP,可独立安装运行。它里面的内容如下:

2021-03-04

从图片上来看是和AS的app里面的内容差不多,那就来看看不同的地方在哪里。

首先当然是资源文件夹resources,其实和ASres是差不多的,只不过DS这里用了全拼。(AS指Android Studio,DS 指DevEco Studio),展开resources如下:

2021-03-04

可以看到有两个文件夹,

baserawfile,首先从单词的字面意思来看是基础和原始文件。官方的意思是 base目录和 限定词目录。按照两级目录形式来组织,目录命名必须符合规范,以便根据设备状态去匹配相应目录下的资源文件。听起来是不是云里雾里,我都不知道你在说啥,下面我说一下我的个人理解,你可以把这个文件夹理解为AS的values文件下,我们之前一些样式、颜色、尺寸、文字都是放在里面的,基本上一个values就够了,但是如果要做国际化或者多语言的话,我们会新建一个values-en的文件夹,这个里面的的资源文件就是切换语言之后使用的。那么你就可以把这个限定词目录理解为values-en。当然我也不知道对不对啊,先这么理解再说吧。限定词目录可以由一个或多个表征应用场景或设备特征的限定词组合而成,包括语言、文字、国家或地区、横竖屏、设备类型和屏幕密度等六个维度,限定词之间通过下划线(_)或者中划线(-)连接。开发者在创建限定词目录时,需要掌握限定词目录的命名要求以及与限定词目录与设备状态的匹配规则。当然这个限定词还是有命名规范的。

限定词目录的命名要求

限定词的组合顺序:语言_文字_国家或地区-横竖屏-设备类型-屏幕密度。开发者可以根据应用的使用场景和设备特征,选择其中的一类或几类限定词组成目录名称。

限定词的连接方式:语言、文字、国家或地区之间采用下划线(_)连接,除此之外的其他限定词之间均采用中划线(-)连接。例如:zh_Hant_CNzh_CN-car-ldpi

限定词的取值范围:每类限定词的取值必须符合表2中的条件,否则,将无法匹配目录中的资源文件。

 表2 限定词取值要求

限定词类型

含义与取值说明

语言

表示设备使用的语言类型,由2个小写字母组成。例如:zh表示中文,en表示英语。

详细取值范围,参见ISO 639-1(ISO制定的语言编码标准)。

文字

表示设备使用的文字类型,由1个大写字母(首字母)和3个小写字母组成。例如:Hans表示简体中文,Hant表示繁体中文。

详细取值范围,参见ISO 15924(ISO制定的文字编码标准)。

国家或地区

表示用户所在的国家或地区,由2~3个大写字母或者3个数字组成。例如:CN表示中国,GB表示英国。

详细取值范围,参见ISO 3166-1(ISO制定的国家和地区编码标准)。

横竖屏

表示设备的屏幕方向,取值如下:

  • vertical:竖屏
  • horizontal:横屏

设备类型

表示设备的类型,取值如下:

  • car:车机
  • tv:智慧屏
  • wearable:智能穿戴

屏幕密度

表示设备的屏幕密度(单位为dpi),取值如下:

  • sdpi:表示小规模的屏幕密度(Small-scale Dots Per Inch),适用于120dpi及以下的设备。
  • mdpi:表示中规模的屏幕密度(Medium-scale Dots Per Inch),适用于120dpi~160dpi的设备。
  • ldpi:表示大规模的屏幕密度(Large-scale Dots Per Inch),适用于160dpi~240dpi的设备。
  • xldpi:表示特大规模的屏幕密度(Extra Large-scale Dots Per Inch),适用于240dpi~320dpi的设备。
  • xxldpi:表示超大规模的屏幕密度(Extra Extra Large-scale Dots Per Inch),适用于320dpi~480dpi的设备。
  • xxxldpi:表示超特大规模的屏幕密度(Extra Extra Extra Large-scale Dots Per Inch),适用于480dpi~640dpi的设备。

资源组目录

base目录与限定词目录下面可以创建资源组目录(包括element、media、animation、layout、graphic、profile),用于存放特定类型的资源文件

表3 资源组目录 说明

资源组目录

目录说明

资源文件

element

表示元素资源,以下每一类数据都采用相应的JSON文件来表征。

  • boolean,布尔型
  • color,颜色
  • float,浮点型
  • intarray,整型数组
  • integer,整型
  • pattern,样式
  • plural,复数形式
  • strarray,字符串数组
  • string,字符串

element目录中的文件名称建议与下面的文件名保持一致。每个文件中只能包含同一类型的数据。

  • boolean.json
  • color.json
  • float.json
  • intarray.json
  • integer.json
  • pattern.json
  • plural.json
  • strarray.json
  • string.json

media

表示媒体资源,包括图片、音频、视频等非文本格式的文件。

文件名可自定义,例如:icon.png。

animation

表示动画资源,采用XML文件格式。

文件名可自定义,例如:zoom_in.xml。

layout

表示布局资源,采用XML文件格式。

文件名可自定义,例如:home_layout.xml。

graphic

表示可绘制资源,采用XML文件格式。

文件名可自定义,例如:notifications_dark.xml。

profile

表示其他类型文件,以原始文件形式保存。

文件名可自定义。

rawfile 这个文件是干嘛的呢?支持创建多层子目录,目录名称可以自定义,文件夹内可以*放置各类资源文件。rawfile目录的文件不会根据设备状态去匹配不同的资源。这个里面就是一个综合体,相当于包含了AS的drawable和mipmap以及其他的一些文件,只不过它没有做尺寸上的细分,需要开发者自行细分做不同设备的适配,不过既然是华为里面的东西,我相信到时候会有一个系统的关于这方面的讲解的。

系统资源文件

系统资源文件说明

系统资源名称

含义

类型

ic_app

表示HarmonyOS应用的默认图标。

媒体

request_location_reminder_title

表示“请求使用设备定位功能”的提示标题。

字符串

request_location_reminder_content

表示“请求使用设备定位功能”的提示内容,即:请在下拉快捷栏打开"位置信息"开关。

字符串

二、编写页面

右键点击“base”文件夹,选择“New > Directory”,命名为“layout”。

2021-03-04

2021-03-04

点击OK,然后右键点击“layout”文件夹,选择“New >Layout Resources File”,命名为“main_layout.xml”。

2021-03-04

2021-03-04

单击Finish,完成布局创建。
布局如下:

1

2

3

4

5

6

7

<?xml version="1.0" encoding="utf-8"?>

<DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"

          ohos:width="match_parent"

          ohos:height="match_parent"

          ohos:orientation="vertical">

   

</DirectionalLayout>

这都是什么鬼东西,我咋看不懂呢?你可能会问ohos是个啥?我就这么告诉你,你把它当成AS中布局文件中的android就可以了。ohos我估计是鸿蒙系统对于布局的一个属性标识。除了这个,还有DirectionalLayout是什么布局呢?这位朋友你的问题咋这么多呢?嗯?

DirectionalLayout是方向布局,闻所未闻?那么还有没有其他的一些布局呢?当然有,如下图所示:

2021-03-04

我们创建xml的时候默认的是DirectionalLayout,这个布局表用于单一方向排列,你可以理解为AS的线性布局,还有五种布局分别是什么呢?AdaptiveBoxLayout(自适应盒式布局)、DependentLayout(相关布局,你可以理解为是AS的相对布局)、PositionLayout(位置布局,相当于绝对布局)、StackLayout(堆叠布局)、TableLayout(表格布局)。

好了,上面看你装逼是装完了,会用吗?这个问题问得好,不是全会!哎,先别动手,放下手中的刀,作为刚学的哪能都会啊,而且我现在只是简单的讲解一下,到时候具体的使用肯定也是要大费周章才能讲完,这个解释怎么样?讲真的,你信我,只要我都会使用之后,我绝对会另写一篇文章来单独讲这个布局,好不好?回到刚才的布局页面。修改一下后如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

<?xml version="1.0" encoding="utf-8"?>

<DependentLayout

    xmlns:ohos="http://schemas.huawei.com/res/ohos"

    ohos:width="match_parent"

    ohos:height="match_parent"

    ohos:background_element="#000000">

  <Text

      ohos:id="$+id:text"

      ohos:width="match_content"

      ohos:height="match_content"

      ohos:center_in_parent="true"

      ohos:text="Hello World"

      ohos:text_color="white"

      ohos:text_size="32fp"/>

  <Button

      ohos:id="$+id:button"

      ohos:width="match_content"

      ohos:height="match_content"

      ohos:text_size="19fp"

      ohos:text="Next"

      ohos:top_padding="8vp"

      ohos:bottom_padding="8vp"

      ohos:right_padding="80vp"

      ohos:left_padding="80vp"

      ohos:text_color="white"

      ohos:background_element="$graphic:button_element"

      ohos:center_in_parent="true"

      ohos:align_parent_bottom="true"/>

</DependentLayout>

可以看到,我把DirectionalLayout改成DependentLayout之后再里面放了一个Text和一个Button,
首先来看我们不熟悉的属性值:

1

2

ohos:width="match_content"

ohos:height="match_content"

宽高为match_content,表示组件大小与它的内容占据的大小范围相适应,简单说,就是自适应大小,类似于AS的wrap_content,这么一说就好理解了。

1

ohos:center_in_parent="true"

这个从字面意思看是在父布局内居中的意思。

1

ohos:text_size="32fp"

文字大小使用fp,尺寸大小使用vp

1

ohos:background_element="$graphic:button_element"

背景,里面的值通过引用“button_element”来显示的,你可能会奇怪,为什么要加一个element,单独用background不行吗?你别忘了,开发语言是支持JS的,所以你知道为啥有一个element了吗?
可以看到它引用graphic下面的button_element,没有就来创建一个,需要在“base”目录下创建“graphic”文件夹,在“graphic”文件夹中新建一个“button_element.xml”文件。
代码如下:

1

2

3

4

5

6

7

<?xml version="1.0" encoding="utf-8"?>

<shape

    xmlns:ohos="http://schemas.huawei.com/res/ohos"

    ohos:shape="oval">

  <solid

      ohos:color="#007DFF"/>

</shape>

那么布局就已经写好了。下面打开MainAbilitySlice.java

2021-03-04

里面的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

package com.llw.helloworld.slice;

import ohos.aafwk.ability.AbilitySlice;

import ohos.aafwk.content.Intent;

import ohos.agp.components.DirectionalLayout;

import ohos.agp.components.DirectionalLayout.LayoutConfig;

import ohos.agp.components.Text;

import ohos.agp.colors.RgbColor;

import ohos.agp.components.element.ShapeElement;

import ohos.agp.utils.Color;

import ohos.agp.utils.TextAlignment;

public class MainAbilitySlice extends AbilitySlice {

  private DirectionalLayout myLayout = new DirectionalLayout(this);

  @Override

  public void onStart(Intent intent) {

    super.onStart(intent);

    LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);

    myLayout.setLayoutConfig(config);

    ShapeElement element = new ShapeElement();

    element.setRgbColor(new RgbColor(255, 255, 255));

    myLayout.setBackground(element);

    Text text = new Text(this);

    text.setLayoutConfig(config);

    text.setText("Hello World");

    text.setTextColor(new Color(0xFF000000));

    text.setTextSize(50);

    text.setTextAlignment(TextAlignment.CENTER);

    myLayout.addComponent(text);

    super.setUIContent(myLayout);

  }

  @Override

  public void onActive() {

    super.onActive();

  }

  @Override

  public void onForeground(Intent intent) {

    super.onForeground(intent);

  }

}

只要看里面的onStart方法里面的代码,因为我们一开始运行项目就会有一个Hello World!但是之前里面没有布局啊,所以默认的项目是在代码中创建布局的,

2021-03-04

下面来看看这个代码的意思,注释已经写在里面了

2021-03-04

下面清理掉onStart中的方法,增加

1

super.setUIContent(ResourceTable.Layout_main_layout); // 加载XML布局

2021-03-04

添加我们刚才创建的布局。然后运行到远程模拟器上面,这个确实也比较烦就是需要登录才能行。运行出来的效果如下:

2021-03-04

三、页面跳转

APP只有一个页面可不行啊,下面来新建一个页面。

1. 创建Feature Ability

右键点击“com.example.myapplication”文件夹,选择“New > Ability > Empty Feature Ability(Java)”。

2021-03-04

改一下Ability的名字

2021-03-04

点击finish,然后你打开这个SecondAbilitySlice.java之后你会发现里面的代码也帮你生成好了,也是Hello World。首先把它的待删掉。

2021-03-04

删除之后,如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package com.llw.helloworld.slice;

import ohos.aafwk.ability.AbilitySlice;

import ohos.aafwk.content.Intent;

public class SecondAbilitySlice extends AbilitySlice {

  @Override

  protected void onStart(Intent intent) {

    super.onStart(intent);

  }

  @Override

  protected void onActive() {

    super.onActive();

  }

  @Override

  protected void onForeground(Intent intent) {

    super.onForeground(intent);

  }

}

添加代码后如下所示:每一行添加的代码都有注释:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

package com.llw.helloworld.slice;

import ohos.aafwk.ability.AbilitySlice;

import ohos.aafwk.content.Intent;

import ohos.agp.colors.RgbColor;

import ohos.agp.components.DependentLayout;

import ohos.agp.components.Text;

import ohos.agp.components.element.ShapeElement;

import ohos.agp.utils.Color;

import java.math.MathContext;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_PARENT;

public class SecondAbilitySlice extends AbilitySlice {

  @Override

  protected void onStart(Intent intent) {

    super.onStart(intent);

    /*  使用代码设置布局  */

    //声明一个相关布局

    DependentLayout myLayout = new DependentLayout(this);

    //设置布局的大小 宽高占满屏幕

    myLayout.setWidth(MATCH_PARENT);

    myLayout.setHeight(MATCH_PARENT);

    //创建一个形状元素对象

    ShapeElement element = new ShapeElement();

    //设置颜色

    element.setRgbColor(new RgbColor(0,0,0));

    //设置布局的背景

    myLayout.setBackground(element);

    //创建一个文本组件

    Text text = new Text(this);

    //设置文本组件显示的内容

    text.setText("How are you?");

    //设置宽度占满父布局

    text.setWidth(MATCH_PARENT);

    //设置文字大小

    text.setTextSize(55);

    //设置文字颜色

    text.setTextColor(Color.WHITE);

    //设置文本的布局 宽高自适应

    DependentLayout.LayoutConfig textConfig = new DependentLayout.LayoutConfig(MATCH_CONTENT,MATCH_CONTENT);

    //添加显示规则 显示在布局的*

    textConfig.addRule(DependentLayout.LayoutConfig.CENTER_IN_PARENT);

    //给Text组件设置布局配置

    text.setLayoutConfig(textConfig);

    //然后把Text组件添加到布局中

    myLayout.addComponent(text);

    //最后加载布局

    super.setUIContent(myLayout);

  }

  @Override

  protected void onActive() {

    super.onActive();

  }

  @Override

  protected void onForeground(Intent intent) {

    super.onForeground(intent);

  }

}

2. 跳转逻辑

打开第一个页面的“MainAbilitySlice.java”文件,重写onStart()方法添加按钮的响应逻辑,实现点击按钮跳转到下一页。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

package com.llw.helloworld.slice;

import com.llw.helloworld.ResourceTable;

import ohos.aafwk.ability.AbilitySlice;

import ohos.aafwk.content.Intent;

import ohos.aafwk.content.Operation;

import ohos.agp.components.Button;

import ohos.agp.components.Component;

import ohos.agp.components.DirectionalLayout;

import ohos.agp.components.DirectionalLayout.LayoutConfig;

import ohos.agp.components.Text;

import ohos.agp.colors.RgbColor;

import ohos.agp.components.element.ShapeElement;

import ohos.agp.utils.Color;

import ohos.agp.utils.TextAlignment;

/** @author llw

 * @noinspection All */

public class MainAbilitySlice extends AbilitySlice {

  @Override

  public void onStart(Intent intent) {

    super.onStart(intent);

    // 加载XML布局

    super.setUIContent(ResourceTable.Layout_main_layout);

    //这一步就类似与findViewById 初始化组件

    Button button = (Button) findComponentById(ResourceTable.Id_button);

    if(button != null){

      //设置点击监听

      button.setClickedListener(new Component.ClickedListener() {

        @Override

        public void onClick(Component component) {

          // 还是通过Intent来跳转

          Intent secondIntent = new Intent();

          // 指定待启动FA的bundleName和abilityName

          Operation operation = new Intent.OperationBuilder()

              // 设备id

              .withDeviceId("")

              // 应用的包名 怎么跳转个页面搞得这么麻烦呢?

              .withBundleName("com.llw.helloworld")

              // 跳转目标的路径名 通常是包名+类名 或者 . + 类名

              .withAbilityName("com.llw.helloworld.SecondAbility")

//              .withAbilityName(".SecondAbility")

              .build();

          // 设置操作方式

          secondIntent.setOperation(operation);

          // 通过AbilitySlice的startAbility接口实现启动另一个页面

          startAbility(secondIntent);

        }

      });

    }

  }

  @Override

  public void onActive() {

    super.onActive();

  }

  @Override

  public void onForeground(Intent intent) {

    super.onForeground(intent);

  }

}

运行效果如下:用手机拍的,莫要见怪呀。

上一篇:harmonyOS:关于Page Ability生命周期详解✨一文搞懂✨


下一篇:使用Data Ability读取系统设置项