自定义布局

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

常用接口

  • Component类相关接口

    | 接口名称 | 作用 | | --------------------------------- | ------------------------------------------ | | setEstimateSizeListener | 设置测量组件的侦听器。 | | onEstimateSize | 测量组件的大小以确定宽度和高度。 | | setEstimatedSize | 将测量的宽度和高度设置给组件。 | | EstimateSpec.getChildSizeWithMode | 基于指定的大小和模式为子组件创建度量规范。 | | EstimateSpec.getSize | 从提供的度量规范中提取大小。 | | EstimateSpec.getMode | 获取该组件的显示模式。 | | arrange | 相对于容器组件设置组件的位置和大小。 |

  • ComponentContainer类相关接口

    | 接口名称 | 作用 | | ------------------ | -------------------------------------------- | | setArrangeListener | 设置容器组件布局子组件的侦听器。 | | onArrange | 通知容器组件在布局时设置子组件的位置和大小。 |

如何实现自定义布局

使用自定义布局,将各子组件摆放到指定的位置。

图1 自定义布局的使用效果 img

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

    public class CustomLayout extends ComponentContainer {    public CustomLayout(Context context) {        super(context);    }}
    
  2. 实现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);    }}
    
    • 注意事项
      1. 容器类组件在自定义测量过程不仅要测量自身,也要递归的通知各子组件进行测量。
      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);        maxHeight = Math.max(maxHeight, layout.positionY + layout.height);    }
    
  4. 实现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;    }}
    
  5. 在onStart方法中添加此布局,在布局中添加若干子组件,并在界面中显示。

    @Overrideprotected void onStart(Intent intent) {    super.onStart(intent);    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++) {        customLayout.addComponent(getComponent(idx + 1));    }    ShapeElement shapeElement = new ShapeElement();    shapeElement.setRgbColor(COLOR_LAYOUT_BG);    customLayout.setBackground(shapeElement);    LayoutConfig layoutConfig = new LayoutConfig(LayoutConfig.MATCH_PARENT,        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();    shapeElement.setRgbColor(COLOR_BTN_BG);    button.setBackground(shapeElement);    button.setTextColor(Color.WHITE);    LayoutConfig layoutConfig = new LayoutConfig(300, 100);    if (idx == 1) {         layoutConfig = new LayoutConfig(1080, 200);        button.setText("1080 * 200");    } else if (idx == 6) {         layoutConfig = new LayoutConfig(500, 100);        button.setText("500 * 100");    } else if (idx == 8) {         layoutConfig = new LayoutConfig(600, 600);        button.setText("600 * 600");    } else {        button.setText("Item" + idx);    }    layoutConfig.setMargins(10, 10, 10, 10);    button.setLayoutConfig(layoutConfig);    return button;}
    

results matching ""

    No results matching ""