赞
踩
在Java UI框架中,提供了两种编写布局的方式:在XML中声明UI布局和在代码中创建布局。这两种方式创建出的布局没有本质差别,都是我们需要熟悉方式,所以我们将通过XML的方式布局第一张页面,然后再通过代码的方式布局第二张页面。
这里先实现Page间跳转,用以讲解两种Page的构建方法
更多跳转的知识现在后续课程跟进
我们第一次打开IDE会进入这样一个界面
不要点击新建项目!
不要点击新建项目!!
不要点击新建项目!!!
重要的事情说三遍,我们点击+Creat HarmonyOS Project
选择Empty Ability(Java)
点击下一步
创建一个如图的项目,项目名遵循大驼峰式
进入之后我们来到这样一个界面
表示我们项目创建完成了
下面的外部库就是存放第三方jar包的库,没什么好说的,主要就是我们的项目包
左侧的Learnin就是我们的整个项目的包,和IDEA很像,因为我们的鸿蒙IDE是在开源版IDEA基础上研发的,这样用起来就会很习惯
我们先把展开的文件夹合上,这样便于我们层层展开认识结构
第一个是**.gradle**我们基本上不去改它,是自动生成信息,不用去管
第二个目录entry是最重要的,是我们整个应用的主模块,而且每一个可在设备上运行的鸿蒙应用必定有且只有一个entry主模块,我们启动程序是从主模块开始运行
当然后续我们会提到更详细的运行规则
第三个gradle(注意没有.)里面有个wrapper,里面有个jar包,没什么好说的,我们只要掌握一个东西,gradle-wrapper.properties,里面有gradle的版本等相关信息,一般不去改它。
接下来在根模块下有一个build.dradle(应用模块都有一个,这里先说根模块下的),引用第三方jar包、依赖申明等均在此。
dradle十分复杂和好用,暂时我们只要掌握粗浅的就可以了
我们看根目录下的gradle.properties是一gradle的属性文件,我们可以在里面做一些属性,在gradle脚本下可以直接引用,我们暂时用不到
再看这个gradlew,是Linux脚本,而这个gradlew.bat,是Win的bat脚本,基本用不到因为已经有集成脚本了,我们点击右栏里的gradle,选择所需脚本双击就可以了。
下面一个local.properties,是鸿蒙应用的配置文件,点进去看看,会发现有SDK和node.js的路径信息
最后一个setting.gradle,是我们gradle的全局配置,我们一般也不去动
不明白脚本有什么用?
没关系我们底下会举一个例子
以上说完了根目录,我们接下来讲最主要的entry模块
我们点开entry这里也有一个build.dradle,模块引用jar包基本在这里引用。
里面的第一个目录build是IDE在构建项目时自动生成的,我们不会去动,但是我们还是要去了解一下,我们把它点开
里面有一个intermediates,里面是一些中间数据。
如下图
你可能会发现,你的界面跟我好像不太一样,你只有一个intermediates,这是因为你这个程序还没有被运行,即没有被“初始化”,所以我们这里穿插着先讲一下我们怎么运行我们鸿蒙文件,真机调试我们会在后面单独拿出来讲,因此这里先讲一下最快捷的虚拟机调试。
点击工具,点击Device Manager
然后登陆华为开发者账号(没有的话自己去注册)
点击Phone,选择一款虚拟机,点击小三角运行虚拟机
如果你展现了跟我一样的界面,那么你的虚拟机就运行成功了(我选择的是P40)
点击右上角运行
运行成功!
我们再点击build就会像我这样了
我们来看这个新出现的generated,点开找到这个文件
每当我们在应用中添加一个资源文件的时候,这里面都会自动相应生成一个静态索引值,在编程的时候就可以直接利用索引值获取静态资源引用。
通常都是会自动生成的,如果bug了没生成或者你下载的是老版本的鸿蒙IDE,怎么办?
这里就要用到我们gradle脚本了,我们点开右侧的脚本栏,点击小大象,找到这个
双击即可修复bug(这里还是推荐大家用最新的IDE)
build我们就看到这里,我们把它合上
第二个是一个空的文件夹目录,叫做libs,用于存放第三方jar包
第三个src又是entry里面最重要的,这是我们写源码的地方,我们把它打开,我们看到里面有如下几个目录文件
其中的ohos test在后面学习greadle时再详细展开,test里面写的是一些测试文件,没什么好说
我们来看一下main,展开
里面我们会看到三个东西——java、resources和config.json
java写一些java类
resources存放资源,点开
里面的layout存放一些xml布局文件,graphic存放一些组件形状配置(比如xml背景、按钮的形状等),element存放一些元素(比如字符串),media存放媒体文件(图片视频音频),profile我们在后面复盘时再细讲(这个很有用的等会细讲)
config.json又是一个重点中的重点
应用的每个HAP的根目录下都存在一个“config.json”配置文件,主要涵盖以下三个方面:
1)应用的全局配置信息,包含应用的包名、生产厂商、版本号等基本信息。
2)应用在具体设备上的配置信息。
3)HAP包的配置信息,包含每个Ability必须定义的基本属性(如包名、类名、类型以及Ability提供的能力),以及应用访问系统或其他应用受保护部分所需的权限等。
config.json配置文件的内部结构
应用的配置文件“config.json”中由“app”、“deviceConfig”和“module”三个部分组成,缺一不可。
“config.json”文件约定
配置文件“config.json”采用JSON文件格式,由属性和值两部分构成:
1)属性:属性出现顺序不分先后,且每个属性最多只允许出现一次。
2)值:每个属性的值为JSON的基本数据类型(数值、字符串、布尔值、数组、对象或者null类型)。
里面的详细内容,是我们后面单独一节的重点,我们会结合一个思维导图来学习。
这里带大家认识项目结构,是为了带领大家学习FA,因此我们本节课的重中之重,在java这一块
我们点进java,我们会发现有一个叫MyApplication的文件,翻译成中文就是我的应用,顾名思义这里是整个项目的主入口,它是AbilityPackage的子类,在这个例子里面我们不去细讲。
而跟它同级的就是我们程序的另一个主入口
这个MainAbility就是一个Ability,而且是一个FA,因为它有前端页面。
页面在哪?
我们看到里面唯一的方法onStart(),里面有个setMainRoute()——设置主路由,这个方法可以获取到一个具体的页面实例
前面我们提到过,FA的模板是Page Ability,而Page Ability相当于一个页面容器,可以存放好多个页面,每一个页面都有一个AbilitySlice实例,存放在上面那个slice文件夹内,注意Page Ability可以存放多个,但至少存放一个界面,否则就是PA了。
我们打开slice,打开MainAbilitySlice
这个MainAbilitySlice就是一个我们可以看得到的界面,而这个页面的布局在默认的情况下是XML,在onStart()里面通过函数,通过resourceTable里面的静态索引值找到页面布局,进行绑定
其中的Layout表示在rescources中base下的layout文件夹内,同时表示获取的是一个页面布局,ability_man是这个xml文件的名字
找到并打开它
这个就是第一个通过xml方式编写的页面,因为我们要做两个页面实例,所以我们得弄个按钮进行页面跳转
我们上节课提到过了如何进行跳转,这里我们就简单的设置一个按钮,用**onPresent()**进行跳转
系统默认的xml文件,是如图所示的DriectionalLayout,是直接布局(从上到下摆放组件),这样会就会很呆,因为你在手机模拟器可能看起来很正常,但是我们放到TV、Pad、智能手表上运行就会很奇怪,非常奇怪(当然,有时候会更好)
所以我们使用用另一种布局进行开发
我们把之前的xml文件先全删了,再自己一点一点进行开发编写,这样学习效果更好
我们采用<dependentLayout>
进行布局,这是一种依赖式的布局,即其中组件布局取决于我们运行的硬件设备(例如屏幕分辨率)
第一个页面内有一个文本和一个按钮,使用DependentLayout·布局,通过Text和Button组件来实现,其中vp和fp分别表示虚拟像素和字体像素。“ability_main.xml”的示例代码如下:
<?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="#ffffff"> <Text ohos:id="$+id:text" ohos:width="match_content" ohos:height="match_content" ohos:text="MainAbilitySlice" ohos:text_color="#000000" ohos:text_size="32fp" ohos:center_in_parent="true"/> <Button ohos:id="$+id:button" ohos:width="match_content" ohos:height="match_content" ohos:text="Next" ohos:text_size="19fp" ohos:text_color="#FFFFFF" ohos:top_padding="8vp" ohos:bottom_padding="8vp" ohos:right_padding="70vp" ohos:left_padding="70vp" ohos:center_in_parent="true" ohos:below="$id:text" ohos:margin="10vp" ohos:background_element="$graphic:background_button"/> </DependentLayout>
先别去运行,运行出来不对的(因为以上只是部分代码)
我们先讲<DependentLayout></DependentLayout>
,他表示在这个标签里面申明了一个布局,相当于类似JavaWeb里面的<html></Html>
然后看到标签内的第一个属性<DependentLayout xmlns:ohos="http://schemas.huawei.com/res/ohos">
相当于<html lang="en" class="no-js">
是一定要写的,表示了以下布局属性的标准来源,我们接下来要申明的属性前要遵循格式ohos:属性名=属性值
下两行的长宽,顾名思义,设置的是这个布局的长宽,而里面的match_parent
则表示与父容器大小一致,即占满整个容器的全部,意思是不论这个鸿蒙应用在电视、TV、平板、手表上执行,都只会占满整个屏幕
我们再把它的颜色设置为#fffff白色
以上就是这个简单页面的全部布局属性
有了布局,我们还要再布局内添加组件,我们先简单地添加一个text文本组件,来显示我们目前先显示的页面名,再加上一个botton按钮组件用于跳转页面
和JavaWeb不同,这里<test/>
组件单独使用,且组件内容在属性内设置
最基本的我们要给组件设置宽高,设置为match_content
,意思是文本本身有多高,组件就有多高
再设置文本内容为页面Slice名MainAbilitySlice
,调整文字大小,我们看一下根据提示跳出来的单位,有fp、px、vp三种
fp:用于描述字体大小,相对值
px:像素,是一个绝对值,不会随外界变化改变
vp:用于描述边距间隔,相对值
(相对值与屏幕硬件有关)
所以我们单位用fp
设置字体颜色为#00000黑色
到此为止,这个文本文件本身已经写好了,接下来要把它放入布局
我们把center_in_parent
设置为true,表示这个组件放到父容器的中央,垂直方向水平方向都放到中央(它的父容器是这整个布局)
运行下看看
没有问题,根据这个页面我们就可以获取,到这个应用目前处于entry_MainAbility这个Page下的MainAbilitySlice页面
加下来添<button/>
组件
和刚才一样设置好宽高、文字、字号,由于这个是一个按钮,文字占满整个组件肯定不好看,而且得有背景颜色,不然在屏幕上找不着它
先设置内边距(padding)我们输入padding,IDE会自动提示有上下左右四个内边距,根据自己喜好设好(单位上面讲过了)
背景的话,我们采用单独使用一个xml文件进行申明
按钮的背景是蓝色胶囊样式,可以通过graphic目录下的XML文件来设置。
右键点击“graphic”文件夹,选择“New > File”,命名为“background_button.xml”,单击回车键。
“background_button.xml”的示例代码如下:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="100"/>
<solid
ohos:color="#007DFF"/>
</shape>
其中的corner
设置边角,radius
设置转角磨圆shape
设置形状,为长方形,solid
设置背景
这时候我们的按钮实在整个布局的最开始,也就是左上角,接下来,我们要设置它的位置,把它放在text下面,这里就体现了相对布局的相对性,我们既然在申明位置时需要使用text
组件,那么我们就得给它一个唯一识别的东西——ID,来让我们能一下子找到这个组件
格式为$+id:组件的id
表示为组件添加ID,此时IDE会自动在resourceTable里添加这个组件的ID
我们把按钮放在text
组件的下面,设置below="$id:text"
(此处为引用其他组件时的格式:区别在于不加+号)
设置完了我们再设置一下水平居中horizontal_centre
设置为true
我们此时再运行一下
当然,此时点击按钮是没用的,因为没有设置action
如果嫌弃两个组件太近了看着不舒服,就来设置一下外边距margin就好了
到此为止,我们第一个页面,也就是xml页面就做好了
接下来我们做第二个页面,我们将采用编程的方式实现布局
我们新建一个Ability
我们可以看到,IDE为我们自动生成了四个东西,由于系统默认是XML布局,因此这四个东西里面基本上是用来做XML页面的,也就是说这里面有很多文件是如果是编程布局的话使用不到的,删掉就行
我们删掉这两个
删除以后我们找到这个页面的Slice文件,在这里面进行编程式布局
由于删掉了,这里会爆红
我们把这个设置主路由删掉就好了
还有一部分自动生成的东西,在config.json当中,点开找到Abilities
是一个对象数组,其中这一段是原来没有的
是关于新建的Ability的配置文件
有关config.json更详细的内容,我们会单独拿一节课出来讲
话不多说,回到Slice
示例代码如下:
package com.example.learning.slice; //请根据实际工程/包名引入 import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.content.Intent; import ohos.agp.colors.RgbColor; import ohos.agp.components.*; import ohos.agp.components.element.ShapeElement; import ohos.agp.utils.Color; import ohos.agp.components.DependentLayout.LayoutConfig; public class SecondAbilitySlice extends AbilitySlice { @Override public void onStart(Intent intent) { super.onStart(intent); // 声明布局 DependentLayout myLayout = new DependentLayout(this); // 设置布局宽高 myLayout.setWidth(LayoutConfig.MATCH_PARENT); myLayout.setHeight(LayoutConfig.MATCH_PARENT); // 设置布局背景为白色 ShapeElement background = new ShapeElement(); background.setRgbColor(new RgbColor(255, 255, 255)); myLayout.setBackground(background); // 创建一个文本 Text text = new Text(this); text.setText("SecondAbilitySlice"); text.setWidth(LayoutConfig.MATCH_PARENT); text.setHeight(LayoutConfig.MATCH_PARENT); text.setTextSize(32, Text.TextSizeType.FP); text.setTextColor(Color.BLACK); text.setId(1) ; // 设置文本的布局 DependentLayout.LayoutConfig textConfig = new DependentLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT); textConfig.addRule(LayoutConfig.CENTER_IN_PARENT); text.setLayoutConfig(textConfig); myLayout.addComponent(text); //创建一个按钮 Button button =new Button(this); button.setWidth(LayoutConfig.MATCH_PARENT); button.setHeight(LayoutConfig.MATCH_PARENT); button.setText("Next"); button.setTextSize(19, Text.TextSizeType.FP); button.setTextColor(Color.WHITE); button.setPadding(AttrHelper.vp2px(70, AttrHelper.getDensity(this)),AttrHelper.vp2px(8, AttrHelper.getDensity(this)),AttrHelper.vp2px(70, AttrHelper.getDensity(this)),AttrHelper.vp2px(8, AttrHelper.getDensity(this))); //设置按钮样式 ShapeElement backgroundB = new ShapeElement(); backgroundB.setRgbColor(new RgbColor(0, 125, 255)); backgroundB.setShape(0); backgroundB.setCornerRadius(100); button.setBackground(backgroundB); button.setMarginTop(AttrHelper.vp2px(10, AttrHelper.getDensity(this))); //添加按钮组件布局 DependentLayout.LayoutConfig layoutConfig = new DependentLayout.LayoutConfig(); layoutConfig.addRule(LayoutConfig.HORIZONTAL_CENTER); layoutConfig.addRule(LayoutConfig.BELOW,text.getId()); layoutConfig.setMarginBottom(1); button.setLayoutConfig(layoutConfig); myLayout.addComponent(button); button.setClickedListener(listener -> present(new MainAbilitySlice(), new Intent())); //启用布局 super.setUIContent(myLayout); } }
编程开发,和xml开发是可以对应起来的,首先我们要实例化一个布局
DependentLayout myLayout = new DependentLayout(this);
再利用DependentLayout包装好的函数设置它的宽高
myLayout.setWidth(LayoutConfig.MATCH_PARENT); myLayout.setHeight(LayoutConfig.MATCH_PARENT);
宽高是可以直接设定的,但是背景则与要ShapeElement对象实例进行设置,我们先实例化一个ShapeElement
ShapeElement background = new ShapeElement();
,再对这个实例进行赋值,设置背景颜色
background.setRgbColor(new RgbColor(255, 255, 255));
,再把这个ShapeElement通过函数赋给这个布局,作为背景
myLayout.setBackground(background);
这里设置颜色是按照三原色来的,最高是255
接下来创建文本组件Text
,先实例化一个对象TextText text = new Text(this);
设置相关属性
text.setText("SecondAbilitySlice");
text.setWidth(LayoutConfig.MATCH_PARENT);
text.setTextSize(32, Text.TextSizeType.FP);
text.setTextColor(Color.BLACK);
接下来设置这个组件的布局
由于我们这个组件是要放在一个DependentLayout的布局下,所以我们先申明一个DependentLayout的LayoutConfig布局
比如这个布局
DependentLayout.LayoutConfig textConfig =
new DependentLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT);
应用这个布局的组件的宽高将会随着组件内容变化而变化
然后我们给这个布局添加规则,把它放在父容器中间
textConfig.addRule(LayoutConfig.CENTER_IN_PARENT);
最后再把这个布局和text组件绑定在一起,把这个组件放入布局
text.setLayoutConfig(textConfig);
myLayout.addComponent(text);
同理做出button组件,其中只有几个要注意的地方,就是setPadding时使用了鸿蒙的工具类,将vp转化为px因为函数只能调用px
button.setPadding(AttrHelper.vp2px(70, AttrHelper.getDensity(this)),AttrHelper.vp2px(8, AttrHelper.getDensity(this)),AttrHelper.vp2px(70, AttrHelper.getDensity(this)),AttrHelper.vp2px(8, AttrHelper.getDensity(this)));
最后,我说明一下addRule语句,这个很重要,同时也讲一下学习方法(对学习Java、HarmonyOS等语言都适用)
我们这里的目的是给button组件加上一个布局,布局的要求是居中显示在text组件下方,总共两个约束,所以要添加两个rule,一个是相对父容器的,一个是相对某组件的
其中系统可以自动识别父容器,但在addRule时无法传参对象,只能传参int,因此我们要给text组件一个ID
{
···
text.setId(1) ;
···
···
layoutConfig.addRule(LayoutConfig.HORIZONTAL_CENTER);
layoutConfig.addRule(LayoutConfig.BELOW,text.getId());
···
···
···
}
这样我们就把button组件设置在text组件下方了
这个按钮是我在参阅官方文档之后,觉得它给出的案例不完善,自己改进的,可以说找遍全网也找不出来第二份(截止至这篇博客发布)
网上更多的是讲解xml方式的,确实xml最为常用且好用,但不代表我们不需要掌握编程的方法,但很不巧的是,大家似乎都停留在了官方文档给出的案例那个层面,因此当我们想彻底搞懂某个别人不在意、网上找不到的东西时,该怎么学呢?
这里讲一下我给出的建议
第一步,分析我们想搞懂的到底是什么?
对于这个案例来说,我们要搞懂的是一个组件布局。
第二步,寻找参考
第一个参考:开发文档中给出的添加布局设置,使用的是addRule
第二个参考:xml布局当中,使用的是唯一标识ID,用以识别发生关系的组件
第三步,看源码思考
我们点进addRule的源码看一下
public void addRule(int verb, int subject) { throw new RuntimeException("Stub!"); } public void addRule(int verb) { throw new RuntimeException("Stub!"); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这里可以看到这个方法有两个实现,一个是传递参数verb,一个是传递verb和一个subject,英文翻译成中文就是,动作和对象
再点击setId方法
public void setId(int id) { throw new RuntimeException("Stub!"); }
- 1
- 2
- 3
可以发现,组件的ID是int(不同于xml),既然有所不同,那么我们创建的组件是否会加入ResouceTable呢?我们去看一下
发现没有,这大概就是编程的形式创建页面的不一样之处
第四步,尝试
阅读完源码,就可以开始自己的尝试了,根据我们刚才查看源码的操作,心里应该有一个思路了
1、给text设置一个ID
2、添加一条规则,采用双参数传递的方法,一个参数是至于底部,一个参数是置于底部对象的ID
3、运行,调试
第五步,当原有思路是错误的
去官方文档再寻找参考,返回第二步
如果搞不出来就去睡觉,睡一觉脑子清醒了再来钻
以上
再设置跳转回到刚才的页面,这样我们两个页面就实现了相互跳转了
下一节将详细讲述页面间的跳转(有参无参、Page内Page间等)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。