摆动自定义布局 - 组件包装和高度计算

时间:2012-11-11 13:10:36

标签: java swing layout

我正在尝试创建自定义布局,这允许我以百分比指定组件的宽度,并基于该百分比宽度指定布局组件。以下是我最终的实现。

我遇到的问题是,最内部面板之一的计算宽度不足以将其所有组件保存在一行中,而下面的实现将它们包装到下一行,但父级的高度[全部]层次结构中的容器]固定为某些像素[在我的例子中我使用了40px],并且它不允许包装的组件显示。

请你建议一种解决方法......

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

import javax.swing.BoxLayout;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;


/**
 * @author Rakesh.A
 *
 */
public class Example extends JPanel {

public Example() {
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    for (int i = 0; i < 1; i++) {
        JPanel row = new JPanel();
        row.setLayout(new PercentageWidthLayout(5, 5));

        JPanel column1 = new JPanel();
        column1.setOpaque(true);
        column1.setBackground(Color.white);

        JPanel column2 = createColumn2();

        row.add(column1, new MyConstraints(15, false)); // uses 15% of the available size
        row.add(column2, new MyConstraints(50, false, true, true)); // uses 50% of the available size and wraps its contents
        row.add(new JPanel(), new MyConstraints(25, false)); // uses 25% of the available size
        add(row);
    }
}

private JPanel createColumn2() {
    JPanel column = new JPanel();
    column.setOpaque(true);
    column.setBackground(Color.green);

    column.setLayout(new PercentageWidthLayout(3, 3, 35));
    // total percentage is 100% for all the below components
    column.add(new MyComponent(30, 28), new MyConstraints(20, true, false, true));
    column.add(new MyComponent(30, 28), new MyConstraints(10, true, false, true));
    column.add(new MyComponent(30, 28), new MyConstraints(20, true, false, true));
    column.add(new MyComponent(30, 28), new MyConstraints(20, true, false, true));
    column.add(new MyComponent(30, 28), new MyConstraints(10, true, false, true));
    column.add(new MyComponent(30, 28), new MyConstraints(10, true, false, true));
    column.add(new MyComponent(30, 28), new MyConstraints(10, true, false, true));
    return column;
}

public static void main(final String[] args) {
    JDialog dialog = new JDialog();
    dialog.setSize(500, 150);
    Example content = new Example();
    JScrollPane scrl = new JScrollPane(content, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    dialog.getContentPane().add(scrl);
    dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
    dialog.setVisible(true);
}

public static class MyComponent extends JPanel {
    private Dimension minSize;
    public MyComponent(final int minWidth, final int minHeight) {
        minSize = new Dimension(minWidth, minHeight);

        setOpaque(true);
        setBackground(Color.yellow);

        add(new JLabel("Block"));
    }

    @Override
    public Dimension getPreferredSize() {
        return minSize;
    }

    @Override
    public Dimension getMaximumSize() {
        return minSize;
    }
    @Override
    public Dimension getMinimumSize() {
        return minSize;
    }
}

public static class PercentageWidthLayout implements LayoutManager2 {
    private LinkedHashMap<Component, MyConstraints> components;

    private final int leftMargin;
    private final int topMargin;
    private final int rowHeight;

    // default size of the block
    public static final Dimension minimumSize = new Dimension(10, 40);
    public static final Dimension preferredSize = new Dimension(100, 40);

    // default left margin between components
    public static final int defaultLeftMargin = 5;
    // default bottom margin between components
    public static final int defaultTopMargin = 5;
    // default row height
    public static final int defaultRowHeight = 0;

    public PercentageWidthLayout() {
        this(defaultLeftMargin, defaultTopMargin);
    }

    public PercentageWidthLayout(final int leftMargin, final int topMargin) {
        this(leftMargin, topMargin, defaultRowHeight);
    }

    public PercentageWidthLayout(final int leftMargin, final int topMargin, final int rowHeight) {
        this.leftMargin = leftMargin;
        this.topMargin = topMargin;
        this.rowHeight = rowHeight;

        components = new LinkedHashMap<Component, MyConstraints>();
    }

    @Override
    public Dimension preferredLayoutSize(final Container parent) {
        int maxX = 0;
        int maxY = 0;
        for (Entry<Component, MyConstraints> compEntry : components.entrySet()) {
            Rectangle bounds = compEntry.getKey().getBounds();
            maxX = Math.max(maxX, (int) bounds.getMaxX());
            maxY = Math.max(maxY, (int) bounds.getMaxY());
        }
        if (maxX == 0 || maxY == 0) {
            return preferredSize;
        }
        return new Dimension(maxX, maxY);
    }

    @Override
    public Dimension minimumLayoutSize(final Container parent) {
        return minimumSize;
    }

    @Override
    public Dimension maximumLayoutSize(final Container target) {
        return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    @Override
    public void layoutContainer(final Container parent) {
        synchronized (parent.getTreeLock()) {
            // validate total percentage
            validatePercentages();
            // calculate available width & height for the components
            Insets insets = parent.getInsets();
            // available width after removing border space
            int maxClientWidth = parent.getWidth() - insets.left - insets.right;
            // calculated available width for the components
            int clientWidth = maxClientWidth - (parent.getComponents().length * leftMargin);
            // calculated available height for the components
            int clientHeight = ((rowHeight > 0) ? rowHeight : preferredSize.height) - insets.top - insets.bottom - topMargin * 2;

            // layout the components
            int x = insets.left + leftMargin;
            int y = insets.top + topMargin;

            if (clientWidth > 0 && clientHeight > 0) {
                for (Component component : parent.getComponents()) {
                    // get the constraints to be applied
                    MyConstraints constraints = components.get(component);
                    // calculate component width according to the given percentage
                    int componentWidth = clientWidth * constraints.percentage / 100;

                    // calculate the preferred size of the component
                    int prefW = component.getPreferredSize().width;
                    if (constraints.usePreferredSize && componentWidth < prefW) {
                        // configured to use preferred size if calculated size is less than the
                        // preferred size
                        componentWidth = prefW;
                    }

                    // calculate the minimum size of the component
                    int minW = component.getMinimumSize().width;
                    if (constraints.useMinWidth && componentWidth < minW) {
                        // configured to use minimum width if calculated size is less than the
                        // minimum size
                        componentWidth = minW;
                    }
                    // check and wrap component to next row if needed
                    if (constraints.wrapComponents && x + componentWidth > parent.getWidth()) {
                        x = insets.left + leftMargin;
                        y += clientHeight + insets.top;
                        // update height of the parent component if it doesn fit
                        // if (parent.getHeight() < y + clientHeight) {
                        // parent.setSize(parent.getWidth(), parent.getHeight() + rowHeight);
                        // }
                    }
                    component.setBounds(x, y, componentWidth, clientHeight);
                    // update x coordinate
                    x += componentWidth + leftMargin;
                }
            }
        }
    }

    @Override
    public void addLayoutComponent(final String name, final Component comp) {
    }

    @Override
    public void removeLayoutComponent(final Component comp) {
        components.remove(comp); // remove component from map
    }

    @Override
    public void addLayoutComponent(final Component comp, final Object constraints) {
        if (constraints == null || !(constraints instanceof MyConstraints)) {
            throw new IllegalArgumentException("Invalid constraints object! - " + constraints);
        }
        MyConstraints myConstraints = (MyConstraints) constraints;
        if (myConstraints.percentage > 100) {
            throw new IllegalArgumentException("Invalid percentage value [" + myConstraints.percentage + "]!");
        }
        components.put(comp, myConstraints);
    }

    @Override
    public float getLayoutAlignmentX(final Container target) {
        return 0;
    }

    @Override
    public float getLayoutAlignmentY(final Container target) {
        return 0;
    }

    @Override
    public void invalidateLayout(final Container target) {
    }

    public int getLeftMargin() {
        return leftMargin;
    }

    public int getTopMargin() {
        return topMargin;
    }

    public int getRowHeight() {
        return rowHeight;
    }

    public static Integer calculatePercentage(final float value, final int total) {
        return new Integer((int) (value / total * 100));
    }

    private void validatePercentages() {
        int total = 0;
        for (Entry<Component, MyConstraints> compEntry : components.entrySet()) {
            total += compEntry.getValue().percentage;
        }
        if (total > 100) {
            throw new IllegalArgumentException("Total percentage [" + total + "] of the components in the layout is more than 100!");
        }
    }

}

/**
 * @author Rakesh.A
 *
 */
public static class MyConstraints {
    public final int percentage;
    public final boolean usePreferredSize, useMinWidth, wrapComponents;

    public MyConstraints(final int percentage, final boolean usePreferredSize) {
        this(percentage, usePreferredSize, false);
    }

    public MyConstraints(final int percentage, final boolean usePreferredSize, final boolean useMinWidth) {
        this(percentage, usePreferredSize, useMinWidth, false);
    }

    public MyConstraints(final int percentage, final boolean usePreferredSize, final boolean useMinWidth, final boolean wrapComponents) {
        this.percentage = percentage;
        this.usePreferredSize = usePreferredSize;
        this.useMinWidth = useMinWidth;
        this.wrapComponents = wrapComponents;
    }
}
}

添加到此,根面板将添加到JScrollPane,并且还需要更新。

1 个答案:

答案 0 :(得分:0)

谢谢大家的投入。我为这个问题选择了一种解决方法,而不是包装组件,我添加了一个水平滚动条:)