乐趣区

关于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

退出移动版