JAVA自定义布局 原创 精华

发布于 2021-8-26 18:03
浏览
2收藏

介绍

自定义布局有两种:继承原有布局拓展、自我设置布局。前一种比较简单,这篇文章主要讲的是自我设置布局。这个设置的是一个靠左纵向展示子组件的布局,比较特殊的是如果父布局有可展示空间,子组件的高度比自身高度高,布局本身会使用子组件的高度。

实现步骤

1、继承布局类,实现构造方法
2、测量布局,这里不仅要对容器组件进行测量,还需要对其子组件进行测量
3、根据测量结果进行子组件位置排放

布局基类继承

继承布局的基类ComponentContainer,这里如果有自定义属性的设置,参照上一篇文章“JAVA自定义组件”中的自定义属性,这里不做描述。具体代码如下:

public LinearLayout(Context context) {
        super(context,null);
    }

public LinearLayout(Context context, AttrSet attrSet) {
        super(context, attrSet);
        setEstimateSizeListener(this);
        setArrangeListener(this);
    }

布局及其子组件测量

    这一步我们需要弄懂三种测试模式,不同测量模式,测量结果也不同:
1、UNCONSTRAINT,父组件对子组件没有约束,表示子组件可以任意大小。例如,带滚动条的父布局,组件设置多大就是多大。
2、PRECISE,父组件已确定子组件的大小。例如,组件设置match_parent。
3、NOT_EXCEED,已为子组件确定了最大大小,子组件不能超过指定大小。例如,组件设置match_content,此时使用组件默认大小。
    我们需要继承Component.EstimateSizeListener接口,实现onEstimateSize方法来完成测量,并把测量结果使用setEstimatedSize对组件进行设置。
1、获取xml配置的宽高、模式。
2、对子组件进行测量,将子组件测量结果使用setEstimatedSize对组件进行设置。
3、将子组件的测试结果记录下来,用于后面的位置排放。
4、根据子组件测试结果,改变布局组件的xml配置,并使用setEstimatedSize对布局组件进行设置。
具体代码如下:

public boolean onEstimateSize(int widthEstimateSize, int heightEstimateSize) {
        //获取xml配置的宽高、模式
        width = EstimateSpec.getSize(widthEstimateSize);
        height = EstimateSpec.getSize(heightEstimateSize);
        int widthMode = EstimateSpec.getMode(widthEstimateSize);
        int heightMode = EstimateSpec.getMode(heightEstimateSize);

        //对子组件进行测量,将子组件测量结果使用setEstimatedSize对组件进行设置
        estimateSizeChildren();
        //将子组件的测试结果记录下来,用于后面的位置排放
        addView();

        //根据子组件测试结果,改变布局组件的xml配置
        if(heightMode == EstimateSpec.NOT_EXCEED || height<childrenHeight){
            height = childrenHeight;
        }
        setEstimatedSize(EstimateSpec.getSizeWithMode(width,widthMode),EstimateSpec.getSizeWithMode(height,heightMode));
        return true;
    }

子组件位置排放

我们需要继承ComponentContainer.ArrangeListener接口,实现onArrange方法来完成子组件位置排放。这里使用了上面测量的结果childrenView(子组件位置、宽高信息)来排放子组件的位置。
具体代码如下:

    public boolean onArrange(int i, int i1, int i2, int i3) {
            for(int count=0;count<getChildCount();count++){
                Component component = getComponentAt(count);
                Lay lay = childrenView.get(count);
                if(component != null && lay != null) {
                    component.arrange(lay.x,lay.y,lay.width,lay.height);
                }
            }
            return true;
        }

总结

实现自定义布局,测量组件及其子组件宽高、根据测量宽高来排放子组件的位置这两步,均需要在构造函数添加其监听。
完整的java代码如下:

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.util.ArrayList;
import java.util.List;

public class LinearLayout extends ComponentContainer implements Component.EstimateSizeListener,ComponentContainer.ArrangeListener {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo");
    private int width = 0;
    private int height = 0;
    private int heightMode = 0;
    private int childrenHeight = 0;
    private List<Lay> childrenView = new ArrayList<>();

    private class Lay{
        int x = 0;
        int y = 0;
        int width = 0;
        int height = 0;
    }

    public LinearLayout(Context context) {
        super(context,null);
    }

    public LinearLayout(Context context, AttrSet attrSet) {
        super(context, attrSet);
        setEstimateSizeListener(this);
        setArrangeListener(this);
    }

    @Override
    public boolean onEstimateSize(int widthEstimateSize, int heightEstimateSize) {
        //获取xml配置的宽高、模式
        width = EstimateSpec.getSize(widthEstimateSize);
        height = EstimateSpec.getSize(heightEstimateSize);
        int widthMode = EstimateSpec.getMode(widthEstimateSize);
        int heightMode = EstimateSpec.getMode(heightEstimateSize);

        //对子组件进行测量,将子组件测量结果使用setEstimatedSize对组件进行设置
        estimateSizeChildren();
        //将子组件的测试结果记录下来,用于后面的位置排放
        addView();

        //根据子组件测试结果,改变布局组件的xml配置
        if(heightMode == EstimateSpec.NOT_EXCEED || height<childrenHeight){
            height = childrenHeight;
        }
        setEstimatedSize(EstimateSpec.getSizeWithMode(width,widthMode),EstimateSpec.getSizeWithMode(height,heightMode));
        return true;
    }

    private void addView(){
        childrenView.clear();
        childrenHeight = 0;
        for(int i = 0;i < getChildCount();i++) {
            Component view = getComponentAt(i);

            Lay lay = new Lay();
            lay.x = view.getMarginLeft();
            lay.y = childrenHeight+view.getMarginTop();
            lay.width = view.getEstimatedWidth();
            lay.height = view.getEstimatedHeight();
            childrenView.add(lay);

            childrenHeight += lay.height +view.getMarginBottom()+view.getMarginTop();
        }
    }

    public void estimateSizeChildren(){
        for(int i=0;i<getChildCount();i++){
            Component view = getComponentAt(i);
            if(view != null ) {
                int childWidth = 0;
                int childHeight = 0;
                LayoutConfig config = view.getLayoutConfig();
                if (config.width == LayoutConfig.MATCH_PARENT) {
                    childWidth = EstimateSpec.getSizeWithMode(width-view.getMarginLeft()-view.getMarginRight(),EstimateSpec.PRECISE);
                } else if (config.width == LayoutConfig.MATCH_CONTENT) {
                    childWidth = EstimateSpec.getSizeWithMode(config.width,EstimateSpec.NOT_EXCEED);
                } else {
                    childWidth = EstimateSpec.getSizeWithMode(config.width,EstimateSpec.PRECISE);
                }

                if (config.height == LayoutConfig.MATCH_PARENT) {
                    childHeight = EstimateSpec.getSizeWithMode(height-view.getMarginTop()-view.getMarginBottom(),EstimateSpec.PRECISE);
                } else if (config.height == LayoutConfig.MATCH_CONTENT) {
                    childHeight = EstimateSpec.getSizeWithMode(config.height,EstimateSpec.NOT_EXCEED);
                } else {
                    childHeight = EstimateSpec.getSizeWithMode(config.height,EstimateSpec.PRECISE);
                }

                view.estimateSize(childWidth,childHeight);
            }
        }
    }


    @Override
    public boolean onArrange(int i, int i1, int i2, int i3) {
        for(int count=0;count<getChildCount();count++){
            Component component = getComponentAt(count);
            Lay lay = childrenView.get(count);
            if(component != null && lay != null) {
                component.arrange(lay.x,lay.y,lay.width,lay.height);
            }
        }
        return true;
    }
}

完整的xml代码如下:

<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:orientation="vertical">
    <com.example.javacustomcomponent.component.LinearLayout
        ohos:height="200vp"
        ohos:width="match_parent"
        ohos:background_element="#d2691e">
        <Text
            ohos:height="100vp"
            ohos:width="100vp"
            ohos:text="11111111122"
            ohos:text_color="#000000"
            ohos:text_size="50fp"
            ohos:background_element="#ffffff"></Text>
        <Text
            ohos:height="500vp"
            ohos:width="120vp"
            ohos:text="11111111122"
            ohos:text_color="#ffffff"
            ohos:background_element="#000000"
            ohos:text_size="50fp"></Text>
    </com.example.javacustomcomponent.component.LinearLayout>
</DirectionalLayout>

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-8-26 18:03:48修改
4
收藏 2
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐