JAVA自定义布局 原创 精华

cqyin
发布于 2021-8-26 18:03
1.8w浏览
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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

布局及其子组件测量

    这一步我们需要弄懂三种测试模式,不同测量模式,测量结果也不同:
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;
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

子组件位置排放

我们需要继承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;
        }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

总结

实现自定义布局,测量组件及其子组件宽高、根据测量宽高来排放子组件的位置这两步,均需要在构造函数添加其监听。
完整的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;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.

完整的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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
已于2021-8-26 18:03:48修改
4
收藏 2
回复
举报
4
2


回复
    相关推荐