4.10【
HarmonyOS鸿蒙开发】自定义布局
作者:韩茹
公司:程序咖(北京)科技有限公司
鸿蒙巴士专栏作家
当Java UI框架提供的布局无法满足设计需求时,可以创建自定义布局,根据需求自定义布局规则。
一、常用接口 Component类相关接口
接口名称
| 作用
|
setEstimateSizeListener
| 设置测量组件的侦听器。
|
onEstimateSize
| 测量组件的大小以确定宽度和高度。
|
setEstimatedSize
| 将测量的宽度和高度设置给组件。
|
EstimateSpec.getChildSizeWithMode
| 基于指定的大小和模式为子组件创建度量规范。
|
EstimateSpec.getSize
| 从提供的度量规范中提取大小。
|
EstimateSpec.getMode
| 获取该组件的显示模式。
|
arrange
| 相对于容器组件设置组件的位置和大小。
|
ComponentContainer类相关接口
接口名称
| 作用
|
setArrangeListener
| 设置容器组件布局子组件的侦听器。
|
onArrange
| 通知容器组件在布局时设置子组件的位置和大小。
|
二、如何实现自定义布局 使用自定义布局,将各子组件摆放到指定的位置。
- 创建自定义布局的类,并继承ComponentContainer,添加构造方法。
public class CustomLayout extends ComponentContainer { public CustomLayout(Context context) { super(context); }}
- 实现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); }}
- 注意事项容器类组件在自定义测量过程不仅要测量自身,也要递归的通知各子组件进行测量。测量出的大小需通过setEstimatedSize设置给组件,并且必须返回true使测量值生效。
- 测量时,需要确定每个子组件大小和位置的数据,并保存这些数据。
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); }
- 实现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; }}
- 在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; }}
效果图:
花粉社群VIP加油站
恭喜你,领取到一张面值 0 元的优惠券
只有购买全集内容 0.00 元,才可抵扣使用。
有效期截止于:2020-12-12 23:59
是否立即使用?