鸿蒙应用开发 | 最牛逼的 自定义 布局

大家好,我是你们的朋友 朋哥,今天聊聊自定义布局,自定义布局就是当前系统布局不满足时的布局方式。

上一篇原创文章 解读了 鸿蒙开发布局的 自适应盒子布局(AdaptiveBoxLayout),通过设置布局规则来调整布局,到目前,布局开发基本完成了。

有点激动接下来的组件,目录已经准备好,大家跟进步伐。

布局完成肯定要总结一下,其实布局是开发ui的关键,需要开发者做到,看到界面就能明白需要用上面布局做开发。

为了更好的理解布局的作用,今天来开发一下自定义布局。

简介:

来看看官方对自定义的说明。

HarmonyOS提供了一套复杂且强大的Java UI框架,其中ComponentContainer作为容器容纳Component或ComponentContainer对象,并对它们进行布局。

Java UI框架也提供了一部分Component和ComponentContainer的具体子类,即常用的组件(比如:Text、Button、Image等)和常用的布局(比如:DirectionalLayout、DependentLayout等)。如果现有的组件和布局无法满足设计需求,例如仿遥控器的圆盘按钮、可滑动的环形控制器等,可以通过自定义组件和自定义布局来实现。

自定义布局是由开发者定义的具有特定布局规则的容器类组件,通过扩展ComponentContainer或其子类实现,可以将各子组件摆放到指定的位置,也可响应用户的滑动、拖拽等事件。

总结起来就是:当Java UI框架提供的布局无法满足设计需求时,可以创建自定义布局,根据需求自定义布局规则。

相关接口

Component类相关接口

ComponentContainer类相关接口

学习开发动手敲一下代码,才能理解的更深入。

1,创建项目

首先创建一个鸿蒙项目(Java版),关于创建项目 有新手不知道怎么创建的,可以查看第一篇文章 鸿蒙开发入门

2,

创建自定义布局的类,并继承ComponentContainer

实现ComponentContainer.EstimateSizeListener接口,在onEstimateSize方法中进行测量。

代码如下:

package com.example.customlayout;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.app.Context;
import java.util.HashMap;
import java.util.Map;

public class CustomLayout extends ComponentContainer implements ComponentContainer.EstimateSizeListener
,ComponentContainer.ArrangeListener{
    private int xx = 0;

    private int yy = 0;

    private int maxWidth = 0;

    private int maxHeight = 0;

    private int lastHeight = 0;

    // 子组件索引与其布局数据的集合
    private final Map<Integer, Layout> axis = new HashMap<>();

    public CustomLayout(Context context) {
        super(context);
        setEstimateSizeListener(this);// 设置监听
        setArrangeListener(this);// 设置子组件排列监听
    }
    private static class Layout {
        int positionX = 0;
        int positionY = 0;
        int width = 0;
        int height = 0;
    }

    private void invalidateValues() {
        xx = 0;
        yy = 0;
        maxWidth = 0;
        maxHeight = 0;
        axis.clear();
    }

    // 实现ComponentContainer.EstimateSizeListener接口,在onEstimateSize方法中进行测量
    @Override
    public boolean onEstimateSize(int widthEstimatedConfig, int heightEstimatedConfig) {

        // 通知子组件进行测量
        measureChildren(widthEstimatedConfig, heightEstimatedConfig);
        int width = Component.EstimateSpec.getSize(widthEstimatedConfig);

        // 关联子组件的索引与其布局数据
        for (int idx = 0; idx < getChildCount(); idx++) {
            Component childView = getComponentAt(idx);
            addChild(childView, idx, width);
        }

        setEstimatedSize(
                Component.EstimateSpec.getChildSizeWithMode(maxWidth, widthEstimatedConfig, 0),
                Component.EstimateSpec.getChildSizeWithMode(maxHeight, heightEstimatedConfig, 0));
        return true;
    }

    private void measureChildren(int widthEstimatedConfig, int heightEstimatedConfig) {
        for (int idx = 0; idx < getChildCount(); idx++) {
            Component childView = getComponentAt(idx);
            if (childView != null) {
                measureChild(childView, widthEstimatedConfig, heightEstimatedConfig);
            }
        }
    }

    private void measureChild(Component child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
        ComponentContainer.LayoutConfig lc = child.getLayoutConfig();
        int childWidthMeasureSpec = EstimateSpec.getChildSizeWithMode(
                lc.width, parentWidthMeasureSpec, EstimateSpec.UNCONSTRAINT);
        int childHeightMeasureSpec = EstimateSpec.getChildSizeWithMode(
                lc.height, parentHeightMeasureSpec, EstimateSpec.UNCONSTRAINT);
        child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    // 测量时,需要确定每个子组件大小和位置的数据,并保存这些数据
    private void addChild(Component component, int id, int layoutWidth) {
        Layout layout = new Layout();
        layout.positionX = xx + component.getMarginLeft();
        layout.positionY = yy + component.getMarginTop();
        layout.width = component.getEstimatedWidth();
        layout.height = component.getEstimatedHeight();
        if ((xx + layout.width) > layoutWidth) {
            xx = 0;
            yy += lastHeight;
            lastHeight = 0;
            layout.positionX = xx + component.getMarginLeft();
            layout.positionY = yy + component.getMarginTop();
        }
        axis.put(id, layout);
        lastHeight = Math.max(lastHeight, layout.height + component.getMarginBottom());
        xx += layout.width + component.getMarginRight();
        maxWidth = Math.max(maxWidth, layout.positionX + layout.width);
        maxHeight = Math.max(maxHeight, layout.positionY + layout.height);
    }
    // 实现ComponentContainer.ArrangeListener接口,在onArrange方法中排列子组件
    @Override
    public boolean onArrange(int left, int top, int width, int height) {

        // 对各个子组件进行布局
        for (int idx = 0; idx < getChildCount(); idx++) {
            Component childView = getComponentAt(idx);
            Layout layout = axis.get(idx);
            if (layout != null) {
                childView.arrange(layout.positionX, layout.positionY, layout.width, layout.height);
            }
        }
        return true;
    }
}
复制代码

说明:

1,实现ComponentContainer.EstimateSizeListener接口,在onEstimateSize方法中进行测量

2,注意事项:

容器类组件在自定义测量过程不仅要测量自身,也要递归的通知各子组件进行测量。

测量出的大小需通过setEstimatedSize设置给组件,并且必须返回true使测量值生效。

测量时,需要确定每个子组件大小和位置的数据,并保存这些数据

3,实现ComponentContainer.ArrangeListener接口,在onArrange方法中排列子组件
4,自定义布局功能就是实现组件的自适应显示,按照线性布局排列。

3,自定义布局的使用

package com.example.customlayout;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;

public class MainAbility extends Ability {
    @Override
    public void onStart(Intent intent) {
        DirectionalLayout myLayout = new DirectionalLayout(getContext());
        CustomLayout customLayout = new CustomLayout(this);// 自定义布局 设置线性布局,多个组件的宽度大于屏幕的宽度会自动换行显示
        for (int idx = 0; idx < 6; idx++) {
            customLayout.addComponent(getComponent(idx + 1));
        }
        // 自定义布局背景颜色
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(RgbColor.fromArgbInt(Color.getIntColor("#4169E1")));
        customLayout.setBackground(shapeElement);
        myLayout.addComponent(customLayout); //自定义布局添加到布局DirectionalLayout中
        super.setUIContent(myLayout);// 将布局添加到 根布局中显示
    }

    //创建动态子组件
    private Component getComponent(int idx) {
        Button button = new Button(getContext()); // 创建个按钮
        // 设置按钮颜色
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(RgbColor.fromArgbInt(Color.getIntColor("#FF1493")));
        button.setBackground(shapeElement); 
        button.setTextColor(Color.WHITE);// 按钮字体颜色
        button.setTextSize(40); // 设置字体大小
        // 设置每个按钮布局,包括大小和内容
       DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(300, 100);
        if (idx == 1) {
            layoutConfig = new DirectionalLayout.LayoutConfig(1080, 200);
            button.setText("自定义组件1");
        } else if (idx == 2) {
            layoutConfig = new DirectionalLayout.LayoutConfig(500, 200);
            button.setText("自定义组件2");
        } else if (idx == 3) {
            layoutConfig = new DirectionalLayout.LayoutConfig(400, 400);
            button.setText("自定义组件3");
        }  else if (idx == 4) {
            layoutConfig = new DirectionalLayout.LayoutConfig(50, 200);
            button.setText("自定义组件4");
        }  else if (idx == 5) {
            layoutConfig = new DirectionalLayout.LayoutConfig(100, 200);
            button.setText("自定义组件5");
        } else {
            button.setText("自定义组件6");
        }
        layoutConfig.setMargins(10, 10, 10, 10);
        button.setLayoutConfig(layoutConfig);
        return button;
    }
}
复制代码

1,在 MainAbility中实现自定义布局的调用,并且创建动态组件,显示组件的大小和样式。

自定义布局是因为当前布局不能满足需要或者为了统一布局 从新做的布局规则。
需要重写

ComponentContainer

实现需要的接口完成布局的设置。

运行效果图:

图片

老规矩 代码不能少,要不然小伙伴该说我小气了。
代码连接: gitee.com/codegrowth/…

关注公众号【程序员漫话编程】,后台回复【鸿蒙】,即可获取上千鸿蒙开源组件~

原创不易,有用就关注一下。要是帮到了你 就给个三连吧,多谢支持。

觉得不错的小伙伴,记得帮我 点个赞和关注哟,笔芯笔芯~**

作者:码工

有问题请留言或者私信,可以 微信搜索:程序员漫话编程,关注公众号获得更多免费学习资料。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享