网站首页 > 知识剖析 正文
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;
}
}
效果图:
猜你喜欢
- 2024-11-22 UI: 为啥你这个页面边框1px看起来这么粗?
- 2024-11-22 开源推荐!一款支持pc端&移动端的滑动验证码组件
- 2024-11-22 Android 约束布局
- 2024-11-22 Android常用布局总结之(LinearLayout、GridLayout等4种)
- 2024-11-22 Visual Studio跨平台开发实战(4):Xamarin Android控制项介绍
- 2024-11-22 开源版SearchGPT来了,两张3090就可复现,超越Perplexity付费版
- 2024-11-22 不得不佩服,美观小巧的网页内容编辑器——ContentTools
- 2024-11-22 HTML中一些常见的特殊标签样式属性
- 2024-11-22 NBA勇士队的五个历史荣誉,你见证过几个?揭晓勇士队的球队文化
- 2024-11-22 90%不知道的css常识:元素纵向百分比是相对于宽度不是高度
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)