华为鸿蒙HarmonyOS Java 布局开发

在HarmonyOS应用中,所有的用户界面元素都是由 ComponentComponentContainer对象构成。

Component 是绘制在屏幕上的一个对象(可类比成Android的View),用户能与之交互。component 提供了内容显示,是界面中所有组件的基类,开发者可以给Component 设置事件处理回调来创建一个可交互的组件。Java UI 框架提供了一些常用的界面元素,也可以称为组件,组件一般直接继承Component 或它的子类,如 Text、Image等

ComponentContainer (可类比成Android的ViewGroup) 是一个用于容纳其它Component 和 ComponentContainer 对象的容器。作为容器容纳 Component 和 ComponentContainer 对象 ,并对它们进行布局。Java UI框架提供了一些标准布局功能的容器,它们继承自 ComponentContainer ,一般以 “Layout” 结尾,如 DirectionaLayout、DependenLayout等。

为了方便开发者开发APP,HarmonyOS在Java UI 框架中提供了一系列的Component和 ComponentContainer 的具体子类,即创建用户界面(UI)的各类组件,包括一些常用的组件(比如:文本、按钮、图片、列表等)和常用的布局(比如: DirectionaLayout 和 DependentLayout)。用户可通过组件进行交互操作,并获得响应。

和Android 一样在HarmonyOS中,所有的UI操作都必须在主线程完成。

LayoutConfig

每种布局都根据自身特点提供LayoutConfig供子Component设定布局属性和参数,通过指定布局属性可以对子Component布局中的显示效果进行约束。例如:“width”、“height”是最基本的布局属性,它们指定了组件的大小:

鸿蒙HarmonyOS中的LayountConfig类似于Android中的 LayoutParams

Java UI开发指南

[$]

HarmonyOS提供了Ability(类似于Android中的Activity)和AbilitySlice(类似于Android中的Fragment)两个基础类。有界面的Ability绑定了系统的Window进行UI展示,且具有生命周期。AbilitySlice主要用于承载Ability的具体逻辑实现和界面UI,是应用显示、运行和跳转的最小单元。AbilitySlice通过setUIContent() 为界面设置布局。

AbilitySlice的UI接口

AbilitySlice通过setUIContent() 为界面的根布局,以此来显示一个界面

setUIContent(ComponentContainer componentContainer)

在Java UI 框架中,设置setUIContent有两种方式,一种是通过Java代码创建布局,另一种是通过在xml中声明UI布局。

  • 通过Java代码创建布局:用代码创建 Component和ComponentContainer对象,为这些对象设置合适的布局参数和属性值,并将Component添加到 ComponentContainer 中,从而创建出完整的界面。
  • 通过在XML文件中声明布局:按层级结构来描述Component和ComponentContainer的关系,给组件节点设定合适的布局参数和属性值,代码中可以直接加载生成此布局。

这两种方式创建出来的布局没有本质的区别,在XML中声明布局,在加载后同样可在代码中对该布局进行修改。

组件分类

根据组建的功能,可以将组件分为布局类、显示类、交互类三类:

组件类别组件名称功能描述
布局类PositionLayout、DirectionLayout、StackLayout、DependentLayout、TableLayout、AdaptiveBoxLayout提供了不同布局规范的组件容器,例如以单一的方向排列的DirectionLayout、以相对位置排列的DependentLayont、以确切位置排列的PositionLayont等
显示类Text、Image、Clock、TickTimer、ProgressBar提供了单纯的内容显示,例如用于文本显示的Text,用于图像显示的Image等
交互类TextField、Button、Checkbox、RadioButton/RadioContainer、Switch、ToggleButton、Slider、Rating、ScrollView、TabList、ListContainer、PageSlider、PageFlipper、PageSliderIndicator、Picler、TimePicker、DatePicker、SurfaceProvider、ComponentProvider等等提供了具体场景下与用户交互响应的功能,例如Button提供了点击响应功能,Slider提供了进度选择功能等

布局单位

HarmonyOS重新定义了界面换算单位,使用虚拟像素(virtual pixels),简称vp。用来作为一台设备针对应用而言所具有的虚拟尺寸,是定义应用内参数尺寸的度量单位。虚拟像素是一种可灵活使用和缩放的单位,它与屏幕像素的关系是 1vp 约等于 160dpi 屏幕密度设备上的1px。在不同密度的设备之间,HarmonyOS 会针对性的转换设备间对应的实际像素值。

另外,针对文本,HarmonyOS 提供了字体像素(font-size pixels),简称 fp作为单位。

字体像素大小默认情况下与 vp 相同,即 1fp = 1vp ,但当用户修改了字体显示大小,那么字体大小则会在 vp 的基础上乘以 scale 系数,即 1fp = 1vp * scale

通过Java代码实现点击按钮累加数字的案例:

效果图:

核心代码如下:

package com.example.helloword.slice;

import com.example.helloword.ResourceTable;
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.utils.LayoutAlignment;

public class LayoutAbilitySlice extends AbilitySlice {

    private int count = 0;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);

        // ResourceTable.Layout_ability_layout

        super.setUIContent(getLayout());
    }

    private ComponentContainer getLayout() {

        /*
         创建根布局容器
         DirectionalLayout(方向排列) 类似于Android LineaLayout(线性布局)
         */
        DirectionalLayout directionalLayout = new DirectionalLayout(this);
        // 设置布局宽高填充父容器
        directionalLayout.setWidth(DirectionalLayout.LayoutConfig.MATCH_PARENT);
        directionalLayout.setHeight(DirectionalLayout.LayoutConfig.MATCH_PARENT);
        // 设置竖直排列
        directionalLayout.setOrientation(Component.VERTICAL);


        // 创建一个 Text 组件
        Text text = new Text(this);
        text.setText("count:"+count);
        text.setTextColor(Color.BLACK);
        text.setTextSize(30);


        // 创建LayoutConfig 来控制布局
        DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(DirectionalLayout.LayoutConfig.MATCH_CONTENT,DirectionalLayout.LayoutConfig.MATCH_CONTENT);
        // 设置DirectionalLayout的子元素水平居中
        layoutConfig.alignment = LayoutAlignment.HORIZONTAL_CENTER;
        // 设置Text 的布局配置
        text.setLayoutConfig(layoutConfig);
        // 将 Text 添加到directionalLayout容器中
        directionalLayout.addComponent(text);


        // 添加一个 Button 组件
        Button button = new Button(this);
        // 通过 layoutConfig 对象设置Button的间距
        layoutConfig.setMarginTop(100);
        button.setText("点我");
        button.setTextSize(50);
        button.setTextColor(Color.WHITE);

        // 通过ShapeElement 给button 设置圆角
        ShapeElement shapeElement = new ShapeElement();
        // 设置颜色
        shapeElement.setRgbColor(new RgbColor(188,125,255));
        // 设置圆角的半径
        shapeElement.setCornerRadius(20);
        // 然后将圆角设置给button
        button.setBackground(shapeElement);
        // 设置 button 的内边距,从而增加 button 容器的大小
        button.setPadding(30,10,30,10);

        button.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                count++;
                text.setText("count:"+count);
            }
        });

        button.setLayoutConfig(layoutConfig);
        directionalLayout.addComponent(button);


        return directionalLayout;
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
}

在代码示例中,可以看到设置组件宽高的方法有两种

1、通过组件自身的 setWidth() / setHeight() 来设置

2、通过setLayoutConfig() 来设置组件的宽高。

这两种方法的区别是,后者还可以增加更多的布局属性来设置,例如:通过 alignment 设置水平居中的约束。另外,这两种方式设置的宽高以最后一次设置的为最终结果,它们的取值一致,可以是 MATCH_PARENT (表示组件大小将扩展为父组件允许的最大值,它将占据父组件方向上的剩余大小)、MATCH_CONTENT (表示组件大小与它内容占据的大小范围相适应)

通过XML实现点击按钮累加数字的案例:

XML声明布局的方式更加简便直观,每一个Component和ComponentContainer对象大部分属性都支持在XML中进行设置,它们都有各自的XML属性列表。某些属性仅适用于特定的组件,例如:只有Text组件支持 text_color 属性,其它组件如果添加了该属性,那么则会忽略。具有继承关系的组件子类将继承父类的属性列表,Component作为组件的基类,拥有各个组件常用的属性,比如:ID、宽度、高度等等

ID

ohos:id="$+id:text_helloworld"

在XML中使用上述表达式来声明一个组件的ID,它会在编译过程中生成一个常量。通过该ID可以获取到对应的组件,然后通过代码的方式来操作组件。另外,在DependentLayout(相对布局)布局中,组件之间需要通过ID来描述相对位置关系。

注意:设置ID时,请确保它的唯一性。

宽度、高度

ohos:height="match_parent"
ohos:width="match_content"

在XML中设置组件的宽高和代码中设置类似,它的取值为:

  • 具体的数值,例如:10 (表示10像素)、10vp(表示10虚拟像素)
  • MATCH_PARENT:表示组件大小将扩展为父组件允许的最大值,它将占据父组件方向上的剩余大小,在XML中用match_parent来表示
  • MATCH_CONTENT:表示组件大小与它的内容占据的大小范围相适应,在XML中用 match_content 来表示

案例效果图:

ability_layout.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:orientation="vertical"
    >

    <!--
    ohos:layout_alignment="horizontal_center" 表示Text组件在父元素中是水平方向居中显示的
    -->
    <Text
        ohos:id="$+id:text_count"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="horizontal_center"
        ohos:text="count:0"
        ohos:text_size="30fp"
        ohos:text_color="#000000"
        />

    <!--
    ohos:background_element="$graphic:background_button" 表示button组件的背景使用自定义的圆角背景
    -->
    <Button
        ohos:id="$+id:btn_count"
        ohos:width="match_content"
        ohos:height="match_content"
        ohos:top_margin="100vp"
        ohos:layout_alignment="horizontal_center"
        ohos:left_padding="30vp"
        ohos:top_padding="10vp"
        ohos:right_padding="30vp"
        ohos:bottom_padding="10vp"
        ohos:text_size="50fp"
        ohos:text="点我"
        ohos:text_color="#FFFFFF"
        ohos:background_element="$graphic:background_button"
        >

    </Button>

</DirectionalLayout>

按钮的自定义圆角背景如下:

<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">

    <!-- 自定义圆角背景作为Button的背景 -->

    <!--
    填充
    ohos:color="#BA7CFE" 表示背景的填充色
    -->
    <solid
        ohos:color="#BA7CFE"/>

    <!--
    角度
    ohos:radius="20" 表示圆角的半径
     -->
    <corners
        ohos:radius="20"
    ></corners>

</shape>

java辅助代码如下:

package com.example.helloword.slice;

import com.example.helloword.ResourceTable;
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.utils.LayoutAlignment;

public class LayoutAbilitySlice extends AbilitySlice {

    private int count = 0;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);

        super.setUIContent(ResourceTable.Layout_ability_layout);

        initView();
    }

    /*
    *
    * */
    private void initView() {
        // 通过 ID 获取对应的组件
        Text text = (Text) findComponentById(ResourceTable.Id_text_count);
        Button button = (Button) findComponentById(ResourceTable.Id_btn_count);
        // 给 Button 设置点击事件
        button.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                count++;
                text.setText("count:"+count);
            }
        });

    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
}

[/$]

如果觉得文章有帮助到你,可以扫描以下二维码
   请本文作者 喝一杯
pay_weixin pay_weixin

发表评论

电子邮件地址不会被公开。 必填项已用*标注