鸿蒙App开发实战
一、概述
在2021年6月2日,华为发布了鸿蒙系统--HarmonyOS。它是一款面向未来、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统。
在传统的单设备系统能力的基础上,HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备。
简而言之:HarmonyOS是一款面向物联网的系统。搞过智能家居的朋友应该知道,联网真是一个大问题,具体细节,我就不多说了,谁用谁知道。如果现在用安装了HarmonyOS的路由器,用安装HarmonyOS的手机和智慧屏,那分布式的意义就被激出来了,yyds。
如果你是硬件工程师和单片机程序员,你可以实现将操作系统安装在硬件设备上,然后实现设备操作,还有跟App的交互;对于应用工程师而言,你可以开发App并安装在手机、平板或手表上。我怕弄硬件设备时,会被电到,所以选择开发App的方向。
如果你错过了iOS App的淘金时代,错过Android App的外包时机,想在鸿蒙App中,能赚到买一份盒饭的钱,那你就该马上关注我,好恰饭。
那什么人学鸿蒙App开发有优势?--懂Java的人和懂前端的人,还有我这种既懂Java又懂前端的人,哈哈...,不要打我,要关注我。
HarmonyOS支持的开发语言包括Java、XML、C/C++ 、 JS、CSS和HML(HarmonyOS Markup Language),其中UI ,用Java和JS实现。
好了,今天就说这么多,下节就正式进入App开发阶段。
二、创建项目
学一门编程语言,废话再多,都不如动手实践来得简单。走,去https://developer.harmonyos.com/cn/develop/deveco-studiodownload下载属于你操作系统的开发工具。工具安装太简单了,简单到我都不想说了。
打开HUAWEI DevEco Studio开发工具,用过jetbrains全家桶中(Java、Python、PHP或C等)开发工具中一个或多个的程序员,都会倍感亲切,因为它是基于IntelliJ IDEA Community的开源版本打造而来的。
好了,好了,知道你等不急,准备大干一场了。那就话不多说,直接进入主题吧。
1. 创建项目
DevEco Studio升级之后,创建项目的窗口如下,单击【Create HarmonyOS Project】,进入下一个窗口。
选择第2个:Empty Ability(Java),接着用你的神力按下右下角的【Next】按钮。
项目配置信息窗口,要填写的内容如下:
Project Name:项目名称,暂叫Booking,假装你是一个很爱读书的家伙;
Project Type: 项目类型,Service--免安装的原子化服务,Application--应用程序,选后面这个。
Package Name:包名。一般就是网址的域名+项目名称,这个用于区分不同的App,我随便写了一个,你也可以随便写一个;
Sava Location:项目保存目录,我用的是一台用好几个脸盆换来的苹果电脑,所以你看不到C:这样的,你用Windows的话,选择放在D:盘的某个文件夹下就好了,开发步骤和工具都是一个样的,只是这种目录略有不同而已;
Compaible API Version:编译API的版本,选择SDK:API Version 5;
Device Type:程序支持的设备,不要贪多,选择支持手机Phone先,等以后有机会了,再扩展到平板:Tablet,智慧屏(如门禁人脸识别屏):TV,穿戴设备(如手表):Wearable和汽车:Car;
Show in Service Center:是否在服务中心露出。如果Project Type为Service,则会同步创建一个2*2的服务卡片模板,同时还会创建入口卡片;如果Project Type为Application,则只会创建一个2*2的服务卡片模板。
按下【Finish】按钮之后,一个叫Booking的鸿蒙App项目就创建好了。
2. 安装HarmonyOS SDK
找到【Tools】菜单栏并点击它,选择【SDK Manager】,在弹出的窗口中,选择你想安装的SDK版本。找到HarmonyOS SDK,勾选第1或第2项,我勾选了SDK(API Version5),点击【Apply】之后,就会开始下载并安装自动安装的。如果已经安装的,跳过这一步。
安装不会太久,稍等一下就好了。如果着急的话,我告诉你一个方法,那就是在心中默念:马上就好了,马上就好了。
3. 安装模拟器
若想运行鸿蒙App,一是用安装了HarmonyOS系统的手机运行,二是用模拟器运行。考虑到好多朋友还没赚到买HarmonyOS手机的钱,所有选择用第2种方式。
找到【Tools】菜单栏并点击它,选择【Device Manager】,在弹出的窗口中,要求你进行登录Login,这是因为模拟器目前只能支持远程调试(本地的还在开发当中),你登录就是了。如果没有登录账号,自己去https://developer.harmonyos.com/cn/注册一下就好了。
Remote Emulator每次使用时长为1小时,到期后会自动释放资源,当然,你可以重新申请。这就是要登录的原因了。希望快点发布可以下载到本地的模拟器Local Emulator。
成功登录之后,会显示出可用的虚拟机,列表如下。单击手机P40所在行的运行图标(最后面那个箭头图标),就会启动手机的虚拟机。
4. 运行App
在开发工具的主窗口中,你可以看到鸿蒙手机P40的虚拟机。眼光往虚拟机上方瞟一瞟,有没有见到:【entry】【HUAWEI ANA-AN00】后的的箭头没?--单击它,运行鸿蒙App。运行可能会有点慢,需要稍等片刻。
右下角有:【Deploying HAP......】告诉你在努力发布中。
等了7749秒,终于运行成功了,效果如下:
你好,世界
三、项目文件详解
在进行鸿蒙App功能开发之前,你应该对HarmonyOS App的逻辑结构,有所了解。鸿蒙App要发布的程序,需要打包成HAP(HarmonyOS Ability Package)格式。一个App由代码、资源、第三方库及应用清单文件组成,项目结构如下图2.1所示。
图 2.1 项目结构
gradle:Gradle配置文件,由开发工具自动生成,一般情况下不需要进行修改;
entry:默认启动模块(主模块),程序员用于编写源码文件以及开发资源文件的目录:
entry>libs:用于存放entry模块的依赖文件,如Java库文件(.jar);
entry>src>main>Java:用于存放Java源码,这是程序员开发功能的地方;
entry>src>main>resources:用于存放应用所用到的资源文件,如图形、多媒体、字符串、布局文件等,这是程序员配置资源的地方,它包括两大类目录,一类为base目录与限定词目录,另一类为rawfile目录。
base 目录中的资源文件会被编译成二进制文件,并赋予资源文件ID,如存放字符串的文件string.json;rawfile目录中的资源文件会被直接打包进应用,不经过编译,也不会被赋予资源文件ID,如js文件。
base下资源组目录element、media、animation、layout、graphic和profile的作用,如图2.2所示:
图2.2 资源文件解析
entry>src>main>config.json:HAP清单文件;
entry>src>test:编写代码单元测试代码的目录,运行在本地Java虚拟机上;
entry>.gitignore:标识git版本管理需要忽略的文件;
entry>build.gradle:entry模块的编译配置文件。
项目文件内容的解析就这么多,你不用背记下来,大概知道每个目录和文件的作用是什么就好了。这些文件会在开发的过程中,慢慢熟悉的,不用着急。
其实,鸿蒙App的整体内容,并没有那么复杂,开发应用代码主要围绕Ability组件展开。你在项目中,看到MainAbility,就是Ability来的。
为了方便开发和维护,将页面Ability的资源放到resources中,再用自动生成的代码进行引用,是一个不错的分层解决方案,这也是存在resources原因,明白了么?
四、Ability介绍
Ability是鸿蒙App的重要组成部分,分为Page Ability、Service Ability和Data Ability三类,它们的区别如下:
Page Ability:构造和用户交互的窗口,像你用今日头条App,在看我这篇文章的当前屏幕,就是一个窗口,在窗口中,你可以做一些交互动作,如转发、点赞或留言。如图3.1,显示"你好,世界"的也是一个窗口。
图3.1 App窗口
Service Ability:用于提供后台运行任务的能力,就像你打开音乐App,选一首歌播放之后,继续选其他歌曲,此时播放的歌曲就是运行在后台,用Service实现的功能;
Data Ability:用于对外部提供统一的数据访问抽象,就是那种专门操作数据用的接口。
为了更好理解,特将Page Ability叫成Feature(要脸的) Ability,简称FA;而将Service Ability和Data Ability归类成Particle(不要脸的) Ability,简称PA。
肉眼,一望,就可以看得出谁要脸谁不要脸;鸿蒙App,在config.json中一配,就可以看得出Ability是要脸的还是不要脸的。
在配置文件(config.json)中注册Ability时,可以通过配置Ability元素中的type属性来指定Ability模板类型。其中,type的取值可以为page、service或data,分别代表Page模板、Service模板和Data模板。
"abilities": [
{"skills": [
{"entities": ["entity.system.home"],"actions": ["action.system.home"]
}
],"orientation":"unspecified","name":"com.lc.hm.booking.MainAbility","icon":"$media:icon","description":"$string:mainability_description","label":"$string:entry_MainAbility","type":"page","launchType":"standard"}
]
五、Ability和AbilitySlice区别
在Booking项目中,有一个MainAbility.java文件,它就是要脸的Ability。一个要脸的Ability由多个业务能力高度相关性的AbilitySlice构成。如新闻浏览功能,包含两个AbilitySlice:一个AbilitySlice用于展示新闻列表,另一个AbilitySlice用于展示新闻详情。它们就是总和分的设计模式,关系如下图4.1所示:
图4.1 Page与AbilitySlice
虽然一个Ability可以包含多个AbilitySlice,但是Ability进入前台时界面默认只展示一个AbilitySlice。Ability用setMainRoute()方法启动主AbilitySlice的代码如下:
packagecom.lc.hm.booking;importcom.lc.hm.booking.slice.MainAbilitySlice;importohos.aafwk.ability.Ability;importohos.aafwk.content.Intent;publicclassMainAbilityextendsAbility{@OverridepublicvoidonStart(Intent intent){super.onStart(intent);//路由(打开)到主AbilitySlicesuper.setMainRoute(MainAbilitySlice.class.getName());
}
}
顺着路由,找到slice下的MainAbilitySlice.java文件,打开之后,代码如下所示,其中核心的代码是setUIContent()这行加载布局文件的代码,将这行代码复制出来并粘贴到MainAbility.java文件中。
super.setUIContent(ResourceTable.Layout_ability_main);
注释MainAbility.java文件中的setMainRoute()所在行的代码,加上从MainAbilitySlice.java文件复制过来的代码,结果如下:
packagecom.lc.hm.booking;importcom.lc.hm.booking.slice.MainAbilitySlice;importohos.aafwk.ability.Ability;importohos.aafwk.content.Intent;publicclassMainAbilityextendsAbility{@OverridepublicvoidonStart(Intent intent){super.onStart(intent);//路由(打开)到主AbilitySlice//super.setMainRoute(MainAbilitySlice.class.getName());super.setUIContent(ResourceTable.Layout_ability_main);
}
}
运行项目之后,你会发现效果和之前的是一模一样的。
由此说明AbilitySlice是可选的,只是在下面两种情况时,用AbilitySlice更优:
①页面有多种布局,需要对页面进行动态布局,每种布局可以对应一个AbilitySlice;
②页面有多个Tab选项卡,需要在多个Tab之间切换,每个Tab可以对应一个。
而AbilitySlice用到的两种情况,在开发App时是非常常见的,这就是鸿蒙App引入AbilitySlice的原因。用AbilitySlice,在同一个窗口中切换页面,用Ability,弹出新窗口。
六、Ability页面,创建和加载布局
要脸Ability的开发,占了鸿蒙App开发的大部分工作,所以你需要多花点时间研究,研究。再次打开MainAbility.java文件,左手按住键盘上的Ctrl键,右手将鼠标移到Layout_ability_main上,鼠标单击之后,会打开一个ability_main.xml文件。
super.setUIContent(ResourceTable.Layout_ability_main);
从ability_main.xml文件在layout目录下,你应该能明白,它是一个专门用于布局的文件,其中DirectionalLayout为垂直方向的线性布局管理器,Text为文本控件,关于它们的详细介绍,后面都会说到,此刻,你大概了解一下就好。
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="center"ohos:orientation="vertical"><Textohos:id="$+id:text_helloworld"ohos:height="match_content"ohos:width="match_content"ohos:background_element="$graphic:background_ability_main"ohos:layout_alignment="horizontal_center"ohos:text="$string:mainability_HelloWorld"ohos:text_size="40vp"/>DirectionalLayout>
鸿蒙实现页面布局,有两种方法:一种是用上面的xml文件进行布局,接着在Java类中用setUIContent()加载,红线ResourceTable.Layout_ability_main由开发工具自动生成;还有一种方法就是通过Java代码实现。
在MainAbility.java类中用代码实现布局如下所示,为了不让你感觉到单调,我特意将文字改成了"你好,鸿蒙"。
packagecom.lc.hm.booking;importohos.aafwk.ability.Ability;importohos.aafwk.content.Intent;importohos.agp.components.DirectionalLayout;importohos.agp.components.Text;importohos.agp.utils.LayoutAlignment;importohos.agp.components.DependentLayout.LayoutConfig;publicclassMainAbilityextendsAbility{@OverridepublicvoidonStart(Intent intent){super.onStart(intent);
DirectionalLayout myLayout =newDirectionalLayout(this);// 设置布局宽高myLayout.setWidth(LayoutConfig.MATCH_PARENT);
myLayout.setHeight(LayoutConfig.MATCH_PARENT);
myLayout.setAlignment(LayoutAlignment.CENTER);// 创建一个文本Text text =newText(this);
text.setText("你好,鸿蒙");
text.setWidth(LayoutConfig.MATCH_PARENT);
text.setTextSize(100);// 设置文本的布局DirectionalLayout.LayoutConfig textConfig =newDirectionalLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT);
text.setLayoutConfig(textConfig);
myLayout.addComponent(text);super.setUIContent(myLayout);
}
}
运行代码之后,效果如下,和用xml布局是一样的。
虽然可以用Java代码实现布局,但大家都不这样用,除了工作量大,还是不好维护和扩展的问题,所以建议你开发鸿蒙App,也用xml布局的方式。
七、Ability页面,如何获取资源
鸿蒙App的资源,指的是resources目录下的字符串、图片和音频等。资源获取,说的是在Java类或布局文件XML中对字符串、图片等的引用。
resources目录包括两大类目录,一类为base目录与限定词目录,另一类为rawfile目录。base目录,通过指定资源类型(type)和资源名称(name)来引用;rawfile目录中的资源文件,通过指定文件路径和文件名称来引用。
打开base/element下的string.json,你可以看到字符串是用键值对的方式进行定义的,代码如下所示:
{"string": [
{"name":"entry_MainAbility","value":"entry_MainAbility"},
{"name":"mainability_description","value":"Java_Empty Ability"},
{"name":"mainability_HelloWorld","value":"Hello World"}
]
}
在string.json文件中,添加一项内容,如下:
{"name":"say","value":"学鸿蒙App开发的人最帅"}
重复代码就不粘贴了,string.json完整内容如图6.1所示。
图6.1 string.json
在你按下保存代码的一刻,开发工具会自动将"say"内容添加到ResourceTable类中,格式为ResourceTable.type_name,其中type为资源类型,如字符串:String;name为资源名称,如"say"。
1. 在Java类中,通过ResourceManager类获取ResourceTable.String_say的value:"学鸿蒙App开发的人最帅",代码(MainAbility.java文件中)示例如下:
packagecom.lc.hm.booking;importohos.aafwk.ability.Ability;importohos.aafwk.content.Intent;importohos.global.resource.ResourceManager;publicclassMainAbilityextendsAbility{@OverridepublicvoidonStart(Intent intent){super.onStart(intent);try{
ResourceManager resManager =this.getResourceManager();
String result = resManager
.getElement(ResourceTable.String_say).getString();
System.out.println(result);
}catch(Exception e) {
System.out.println("资源找不到");
}
}
}
运行代码之后,会在输出窗口,看到如下内容:
I/System.out: 学鸿蒙App开发的人最帅
2. 在布局文件xml中,引用资源文件的格式:$type:name,其中,type为类型,如string(注意:首字母小写),name为名称,如"say",打开布局文件,调整如下:
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="center"ohos:orientation="vertical"><Textohos:height="match_content"ohos:width="match_content"ohos:text="$string:say"ohos:text_size="30vp"/>DirectionalLayout>
打开MainAbility.java文件,内容调整如下:
packagecom.lc.hm.booking;importohos.aafwk.ability.Ability;importohos.aafwk.content.Intent;publicclassMainAbilityextendsAbility{@OverridepublicvoidonStart(Intent intent){super.onStart(intent);super.setUIContent(ResourceTable.Layout_ability_main);
}
}
运行虚拟机之后,你会在手机窗口中,看到如下效果:
在java代码或xml布局文件中调用base的资源,用法是一样的,只是类型(如String、Media等)不同。
对了,在json文件中,value的值可以是字符串、数字、布尔和数组类型。
另外,如果你想使用原生的资源,那就将其(如demo.js文件)放在rawfile目录下并在java代码中进行调用,用法如下所示:
ResourceManager resManager =this.getResourceManager();
RawFileEntry rawFileEntry = resManager
.getRawFileEntry("resources/rawfile/demo.js");
八、页面国际化
鸿蒙系统,迟早会发布到全球的每个国家的,学鸿蒙App开发的你,赶紧掌握App实现国际化的功能,然后静等花开。
在鸿蒙系统启动时,会有一种默认语言(如简体中文),在打开App时,系统会自动匹配资源文件,如果系统是简体中文,会匹配zh.element目录(存放简体中文)里的资源;如果是英文则会匹配en.element目录(存放英文)里的资源。
Java或xml文件,在中英文两个目录都找不到引用的资源时,会引用默认目录element里的资源,如果连这里都没有,App就会报错。
确定你的MainAbility.java文件内容是如下的内容:
packagecom.lc.hm.booking;importohos.aafwk.ability.Ability;importohos.aafwk.content.Intent;publicclassMainAbilityextendsAbility{@OverridepublicvoidonStart(Intent intent){super.onStart(intent);super.setUIContent(ResourceTable.Layout_ability_main);
}
}
确定layout里的ability_main.xml文件的内容如下:
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="center"ohos:orientation="vertical"><Textohos:height="match_content"ohos:width="match_content"ohos:text="$string:say"ohos:text_size="30vp"/>DirectionalLayout>
打开zh.element里的string.json文件,添加如下内容:
,
{"name":"say","value":"鸿蒙App,学习中..."}
打开en.element里的string.json文件,添加如下内容:
,
{"name":"say","value":"HarmonyOS App,studying."}
运行项目,默认打开App的窗口如下:
图7.1 中文资源
单击中间前面两个图标的任意一个,滑到Home,找到【设置】图标并单击它,找到【系统和更新】--【语言和输入法】--【语言和地区】--【添加语言】直接搜索English,单击English所在行。选择【更改】。
再次回到Home,找到你开发的App(最后一个图标就是),单击打开它,你就会看到英文的内容了。你明白了吗?明白的话,赶紧关注我,不明白的话,赶紧关注我。
以上就是资源的国际化实现,除此之外,还有用Java代码实现的日期格式化,示例如下:
StringlanguageTag ="zh";Stringout = DateFormatUtil.format("EEEEdMMMMy", languageTag,"Asia/Shanghai",0,3600*1000);
电话号码国际化:不同的区域的电话号码有不同的格式化效果,当需要展示本地电话号码时,应遵循当地电话号码的格式化原则,示例代码如下:
Locale.Builder builder =newLocale.Builder();
builder.setLanguage("zh");
builder.setRegion("CN");
builder.setScript("Hant");
Locale locale = builder.build();StringdisplayName = PhoneNumberAttribution
.getAttribute("+8615611xxxxxx","CN", locale);
度量衡格式化:提供了对度量衡国际化能力的支持,可支持度量衡体系和维度之间的转换,与不同国家度量衡体系的自动转换,示例代码如下:
Locale enUS = Locale.US;
MeasureFormatter mes = MeasureFormatter.getInstance(enUS);
mes.format(MeasureOptions.Unit.VOLUME_US_CUP,1000,
MeasureOptions.Unit.VOLUME_SI_LITER,
MeasureOptions.FormatStyle.WIDE));
用Java代码实现的国际化,不用记,也不用背,它们平时用得不多,需要时,再去查询就好了。至于表示设备使用的语言类型(目录名),由2~3个小写字母组成。zh表示简体中文,en表示英语,mai表示迈蒂利语,其他的,你可以查阅ISO 639(ISO制定的语言编码标准)。
九、Ability和AbilitySlice的生命周期
用户操作等行为均会引起页面实例在其生命周期的不同状态之间进行转换,Ability类提供的回调机制能够让其感知外界的变化,从而执行不同的方法。生命周期的不同状态转换及其对应的回调,如图8.1所示。
图 8.1 生命周期
说白点,就是窗口(页面),在启动时,切换时,隐藏时,关闭时会执行对应的回调方法,方便程序员进行相关的处理。如关闭窗口时,进行资源释放。
onStart()方法
首次创建页面实例时,触发该回调方法,在其生命周期过程中仅触发一次。一般在此执行Ability的初始化工作,如加载布局模板。
@OverridepublicvoidonStart(Intent intent){super.onStart(intent);super.setUIContent(ResourceTable.Layout_ability_main);
}
onActive()方法
当Ability可见且获得用户焦点能交互时系统会调用这个方法,在这可处理在onStart()之后的一些补充工作。
@OverridepublicvoidonActive(){
}
onInactive()方法
当Ability失去焦点时,系统将调用此回调,此后进入INACTIVE状态。程序员可以在此回调中实现页面在失去焦点时需要进行处理的业务,如保存临时数据。
@OverridepublicvoidonInactive(){
}
onBackground()方法
在Ability不再对用户可见时,系统将调用此回调,如用户跳转到其他窗口时,会隐藏这个Ability。程序员可以在此方法中释放Ability不可见时无用的资源,或在此回调中执行较为耗时的状态保存操作。
@OverridepublicvoidonBackground(){
}
onForeground()方法
处于BACKGROUND状态的Ability仍然驻留在内存中,当重新回到前台时,如用户导航到此Ability时,将会回调这个onForeground()。
@OverridepublicvoidonForeground(Intent intent){
}
onStop()
系统将要销毁Ability时,将会触发此回调函数,通知用户进行系统资源的释放。
@OverridepublicvoidonStop(){
}
以上方法告诉你,人到什么时候,就该做什么事,鸿蒙出来了,该学鸿蒙App开发时就学。否则将错失良机。
AbilitySlice作为Ability的组成单元,其生命周期是依托于其所属Ability生命周期的。AbilitySlice和Page具有相同的生命周期状态和同名的回调,当Ability生命周期发生变化时,它的AbilitySlice也会发生相同的生命周期变化,它与Ability的相应回调类似,因此不再赘述。
回调方法,不是每一个都会被用到的,不用特意背记。在项目实战时,你就能清楚地知道用到哪一些了。
十、XML创建布局
XML声明布局的方式更加简便直观,是开发App的核心内容之一,咱们完全有必要搞清楚。每一个Component和ComponentContainer对象大部分属性都支持在XML中进行设置,它们有各自的XML属性,也有共同的属性。
这一节说一下组件共同的属性,至于有"个性"的内容,在介绍具体组件时,再详细讲解。所有组件都有的属性,如下:
- ID
ohos:id="$+id:text"
组件唯一编号,可用于区分不同的组件。尤其在DependentLayout布局中,组件之间需要描述相对位置关系,描述时要通过ID来指定对应组件。
在Java类中,是通过组件ID,查找组件的,如果ID名相同,会返回第一个组件,因此你要保证ID的唯一性,避免出现与预期不符合的问题。
2.布局参数
为必选属性,值可为数字,也可为match_parent等。
ohos:width="20vp"ohos:height="10vp"
具体的数值:10(以像素为单位)、10vp(以屏幕相对像素为单位)。
match_parent:表示组件大小将扩展为父组件允许的最大值,它将占据父组件方向上的剩余大小;
match_content:表示组件大小与它的内容占据的大小范围相适应。
ohos:min_width="10vp"ohos:min_height="8vp"
min_width用于调整组件的最小宽度,min_height用于调整组件的最高度。
3.前景背景
值可为图片,也可以为颜色值。
ohos:background_element="$media:bg"ohos:foreground_element="FFFFFF"
找一张背景图,放到media目录下,并在ability_main.xml文件中进行引用,运行虚拟机之后,效果如下:
4.边距
外边距:清除周围的(外边框)元素区域,没有背景颜色,是完全透明的。
ohos:margin="10vp"
margin 可以一次性改变所有上下左右的外边距。如果要单独设置某个外边距,可用如下中的一个或多个。
ohos:top_margin="10vp"ohos:bottom_margin="10vp"ohos:left_margin="10vp"ohos:right_margin="10vp"
内边距:用于在任何定义的边界内的元素内容周围生成空间。
ohos:padding="8vp"
padding可以一次性改变所有上下左右的内边距。如果要单独设置某个内边距,可用如下中的一个或多个。
ohos:top_padding="8vp"ohos:bottom_padding="8vp"ohos:left_padding="8vp"ohos:right_padding="8vp"
外边距和内边距的示意图如下所示:
十一、App线性布局
DirectionalLayout是Java UI中的一种重要组件布局,用于将一组组件(Component)按照水平或者垂直方向排布,能够方便地对齐布局内的组件,布局之间可以互相嵌套使用。
DirectionalLayout使用orientation设置布局内组件的排列方式,值为horizontal时,表示水平方向布局;为vertical时,表示表示垂直方向布局。
ohos:orientation="horizontal"ohos:orientation="vertical"
除了orientation属性之外,还有一个用于对齐布局内组件的alignment属性,它的可选值如下表所示:
取值 | 取值说明 | 使用案例 |
left | 左对齐 | 可以设置取值项如表中所列,也可以使用|进行多项组合。 ohos:alignment="top|left" ohos:alignment="left" |
top | 顶部对齐 | |
right | 右对齐 | |
bottom | 底部对齐 | |
horizontal_center | 水平居中对齐 | |
vertical_center | 垂直居中对齐 | |
center | 居中对齐 | |
start | 靠起始端对齐 | |
end | 靠结束端对齐 |
在一个垂直vertical方向的DirectionalLayout里嵌套两个水平horizontal方向的DirectionalLayout,两个水平方向布局管理器里都有一张图标和文字说明,为了打扮得更好看,我用Component实现了分割线,代码如下:
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="vertical_center"ohos:background_element="$media:bg"ohos:orientation="vertical"><Componentohos:height="1vp"ohos:width="match_content"ohos:alignment="left|center"ohos:background_element="FFFFFF"/><DirectionalLayoutohos:height="match_content"ohos:width="match_content"ohos:alignment="left|center"ohos:bottom_margin="20vp"ohos:left_margin="10vp"ohos:orientation="horizontal"ohos:top_margin="20vp"><Imageohos:height="match_content"ohos:width="match_content"ohos:image_src="$media:sm"/><Textohos:height="match_content"ohos:width="match_content"ohos:left_padding="5vp"ohos:text="扫码"ohos:text_color="FFFFFF"ohos:text_size="25vp"/>DirectionalLayout><Componentohos:height="1vp"ohos:width="match_content"ohos:alignment="left|center"ohos:background_element="FFFFFF"/><DirectionalLayoutohos:height="match_content"ohos:width="match_content"ohos:alignment="left|center"ohos:bottom_margin="20vp"ohos:left_margin="10vp"ohos:orientation="horizontal"ohos:top_margin="20vp"><Imageohos:height="match_content"ohos:width="match_content"ohos:image_src="$media:pz"/><Textohos:height="match_content"ohos:width="match_content"ohos:left_padding="5vp"ohos:text="拍照"ohos:text_color="FFFFFF"ohos:text_size="25vp"/>DirectionalLayout><Componentohos:height="1vp"ohos:width="match_content"ohos:alignment="left|center"ohos:background_element="FFFFFF"/>DirectionalLayout>
运行鸿蒙P40虚拟机之后,效果如下所示:
图10.1 垂直和水平布局
DirectionalLayout里的组件的对齐方式,在ohos:alignment的指定下是保持一致的,如果想要改变个别组件的对齐方式,你可以在组件里,用layout_alignment属性进行调整,它的可选值,如下表所示:
取值 | 取值说明 | 使用案例 |
left | 左对齐 | 可以设置取值项如表中所列,也可以使用|进行多项组合。 ohos:layout_alignment="top" ohos:layout_alignment="top|left" |
top | 顶部对齐 | |
right | 右对齐 | |
bottom | 底部对齐 | |
horizontal_center | 水平居中对齐 | |
vertical_center | 垂直居中对齐 | |
center | 居中对齐 |
DirectionalLayout中的组件使用layout_alignment控制自身在布局中的对齐方式,当对齐方式与排列方式方向一致时,对齐方式不会生效,如设置了水平方向的排列方式,则左对齐、右对齐将不会生效。
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="vertical_center"ohos:background_element="$media:bg"><Textohos:height="match_content"ohos:width="match_content"ohos:layout_alignment="left"ohos:text="程序猿"ohos:text_color="FFFFFF"ohos:text_size="20vp"/><Textohos:height="match_content"ohos:width="match_content"ohos:layout_alignment="horizontal_center"ohos:text="程序媛"ohos:text_color="FFFFFF"ohos:text_size="20vp"/><Textohos:height="match_content"ohos:width="match_content"ohos:layout_alignment="right"ohos:text="都是敲代码的"ohos:text_color="FFFFFF"ohos:text_size="20vp"/>DirectionalLayout>
运行效果如下所示:
图10.2 layout_alignment
好了,到这,你可以先歇口气,关注我之后,再接着往下看,下面还有更重要的内容,那就是组件的权重比。在DirectionalLayout中,用total_weight指定所有子视图的权重之和,在子组件(如Text)中,用weight指定比重。
total_weight的值如下表所示:
取值 | 取值说明 | 使用案例 |
float类型 | 可以直接设置浮点数值,也可以引用float浮点数资源。 | ohos:total_weight="2.5" ohos:total_weight="$float:total_weight" |
weight的值如下所示:
取值 | 取值说明 | 使用案例 |
float类型 | 可以直接设置浮点数值, 也可以引用float浮点数资源。 | ohos:weight="1" ohos:weight="$float:weight" |
仔细观察如下代码,你会发现并没有total_weight属性存在,因为它是可选的,而不是必须的;另外,在组件里用weight属性指定比重时,需要将width置为0。
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:alignment="vertical_center"ohos:background_element="$media:bg"ohos:orientation="horizontal"><Textohos:height="50vp"ohos:width="0vp"ohos:background_element="000fff"ohos:text="学鸿蒙"ohos:text_alignment="center"ohos:text_size="20vp"ohos:weight="1"/><Textohos:height="50vp"ohos:width="0vp"ohos:background_element="ff00ff"ohos:text="App的人"ohos:text_alignment="center"ohos:text_size="20vp"ohos:weight="1"/><Textohos:height="50vp"ohos:width="0vp"ohos:text="最酷......"ohos:text_alignment="center"ohos:background_element="ffff00"ohos:text_size="20vp"ohos:weight="1"/>DirectionalLayout>
在P40虚拟机运行的效果如下所示:
十二、相对布局相对布局DependentLayout,也是比较常用的一个布局管理器,在它里面,组件的排列方式是相对于其他同级组件或者父组件的位置进行布局。
- 相对同级组件
组件B神,想躲在组件A卡的后边时,在B神中用end_of属性指定A卡的身份id即可。你将end_of理解成追尾就好,也就是说,如果想让B在A的尾部,可在组件B神中,用end_of属性指定组件A的id名称,代码如下:
<DependentLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:background_element="$media:bg"><Textohos:id="$+id:a"ohos:height="match_content"ohos:width="match_content"ohos:background_element="EC9DAA"ohos:margin="10vp"ohos:padding="10vp"ohos:text="我是A卡"ohos:text_size="18fp"/><Textohos:id="$+id:b"ohos:end_of="$id:a"ohos:height="match_content"ohos:width="match_content"ohos:background_element="EC9DFF"ohos:margin="10vp"ohos:padding="10vp"ohos:text="我是B神,躲在A后边"ohos:text_size="18fp"/>DependentLayout>
运行虚拟机之后,效果如下图所示:
有在尾部的end_of,就有在前面的start_of,还有在左边的left_of等等,更多用于指定相对位置的属性,如下表所示:
属性名称 | 中文描述 | 取值 | 取值说明 | 使用案例 |
left_of | 将右边缘与另一个子组件的左边缘对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:left_of="$id:component_id" |
right_of | 将左边缘与另一个子组件的右边缘对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:right_of="$id:component_id" |
start_of | 将结束边与另一个子组件的起始边对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:start_of="$id:component_id" |
end_of | 将起始边与另一个子组件的结束边对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:end_of="$id:component_id" |
above | 将下边缘与另一个子组件的上边缘对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id | ohos:above="$id:component_id" |
below | 将上边缘与另一个子组件的下边缘对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id | ohos:below="$id:component_id" |
align_baseline | 将子组件的基线与另一个子组件的基线对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id | ohos:align_baseline="$id:component_id" |
align_left | 将左边缘与另一个子组件的左边缘对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:align_left="$id:component_id" |
align_top | 将上边缘与另一个子组件的上边缘对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id | ohos:align_top="$id:component_id" |
align_right | 将右边缘与另一个子组件的右边缘对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:align_right="$id:component_id" |
align_bottom | 将底边与另一个子组件的底边对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id | ohos:align_bottom="$id:component_id" |
align_start | 将起始边与另一个子组件的起始边对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:align_start="$id:component_id" |
align_end | 将结束边与另一个子组件的结束边对齐 | 引用 | 仅可引用DependentLayout中包含的其他组件的id。 说明
| ohos:align_end="$id:component_id" |
align_parent_left | 将左边缘与父组件的左边缘对齐 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 说明
| ohos:align_parent_left="true" ohos:align_parent_left="$boolean:true" |
align_parent_top | 将上边缘与父组件的上边缘对齐 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 | ohos:align_parent_top="true" ohos:align_parent_top="$boolean:true" |
align_parent_right | 将右边缘与父组件的右边缘对齐 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 说明
| ohos:align_parent_right="true" ohos:align_parent_right="$boolean:true" |
align_parent_bottom | 将底边与父组件的底边对齐 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 | ohos:align_parent_bottom="true" ohos:align_parent_bottom="$boolean:true" |
align_parent_start | 将起始边与父组件的起始边对齐 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 说明
| ohos:align_parent_start="true" ohos:align_parent_start="$boolean:true" |
align_parent_end | 将结束边与父组件的结束边对齐 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 说明
| ohos:align_parent_end="true" ohos:align_parent_end="$boolean:true" |
center_in_parent | 将子组件保持在父组件的中心 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 | ohos:center_in_parent="true" ohos:center_in_parent="$boolean:true" |
horizontal_center | 将子组件保持在父组件水平方向的中心 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 | ohos:horizontal_center="true" ohos:horizontal_center="$boolean:true" |
vertical_center | 将子组件保持在父组件垂直方向的中心 | boolean类型 | 可以直接设置true/false,也可以引用boolean资源。 | ohos:vertical_center="true" ohos:vertical_center="$boolean:true" |
2. 相对父级组件
内部组件相对于外部组件的位置,如内部组件A在外部组件B的左上角,将align_parent_top设置为True即可。组件的位置布局可以进行组合,形成处于左上角、左下角、右上角、右下角的布局。
代码示例如下所示:
<DependentLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:background_element="$media:bg"><Textohos:id="$+id:b"ohos:height="match_content"ohos:width="match_content"ohos:align_parent_top="true"ohos:background_element="EC9DFF"ohos:margin="10vp"ohos:padding="10vp"ohos:text="我是程序猿,我在左上角飘扬"ohos:text_size="18fp"/>DependentLayout>
运行虚拟机之后的效果如下图所示:
十三、表格布局
在开发鸿蒙App时,大多数情况下,用线性布局DirectionalLayout和相对布局DependentLayout就可以搞定的。这两种布局之前讲过了,下面就介绍一下另外的4种布局。
在需要对组团组件进行布局时,可以用表格布局TableLayout和盒子布局AdaptiveBoxLayout进行排版,至于绝对布局PositionLayout和层叠布局StackLayout,平时很少会用到。下面就一起来了解一下这4种新的布局。
表格布局
表格布局TableLayout按行和列的方式进行布局,这个在布局多个组件时,会用到,它的相关属性如下表所示:
属性名称 | 中文描述 | 取值 | 取值说明 | 使用案例 |
alignment_type | 对齐方式 | align_edges | 表示TableLayout内的组件按边界对齐。 | ohos:alignment_type="align_edges" |
align_contents | 表示TableLayout内的组件按边距对齐。 | ohos:alignment_type="align_contents" | ||
column_count | 列数 | integer类型 | 可以直接设置整型数值,也可以引用integer资源。 | ohos:column_count="3" ohos:column_count="$integer:count" |
row_count | 行数 | integer类型 | 可以直接设置整型数值,也可以引用integer资源。 | ohos:row_count="2" ohos:row_count="$integer:count" |
orientation | 排列方向 | horizontal | 表示水平方向布局。 | ohos:orientation="horizontal" |
vertical | 表示垂直方向布局。 | ohos:orientation="vertical" |
示例代码如下所示:
<TableLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:background_element="e8ebf4"ohos:column_count="2"ohos:layout_alignment="horizontal_center"ohos:padding="8vp"ohos:row_count="2"><Textohos:height="60vp"ohos:width="60vp"ohos:margin="8vp"ohos:text="酷的"ohos:text_alignment="center"ohos:text_color="4dd3a0"ohos:text_size="30fp"/><Textohos:height="60vp"ohos:width="60vp"ohos:margin="8vp"ohos:text="美的"ohos:text_alignment="center"ohos:text_color="fe6486"ohos:text_size="30fp"/><Textohos:height="60vp"ohos:width="60vp"ohos:margin="8vp"ohos:text="帅的"ohos:text_alignment="center"ohos:text_color="0099fb"ohos:text_size="30fp"/><Textohos:height="60vp"ohos:width="200vp"ohos:margin="8vp"ohos:text="都是关注我的"ohos:text_alignment="center"ohos:text_color="fc7754"ohos:text_size="30fp"/>TableLayout>
虚拟机运行的效果如下所示:
十四、盒子布局
盒子布局AdaptiveBoxLayout是自适应盒子布局,该布局提供了在不同屏幕尺寸设备上的自适应布局能力,主要用于相同级别的多个组件需要在不同屏幕尺寸设备上自动调整列数的场景。常用于多个组件一起横向或纵向布局。
- 该布局中的每个子组件都用一个单独的盒子装起来,子组件设置的布局参数都是以盒子作为父布局生效,不以整个自适应布局为生效范围。
- 该布局中每个盒子的宽度固定为布局总宽度除以自适应得到的列数,高度为match_content,每一行中的所有盒子按高度最高的进行对齐。
- 该布局水平方向是自动分块,因此水平方向不支持match_content,布局水平宽度仅支持match_parent或固定宽度。
- 自适应仅在水平方向进行了自动分块,纵向没有做限制,因此如果某个子组件的高设置为match_parent类型,可能导致后续行无法显示。
代码示例如下:
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_parent"ohos:width="match_parent"ohos:background_element="f9b958"ohos:orientation="vertical"><AdaptiveBoxLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:id="$+id:adaptive_box_layout"ohos:height="0vp"ohos:width="match_parent"ohos:weight="1"><Textohos:height="40vp"ohos:width="match_parent"ohos:background_element="69ddac"ohos:margin="10vp"ohos:padding="10vp"ohos:text="鸿蒙App"ohos:text_size="18fp"/><Textohos:height="40vp"ohos:width="match_parent"ohos:background_element="0a8bf0"ohos:margin="10vp"ohos:padding="10vp"ohos:text="Android App"ohos:text_size="18fp"/><Textohos:height="40vp"ohos:width="match_parent"ohos:background_element="8345fe"ohos:margin="10vp"ohos:padding="10vp"ohos:text="iOS App"ohos:text_size="18fp"/>AdaptiveBoxLayout><Textohos:height="40vp"ohos:width="match_parent"ohos:background_element="fc7756"ohos:margin="10vp"ohos:padding="10vp"ohos:text="有你的关注,离用专栏发布就近了"ohos:text_size="18fp"/>DirectionalLayout>
在虚拟机下运行的效果,美美的
盒子布局在开发中,有时会用到,你熟悉一下即可。
十五、绝对和层叠布局
绝对布局PositionLayout中,子组件通过指定准确的x/y坐标值在屏幕上显示。(0, 0)为左上角;当向下或向右移动时,坐标值变大;允许组件之间互相重叠。这个布局,在开发中很少用到,知道有这个布局存在即可。
层叠布局
层叠布局StackLayout直接在屏幕上开辟出一块空白的区域,添加到这个布局中的视图都是以层叠的方式显示,而它会把这些视图默认放到这块区域的左上角,第一个添加到布局中的视图显示在最底层,最后一个被放在最顶层。上一层的视图会覆盖下一层的视图。
它偶尔会用在效果切换上,我之前开发过不少App,印象当中也是没用到这个布局管理器。
这两个布局,平时很少用到,只要知道有它们的存在就好了
我告诉你msdn版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!