关于harmonyos:410HarmonyOS鸿蒙开发自定义布局

4.10【HarmonyOS鸿蒙开发】自定义布局

作者:韩茹

公司:程序咖(北京)科技有限公司

鸿蒙巴士专栏作家

当Java UI框架提供的布局无奈满足设计需要时,能够创立自定义布局,依据需要自定义布局规定。

一、罕用接口

Component类相干接口

接口名称 作用
setEstimateSizeListener 设置测量组件的侦听器。
onEstimateSize 测量组件的大小以确定宽度和高度。
setEstimatedSize 将测量的宽度和高度设置给组件。
EstimateSpec.getChildSizeWithMode 基于指定的大小和模式为子组件创立度量标准。
EstimateSpec.getSize 从提供的度量标准中提取大小。
EstimateSpec.getMode 获取该组件的显示模式。
arrange 绝对于容器组件设置组件的地位和大小。

ComponentContainer类相干接口

接口名称 作用
setArrangeListener 设置容器组件布局子组件的侦听器。
onArrange 告诉容器组件在布局时设置子组件的地位和大小。

二、如何实现自定义布局

应用自定义布局,将各子组件摆放到指定的地位。

  1. 创立自定义布局的类,并继承ComponentContainer,增加构造方法。
public class CustomLayout extends ComponentContainer {
    public CustomLayout(Context context) {
        super(context);
    }
}
  1. 实现ComponentContainer.EstimateSizeListener接口,在onEstimateSize办法中进行测量。
public class CustomLayout extends ComponentContainer
    implements ComponentContainer.EstimateSizeListener {

    ...

    public CustomLayout(Context context) {

        ...
        setEstimateSizeListener(this);
    }

    @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);
    }
}
  • 注意事项

    1. 容器类组件在自定义测量过程不仅要测量本身,也要递归的告诉各子组件进行测量。
    2. 测量出的大小需通过setEstimatedSize设置给组件,并且必须返回true使测量值失效。
  1. 测量时,须要确定每个子组件大小和地位的数据,并保留这些数据。
    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<>();

    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();
    }

    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);
    }
  1. 实现ComponentContainer.ArrangeListener接口,在onArrange办法中排列子组件。
public class CustomLayout extends ComponentContainer
    implements ComponentContainer.EstimateSizeListener,
    ComponentContainer.ArrangeListener {

    ...
    
    public CustomLayout(Context context) {

        ...
        setArrangeListener(this);
    }
    @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. 在onStart办法中增加此布局,在布局中增加若干子组件,并在界面中显示。
package com.example.hanrucustomlayout.slice;

import ohos.aafwk.ability.AbilitySlice;
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 MainAbilitySlice extends AbilitySlice {


    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
//        super.setUIContent(ResourceTable.Layout_ability_main);

        DirectionalLayout myLayout= new DirectionalLayout(getContext());
        DirectionalLayout.LayoutConfig config = new DirectionalLayout.LayoutConfig(
                DirectionalLayout.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_PARENT);
        myLayout.setLayoutConfig(config);
        CustomLayout customLayout = new CustomLayout(this);
        for (int idx = 0; idx < 15; idx++) {
            System.out.println("--->"+idx);
            customLayout.addComponent(getComponent(idx + 1));
        }
        ShapeElement shapeElement = new ShapeElement();
        RgbColor COLOR_LAYOUT_BG = new RgbColor(85,85,85);
        shapeElement.setRgbColor(COLOR_LAYOUT_BG);
        customLayout.setBackground(shapeElement);
        DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(DirectionalLayout.LayoutConfig.MATCH_PARENT,
                DirectionalLayout.LayoutConfig.MATCH_CONTENT);
        customLayout.setLayoutConfig(layoutConfig);
        myLayout.addComponent(customLayout);
        super.setUIContent(myLayout);
    }
    //创立子组件
    private Component getComponent(int idx) {
        Button button = new Button(getContext());
        ShapeElement shapeElement = new ShapeElement();
        RgbColor COLOR_BTN_BG = new RgbColor(114,114,114);
        shapeElement.setRgbColor(COLOR_BTN_BG);
        button.setBackground(shapeElement);
        button.setTextColor(Color.WHITE);
        DirectionalLayout.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(300, 100);
        if (idx == 1) {
            layoutConfig = new DirectionalLayout.LayoutConfig(1080, 200);
            button.setText("1080 * 200");
        } else if (idx == 6) {
            layoutConfig = new DirectionalLayout.LayoutConfig(500, 100);
            button.setText("500 * 100");
        } else if (idx == 8) {
            layoutConfig = new DirectionalLayout.LayoutConfig(600, 600);
            button.setText("600 * 600");
        } else {
            button.setText("Item" + idx);
        }
        layoutConfig.setMargins(10, 10, 10, 10);
        button.setLayoutConfig(layoutConfig);
        return button;
    }
}

效果图:

更多内容:

1、社区:鸿蒙巴士https://www.harmonybus.net/

2、公众号:HarmonyBus

3、技术交换QQ群:714518656

4、视频课:https://www.chengxuka.com

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理