HarmonyOS学习路之开发篇—Java UI框架(自定义组件与布局 二)

简介: 自定义布局当Java UI框架提供的布局无法满足需求时,可以创建自定义布局,根据需求自定义布局规则

自定义布局

当Java UI框架提供的布局无法满足需求时,可以创建自定义布局,根据需求自定义布局规则

常用接口

Component类相关接口

image.png

ComponentContainer类相关接口


接口名称


作用


setArrangeListener


设置容器组件布局子组件的侦听器


onArrange


通知容器组件在布局时设置子组件的位置和大小


如何实现自定义布局

使用自定义布局,实现子组件自动换行功能。


自定义布局的使用效果

18e219ab98969f89be49450491e68103.png 

1. 创建自定义布局的类,并继承ComponentContainer,添加构造方法。

public class CustomLayout extends ComponentContainer {
    public CustomLayout(Context context) {
        this(context, null);
    }
    //如需支持xml创建自定义布局,必须添加该构造方法
    public CustomLayout(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }
}

2. 实现ComponentContainer.EstimateSizeListener接口,在onEstimateSize方法中进行测量。

public class CustomLayout extends ComponentContainer
    implements ComponentContainer.EstimateSizeListener {
    ...
    public CustomLayout(Context context, AttrSet attrSet) {
        ...
        setEstimateSizeListener(this);
    }
    @Override
    public boolean onEstimateSize(int widthEstimatedConfig, int heightEstimatedConfig) {
        invalidateValues();
        //通知子组件进行测量
        measureChildren(widthEstimatedConfig, heightEstimatedConfig);
        //关联子组件的索引与其布局数据
        for (int idx = 0; idx < getChildCount(); idx++) {
            Component childView = getComponentAt(idx);
            addChild(childView, idx, EstimateSpec.getSize(widthEstimatedConfig));
        }
        //测量自身
        measureSelf(widthEstimatedConfig, heightEstimatedConfig);
        return true;
    }
    private void measureChildren(int widthEstimatedConfig, int heightEstimatedConfig) {
        for (int idx = 0; idx < getChildCount(); idx++) {
            Component childView = getComponentAt(idx);
            if (childView != null) {
                LayoutConfig lc = childView.getLayoutConfig();
                int childWidthMeasureSpec;
                int childHeightMeasureSpec;
                if (lc.width == LayoutConfig.MATCH_CONTENT) {
                    childWidthMeasureSpec = EstimateSpec.getSizeWithMode(lc.width, EstimateSpec.NOT_EXCEED);
                } else if (lc.width == LayoutConfig.MATCH_PARENT) {
                    int parentWidth = EstimateSpec.getSize(widthEstimatedConfig);
                    int childWidth = parentWidth - childView.getMarginLeft() - childView.getMarginRight();
                    childWidthMeasureSpec = EstimateSpec.getSizeWithMode(childWidth, EstimateSpec.PRECISE);
                } else {
                    childWidthMeasureSpec = EstimateSpec.getSizeWithMode(lc.width, EstimateSpec.PRECISE);
                }
                if (lc.height == LayoutConfig.MATCH_CONTENT) {
                    childHeightMeasureSpec = EstimateSpec.getSizeWithMode(lc.height, EstimateSpec.NOT_EXCEED);
                } else if (lc.height == LayoutConfig.MATCH_PARENT) {
                    int parentHeight = EstimateSpec.getSize(heightEstimatedConfig);
                    int childHeight = parentHeight - childView.getMarginTop() - childView.getMarginBottom();
                    childHeightMeasureSpec = EstimateSpec.getSizeWithMode(childHeight, EstimateSpec.PRECISE);
                } else {
                    childHeightMeasureSpec = EstimateSpec.getSizeWithMode(lc.height, EstimateSpec.PRECISE);
                }
                childView.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }
    private void measureSelf(int widthEstimatedConfig, int heightEstimatedConfig) {
        int widthSpce = EstimateSpec.getMode(widthEstimatedConfig);
        int heightSpce = EstimateSpec.getMode(heightEstimatedConfig);
        int widthConfig = 0;
        switch (widthSpce) {
            case EstimateSpec.UNCONSTRAINT:
            case EstimateSpec.PRECISE:
                int width = EstimateSpec.getSize(widthEstimatedConfig);
                widthConfig = EstimateSpec.getSizeWithMode(width, EstimateSpec.PRECISE);
                break;
            case EstimateSpec.NOT_EXCEED:
                widthConfig = EstimateSpec.getSizeWithMode(maxWidth, EstimateSpec.PRECISE);
                break;
            default:
                break;
        }
        int heightConfig = 0;
        switch (heightSpce) {
            case EstimateSpec.UNCONSTRAINT:
            case EstimateSpec.PRECISE:
                int height = EstimateSpec.getSize(heightEstimatedConfig);
                heightConfig = EstimateSpec.getSizeWithMode(height, EstimateSpec.PRECISE);
                break;
            case EstimateSpec.NOT_EXCEED:
                heightConfig = EstimateSpec.getSizeWithMode(maxHeight, EstimateSpec.PRECISE);
                break;
            default:
                break;
        }
        setEstimatedSize(widthConfig, heightConfig);
    }
}

注意:

  1. 1.容器类组件在自定义测量过程不仅要测量自身,也要递归的通知各子组件进行测量。
  2. 2.测量出的大小需通过setEstimatedSize通知组件,并且必须返回true使测量值生效。

3. 测量时,需要确定每个子组件大小和位置的数据,并保存这些数据。

    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 + component.getMarginRight());
        maxHeight = Math.max(maxHeight, layout.positionY + layout.height + component.getMarginBottom());
    }

4. 实现ComponentContainer.ArrangeListener接口,在onArrange方法中排列子组件。

public class CustomLayout extends ComponentContainer
    implements ComponentContainer.EstimateSizeListener,
    ComponentContainer.ArrangeListener {
    ...
    public CustomLayout(Context context
, AttrSet attrSet
) {
        ...
        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;
    }
}

5. 在xml文件中创建此布局,并添加若干子组件。

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:orientation="vertical">
    <!--请根据实际包名与文件路径引入-->
    <com.huawei.harmonyosdemo.custom.CustomLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:background_element="#555555">
        <Text
            ohos:height="200"
            ohos:width="match_parent"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="match_parent * 200"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
        <Text
            ohos:height="100"
            ohos:width="300"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="item2"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
        <Text
            ohos:height="100"
            ohos:width="300"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="item3"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
        <Text
            ohos:height="100"
            ohos:width="300"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="item4"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
        <Text
            ohos:height="100"
            ohos:width="500"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="500 * 100"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
        <Text
            ohos:height="100"
            ohos:width="300"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="item6"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
        <Text
            ohos:height="600"
            ohos:width="600"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="600 * 600"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
        <Text
            ohos:height="100"
            ohos:width="300"
            ohos:background_element="#727272"
            ohos:margin="10"
            ohos:text="item8"
            ohos:text_alignment="center"
            ohos:text_color="white"
            ohos:text_size="40"/>
    </com.huawei.harmonyosdemo.custom.CustomLayout>
</DirectionalLayout>
相关文章
|
1月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
20天前
|
UED
「Mac畅玩鸿蒙与硬件40」UI互动应用篇17 - 照片墙布局
本篇将带你实现一个简单的照片墙布局应用,通过展示多张图片组成照片墙效果,用户可以点击图片查看其状态变化。
135 67
|
7天前
|
缓存 API 开发者
基于HarmonyOS 5.0 (Next)的一种面向多设备跨平台的高性能自适应布局能力研究和实现
随着万物互联时代的到来,操作系统作为连接设备、应用与用户体验的核心愈发重要。华为发布的HarmonyOS 5.0(Next)是一款完全自主的手机操作系统,实现了全栈自研,在技术架构和生态体验上进行了颠覆性升级。本文聚焦于基于HarmonyOS 5.0(Next)实现多设备跨平台的高性能自适应布局能力,通过深入分析其技术特点和生态优势,结合开发实践探讨如何利用自适应布局和响应式布局技术,确保应用在多种设备上提供一致且优质的用户体验。研究将基于HarmonyOS 5.0(Next)的分布式能力和ArkTS编程语言,展示多设备跨平台环境下实现高性能自适应布局的方法,推动鸿蒙生态的发展。
65 16
基于HarmonyOS 5.0 (Next)的一种面向多设备跨平台的高性能自适应布局能力研究和实现
|
5天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
24天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
【鸿蒙 HarmonyOS】UI 组件 ( 拖动条 Slider 组件 )
【鸿蒙 HarmonyOS】UI 组件 ( 拖动条 Slider 组件 )
404 0
【鸿蒙 HarmonyOS】UI 组件 ( 拖动条 Slider 组件 )
|
XML 前端开发 Java
HarmonyOS学习路之开发篇—Java UI框架(自定义组件与布局 一)
HarmonyOS提供了一套复杂且强大的Java UI框架,其中Component提供内容显示,是界面中所有组件的基类。ComponentContainer作为容器容纳Component或ComponentContainer对象,并对它们进行布局。
|
XML JavaScript 前端开发
HarmonyOS学习路之开发篇—Java UI框架(基础组件说明【三】)
接上一篇文章… 上两篇文章已经介绍了大部分的Java UI组件 ,因为时间关系把一个内容分为了三个部分,这是最后一篇组件的介绍。分别介绍ListContainer、ScrollView和WebView。
|
XML Java 数据格式
HarmonyOS学习路之开发篇—Java UI框架(基础组件说明【二】)
二、分类说明 ④Picker Picker提供了滑动选择器,允许用户从预定义范围中进行选择。 Picker的自有XML属性见下表:
|
XML Java Android开发
HarmonyOS学习路之开发篇—— Java UI框架(基础组件说明【一】)
HarmonyOS的常用组件一般在resources/base/layout下的xml文件中声明,然后在AbilitySlice中通过super.setUIContent(ResourceTable.某layout布局的文件名)来加载布局。在AbilitySlice中通过super.findComponentById(ResourceTable.组件的id)获取组件,获取成功后就可以对该组件进行操作,如添加监听,设置内容等。当然也可以通过代码动态的使用控件。