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