2.6 ArkUI实现一次开发多端部署 原创 精华
本节通过栅格化布局、自适应布局、响应式布局和使用资源,从App的弹性布局和多态组件两个维度,讲解如何实现一次开发多端部署。接着,建立一个ArkUI eTS的开发框架,这个可以作为开发新App的脚手架。
当显示环境发生变化时(如,不同屏幕尺寸的设备切换、横竖屏切换、应用分屏),我们需要及时调整内容的布局方式以适应变化。通过栅格化布局、自适应布局和响应式布局,可以达到多设备下布局的一致性。
2.6.1 栅格化布局
1. 8vp网格系统
综合权衡各类设备的屏幕特征,基于 8vp 为网格的基本单位可以对界面上元素的大小、位置、对齐方式进行更好的规划,构建更有层次感、秩序感,以及多设备上一致的布局效果。一些更小的控件(例如图标)大小也可以对齐 4vp 的网格大小。栅格化布局中的Margin和Gutter的值参考8vp/4vp网格系统设置。如,手机设备上的栅格化设计,基础栅格Margin为24vp,等于3个8vp的宽度;卡片栅格Margin为12vp,等于一个8vp加上一个4vp的宽度。
2.计算栅格的宽度
栅格的宽度在不同设备、不同环境下是动态计算的,并非固定值。与栅格宽度计算相关的三个关键参数:
Margin:栅格布局内容区距离屏幕左、右边缘的间隔。
Gutter:相邻栅格之间的间隔。
栅格数:设备屏幕宽度内可容纳的栅格数量。不同屏幕宽度下的栅格数量有一个约定。
以竖屏手机的栅格宽度计算为例:
屏幕宽度:360vp
Margin: 24vp
Gutter: 24vp
栅格数: 4个(含3个Gutter)
计算公式如下:
1个栅格的宽度 = (屏幕宽度 - Margin x 2 - Gutter x 3)/4 = (360 - 24 x 2 - 24 x 3)/4 = 60(vp)
2个栅格的宽度 = 1个栅格的宽度 + Gutter + 1个栅格的宽度 = 60 + 24 + 60 = 144(vp)
3个栅格的宽度 = 2个栅格的宽度 + Gutter + 1个栅格的宽度 = 144 + 24 + 60 = 228(vp)
4个栅格的宽度 = 3个栅格的宽度 + Gutter + 1个栅格的宽度 = 228 + 24 + 60 = 312(vp)
各类设备的栅格数、Margin、Gutter的参数,如下表所示:
参数项 | 手机 | 折叠屏 | 平板 | 车机 | 智慧屏 |
---|---|---|---|---|---|
竖屏栅格数(个) | 4 | 8 | 8 | - | - |
横屏栅格数(个) | 8 | 8 | 12 | 12 | 12 |
基础栅格Margin(vp) | 24 | 24 | 24 | 24 | 48 |
基础栅格Gutter(vp) | 24 | 24 | 24 | 24 | 24 |
卡片栅格Margin(vp) | 12 | 12 | 12 | 12 | 48 |
卡片栅格Gutter(vp) | 12 | 12 | 12 | 12 | 24 |
3. 栅格布局组件
栅格化布局组件,主要使用GridContainer布局组件、Gird布局组件和GirdItem组件,本节暂不讲解这三个组件的用法,《第4章 布局组件》中会有详细讲解。
4. 栅格断点布局
根据设备实际的屏幕宽度范围,约定栅格的数量,称为栅格断点布局。屏幕宽度小于520vp,栅格数为4;屏幕宽度大于等于520vp,且小于840vp,栅格数为8;屏幕宽度大于等于840vp,栅格数为12。
注意:由于栅格的宽度是基于设备屏幕宽度,根据公式计算而来,在不同设备上宽度存在一定的差异。为了消除这个差异导致的布局效果变差,建议栅格内的子组件排版使用自适应布局的技巧。
2.6.2 自适应布局
1. 自适应拉伸
自适应拉伸是通过设置组件与父级容器的相对比例来实现的。比如,在设计稿上,竖屏手机的宽度是“360vp”,折叠屏的宽度是“600vp”。那么,在布局组件的宽度设置上,不要使用固定值“360vp”或“600vp”,而是用“100%”这种相对值。示例代码如下:
在上述代码中,根容器“Column”的宽度和高度要占满整个手机屏幕,使用“100%”这个相对值实现。在根容器内部放了一个水平布局容器“Row”,相对于根容器“Column”的宽度也是“100%”。同时,因为根容器“Column”设置了屏幕内边距"padding",其中左边距和右边距都是“24vp”,所以,实际上“Row”容器的宽度在竖屏手机的值为“312vp”(360 - 24 -24 = 312),而在折叠屏的实际宽度为“552vp”(600 -24 -24 = 552)。效果如下图所示:
注意:这个设置也能同时适配“平板/车机/智慧屏/智能穿戴”等设备,并且切换为横屏时也是适配的。
接下来,演示自适应拉伸布局下设置子组件大小和位置的两个技巧。
第一个技巧:子组件不刻意设置宽度或设置绝对宽度值,子组件间使用“Blank”组件填充,使Text组件和Button组件贴近左、右边缘。在前面代码的基础上加入如下代码:
效果如下图所示:
后面章节讲解“Flex”布局组件时,还会介绍使用“justifyContent”实现上述效果。
第二个技巧:子组件不设置宽度,但通过“layoutWeight”设置该组件在父级容器中的宽度权重比例。在前面代码的基础上加入如下代码:
"Row"布局组件下三个子组件"Column"通过“1:2:1”的比例瓜分了父级容器的宽度。效果如下图所示:
注意:"layoutWeight"仅适用于“Flex/Row/Column”布局组件下的子组件。
2. 自适应缩放
对于图片的展示,可以锁定宽高比例,同时将宽设置为百分比的值,实现自适应缩放,示例代码如下:
效果如下图:
注意:上述锁定图片的宽高比时建议使用“.aspectRatio(1.5)”这个属性,其中宽高比值“1.5”要根据具体图片的实际宽高比确定。暂时不要使用“.objectFit(ImageFit.Contain)”这个属性来保持图片组件的宽高比。经测试,在本场景下会在图片组件下产生不可控的间距。可能是一个Bug,希望ArkUI eTS正式版时鸿蒙官方能修复这个问题。代码如下:
效果如下图所示:
注意:为了图片在缩放状态下都显示清晰,图片尺寸优先考虑在较大屏幕时能显示清晰。避免小屏清晰,大屏马赛克。
3. 自适应延伸
自适应延伸的要点在于不设置父级容器宽度,由子组件将父容器撑开。当不同设备的屏幕宽度发生变化时,组件随之发生自适应延伸显示更多数量。示例代码如下:
效果如下图所示:
实际项目中,我们会配合“Scroll” 可滚动容器组件,实现被遮挡子组件的显示。本页面的代码在横屏状态下,会有部分内容被遮挡,也可以通过"Scroll"纵向滚动解决。“Scroll”组件的用法在后面章节演示,非本节讲解重点,暂时不实现。
本小节代码位于“index.ets”页面,完整代码如下:
2.6.3 响应式布局
当基本的自适应布局无法满足多终端上屏幕的体验要求时,我们需要针对不同终端的屏幕特点进行响应式的布局。常见的响应式布局样式有:分栏布局、重复布局、挪移布局和缩进布局。
1. 分栏布局
利用屏幕的宽度优势,将有上下级层级的界面同时左右显示。 比如,新闻列表页和新闻详情页。在手机上,屏幕宽度有限,那么在新闻列表页点击某条新闻后,页面跳转到新页面展示新闻详情。但是在宽屏幕设备上,可以不用跳转页面,直接在左侧展示新闻列表,在屏幕右侧展示新闻详情。完成这个案例演示,需要如下代码。初学的同学如果有一些代码看不懂,可以先不用纠结,后面章节会讲解,这里先关注分栏布局的效果:
1) 新闻列表页(/pages/news/list.ets)
2) 新闻详情页(/pages/news/detail.ets)
3) 新闻数据结构及模拟(/model/NewsDataModel.ets)
本案例涉及识别手机屏幕横竖屏,所以,需要在远程模拟器体验真实效果。最终,本案例在手机竖屏下的效果如下图所示:
在手机横屏模式、折叠屏、平板和车机下,新闻列表和新闻详情同时显示。效果如下图所示:
2. 重复布局
利用屏幕的宽度优势,将相同属性的组件横向并列排布。 一般用于卡片陈列。我的实现思路是:先通过"display"接口拿到设备的显示属性,通过"display.getDefaultDisplay()"获得当前设备的"Display"对象,然后通过该对象的“width”属性拿到显示设备的宽度(单位为像素),最后判断不同屏幕宽度时该显示几列。配合设置栅格组件"Gird"的“columnsTemplate”属性来控制显示的列数。示例代码(/pages/index2.ets)如下:
该效果需要通过远程模拟器查看效果,本地预览器无法获得设备属性。由于远程模拟器目前仅有手机设备,下图展示在手机横竖屏时的效果,如下图所示:
该代码在折叠屏和平板上分别显示两列和三列,以下效果为我模拟而来,供参考,效果如下图:
3. 挪移布局
光明顶至高武学“乾坤大挪移”。本案例以手机横竖屏切换时通过挪移组件位置,实现在竖屏和横屏状态下充分利用手机屏幕空间展示信息。实现思路:先判断设备的横竖屏状态,然后根据状态决定组件布局方式,示例代码(/pages/index3.ets)如下:
在远程模拟器上手机竖屏和横屏的效果如下图所示:
4. 缩进布局
为了有更好的内容显示效果,可在不同屏幕宽度的设备上进行相应的缩进处理。 比如,卡片在手机竖屏时为4个栅格的宽度;而在手机横屏状态或折叠屏设备上居中显示,且设置为6个栅格的宽度;在平板设备上居中显示,且设置为8个栅格的宽度。示例代码(/pages/index4.ets)如下:
在远程模拟器上手机竖屏和横屏的效果如下图所示:
前面三小节的代码可到码云下载:
【源码地址:https://gitee.com/cloudev/harmonyos3/tree/master/2.6 】
2.6.4 使用资源实现组件多态
前面讲解的技巧实现了多设备下布局的“一致性”,使同一份代码的App在不同设备上的布局均有良好表现。同时,适配了竖屏模式和横屏模式的差异。
与追求布局一致性的目标相反,在组件开发中,追求组件在多设备、多语言及“深色模式/浅色模式”的“差异性”。让组件在不同环境中呈现差异化的表现,称之为“多态”。
实现组件“多态”的关键技巧在于使用资源。
1. 资源定义
应用资源由开发者在工程的resources目录中定义,resources目录按照两级目录的形式来组织。
一级目录为base目录、限定词目录以及rawfile目录 。如下图所示:
其中:
base目录:默认存在的目录。当应用的resources资源目录中没有与设备状态匹配的限定词目录时,会自动引用该目录中的资源文件。
限定词目录:开发者自行创建的目录。可以由一个或多个表征应用场景或设备特征的限定词组合而成,包括移动国家码和移动网络码、语言、文字、国家或地区、横竖屏、设备类型、颜色模式和屏幕密度等维度。App运行时,优先从限定词目录寻找与当前设备状态匹配的资源(如,中文语言、横屏模式、深色模式),找不到合适资源才会使用base目录中的资源。限定词目录也是实现组件多态的关键。
rawfile目录: 不会根据系统的状态去匹配,rawfile目录中可以直接存放资源文件。
二级目录为资源目录,用于存放字符串、颜色、浮点数等基础元素,以及媒体等资源文件。如下图所示:
其中:
boolean.json:存放布尔型资源。
color.json:存放颜色资源。在“/base/element/color.json”目录下存放手机浅色模式和透明模式的各种颜色值。在限定词目录下的相应“color.json”中存放特定场景时生效的颜色资源。如下图所示:
float.json:存放间距、圆角、字体等资源。 限定词目录下的“float.json”如下图所示:
string.json:存放字符串资源。 原则上,除了从远程服务端数据库里取出的文字外,App中用到的文字显示,尽量设置到“string.json”中,即使是广告图片中存在文字,也建议将图片和文字分离。这样为App实现多语言版减少不必要的麻烦。默认情况下,除了默认的字符串资源,建议增加中文字符串资源和英文字符串资源。如下图所示:
meida目录:媒体资源。如下图所示:
2. 创建资源
刚创建一个ArkUI eTS项目时,并没有上图中的限定词目录,那么,它们是怎样创建出来的呢?步骤如下:
在resources目录上点击鼠标右键,选择“新建”,然后选择“资源目录”,如下图所示:
在左侧的“Available qualifiers”下针对语言、横竖屏、设备、颜色模式(深色/浅色)、屏幕密度等提供各种可选限定词类型,本例以添加对车机的支持为例。选择Device,然后点击向右箭头按钮,如下图所示:
Resource type的选项中,一般我们用得最多的是Element。如果是建立媒体资源,则选择Media。其它几种主要是用于Java UI的资源,我们不用关心。如下图所示:
然后在Device下选择Car(车机),点击“确认”按钮,如下图所示:
此时,在resources目录就建立了“/car/element”的子目录,DevEco Studio中显示为"car.element"。在该目录上使用鼠标右键,选择“新建”,然后选择Element Resource File,如下图所示:
在Root element下拉选项中,选择需要的资源文件模板,如果需要建立颜色资源,就选color,文件名也输入"color",如下图所示:
点击“确认”按钮,如下图所示:
现在,适用于车机的颜色资源“color.json”就成功创建了。如下图所示:
建议同学们利用一个Hello World项目,参照上面的步骤,多尝试下不同资源组合,看看都能建立什么样的限定词资源。
3. 资源引用
在工程中,通过“r”代表resources目录,“app”代表应用内定义的资源,“type”代表资源类型(或资源的存放位置),可以取“color”、“float”、“string”、“media”等,name代表资源命名,由开发者定义资源时确定,如上图中使用"color_1"这个name定义了一个“红色”资源。如果要设置一个文字的颜色为红色,那么可以使用如下代码:
引用rawfile下资源时使用“rawfile仅支持Image控件引用图片资源,filename需要表示为rawfile目录下的文件相对路径,文件名需要包含后缀,路径开头不可以以"/"开头。
2.6.5 ArkUI eTS开发框架
虽然目前ArkUI eTS暂时仅支持手机、折叠屏、平板和车机,但是,通过观察鸿蒙官方的eTS API参考文档,对于每个组件的描述中都有一个支持设备的说明,全部标记对智慧屏和智能穿戴“不支持”,参考下图:
可以解读出一个信息:对智慧屏和穿戴设备的支持是迟早的事情,否则根本没必要在每个文档中多此一举。所以,在本eTS开发框架中提前提供对智慧屏和智能穿戴设备的支持。
该UI框架命名为“HUI”,“H”是指HarmonyOS。随着后续课程的推进节奏,会持续丰富此框架。第一次提交的项目代码已配置好沉浸式体验的状态栏,同时,根据“2.3 ArkUI App设计规范”建立了配套的应用资源。
【源码地址:https://gitee.com/cloudev/HUI 】
2.6.6 小试牛刀
基于初始化Hello World页面,使用资源改造页面,代码如下:
竖屏手机,中文语言环境,浅色模式:效果如下图所示:
竖屏手机,英文语言环境,浅色模式:效果如下图所示:
竖屏手机,中文语言环境,深色模式:效果如下图所示:
手机横屏,深色模式,如下图所示:
折叠屏,竖屏,浅色模式,如下图所示:
折叠屏,横屏,深色模式,如下图所示:
平板,横屏,浅色模式,如下图所示:
平板,竖屏,深色模式,如下图所示:
车机显示效果,如下图所示:
很详细!感谢分享!
谢谢老师分享
请教个:这里面的功能,有什么是Android目前达不到的吗?
如果单纯从单机功能来看,和Android没多大差别。但是,鸿蒙的核心魅力在于分布式,可以利用不同设备的特性,赋能为超级终端。因此,在鸿蒙开发过程中,全场景的开发才是精髓所在。
老师,示例的list.ets中,matchMediaSync显示黄色,提示mediaquery无此属性。如图:
import mediaquery from '@system.mediaquery'
这个有没有引用?
已经引用了。
不过,虽然显示黄底提示,远程预览时还是能正常执行的,不影响效果。
谢谢老师。
目前ArkUI eTS还处于beta版阶段,只要不影响功能,暂时不用太在意,官方应该会有优化的。
老师,我又遇到了一个问题,就是“.fontWeight(Number($r("app.float.fontWeightH6")))”这种设置没起作用,改变float.json里fontWeightH6的值如从500改为700或者900,显示都没有变化,不论是本地预览器还是远程模拟器,都是这样。难道又是beta版Bug?
应该不是Bug。这里有两个点提醒一下:
1.fontWeightH6的值是根据鸿蒙的相关规范设置的,不建议修改。
2.如果仅仅是为了做实验,修改了这个值,因为它是资源,想要生效,需要重新编译一次。
如果重新编译后还不行,方便的话,将你的代码发给我,我帮你调试一下看看。
强!!
哈哈,谢谢鼓励!
老师,我是实验而已,不会修改fontWeightH6的值的。我是试着把fontWeightH6的值改为900,结果发现,文本并没有变粗。
我发现,这种通过float.json资源设置fontWeight值都没有生效,也就是没有效果,文本的字重没有变化,还是默认的样子,不管是系统定义的还是自定义的,都一样。当然,直接在ets文件里设置如fontWeight(900)是有效的, 而.fontWeight(Number($r("app.float.***")))则无效。无关编译,因为重新编译,或者设置后关闭项目重新启动,都不行。不是我的代码问题,我就在你的HUI项目上测试也不行。
难道是我电脑有问题?
不是你的电脑问题,我这边也复现了这个问题。同时,今天我写教程的时候发现Counter()组件代码不被识别,之前没这个问题。可以先不管这个问题。
我会继续关注你提的这个问题。
最近正在做一多,学到了