使用BoxLayout强制Panels之间的空间为0?

时间:2016-10-30 02:09:55

标签: java swing layout-manager java-2d boxlayout

目前,作为我正在研究的项目的一部分,我正在实现一个可用于可视化比特排列的组件(作为加密算法的一部分)。我这样做是通过创建两排"引脚"并通过在提示之间画线来连接它们,在它们之间创建一种网络。

这一点的一个重要部分是我自己和其他可视化的一部分(例如,我可能想要包括S-Box)使用这种可视化,因此我需要能够转向引脚打开和关闭。我对此的解决方案是使用JPanel将引脚行放入页眉和页脚面板中,使其不可见。

我将它们垂直放置在BoxLayout中,但我最终会在它们之间留出空间,即使我在页眉上方和页脚下方添加胶水。

我的例子在初始化时看起来像这样:

Web without resizing.

当我调整它的大小时,它们会聚在一起,但仍然只触及一侧:

Web after resizing.

我猜这是在组件大小和布局方面将我的用户空间转换为设备空间的某种愚蠢错误,但对于我的生活,我找不到它。这是我的代码,虽然我为这个烂摊子道歉:

import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Ellipse2D;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class PermutationWeb extends JPanel
{
    private static enum EndPanelType
    {
        HEADER, FOOTER
    }

    private final JPanel header;
    private final JPanel mainPanel;
    private final JPanel footer;

    private double  widthFactor;
    private double  heightFactor;
    private int     widthMax;
    private int     heightMax;
    private int[]   indexMappings;
    private Point2D.Double[] endpoints;
    private Line2D.Double[]  drawingLines;

    public PermutationWeb(int indices, boolean endPanelsOn)
    {
        super();

        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

        widthMax  = (indices + 1)*2;
        heightMax = (int)Math.round(widthMax*(3.0/17.0));
        widthFactor  = 1;
        heightFactor = 1;

        endpoints    = new Point2D.Double[indices * 2];
        drawingLines = new Line2D.Double[indices];

        for(int i=0; i<indices; i++)
        {
            endpoints[i]         = new Point2D.Double(i*2+2, 0);
            endpoints[i+indices] = new Point2D.Double(i*2+2, heightMax);
            drawingLines[i] = new Line2D.Double();
        }

        header    = new WebEndPanel(EndPanelType.HEADER);
        mainPanel = new WebMainPanel();
        footer    = new WebEndPanel(EndPanelType.FOOTER);

        add(Box.createVerticalGlue());
        add(header);
        add(mainPanel);
        add(footer);
        add(Box.createVerticalGlue());

        setEndPanelsOn(endPanelsOn);
    }

    public Point2D getEndpoint(int index)
    {
        return endpoints[index];
    }

    public void updateMappings(int[] mappings)
    {
        this.indexMappings = mappings;

        for(int i=0; i<indexMappings.length; i++)
        {
            drawingLines[i].setLine(endpoints[i], endpoints[indexMappings.length + indexMappings[i]]);
        }

        //paint();
    }

    public void setEndPanelsOn(boolean endPanelsOn)
    {
        header.setVisible(endPanelsOn);
        footer.setVisible(endPanelsOn);
    }

    @Override
    public Dimension getMaximumSize()
    {
        int height = mainPanel.getHeight();
        if(header.isVisible())
        {
            height += (header.getHeight() * 2);
        }

        int width = mainPanel.getWidth();

        return new Dimension(width, height);
    }

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

    public static void main(String[] args)
    {
        JFrame jf = new JFrame();
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setSize(800, 600);

        int[] mappings = {0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15};

        PermutationWeb webTest = new PermutationWeb(16, true);
        jf.add(webTest);
        jf.setVisible(true);
        webTest.setVisible(true);

        webTest.updateMappings(mappings);

        System.out.printf("Header:    [%s]\nMainPanel: [%s]\nFooter:    [%s]\n",
                webTest.header.getSize().toString(),
                webTest.mainPanel.getSize().toString(),
                webTest.footer.getSize().toString());
    }


    private class WebMainPanel extends WebSubPanel
    {
        private static final double HEIGHT_RATIO = 0.25;

        @Override
        public void paintComponent(Graphics g)
        {
            Graphics2D g2 = (Graphics2D)g;
            super.paintComponent(g2);

            scaleTo(getSize());
            g2.scale(widthFactor, widthFactor);
            g2.setStroke(new BasicStroke((float)(2.0/widthFactor)));

            for(Line2D line: drawingLines)
            {
                g2.draw(line);
            }
        }

        @Override
        public Dimension getMaximumSize()
        {
            return new Dimension(MAX_WIDTH_PX, (int)(MAX_WIDTH_PX*HEIGHT_RATIO));
        }
    }

    private class WebEndPanel extends WebSubPanel
    {
        private static final double HEIGHT_RATIO = 0.125;
        private static final double PIN_RADIUS = 0.5;

        private final EndPanelType endType;
        private Line2D.Double[]    edgeLines;
        private Ellipse2D.Double[] pinHeads;

        public WebEndPanel(EndPanelType endType)
        {
            super();
            this.endType = endType;
            this.edgeLines = new Line2D.Double[endpoints.length/2];
            this.pinHeads  = new Ellipse2D.Double[endpoints.length/2];

            for(int i=0; i<edgeLines.length; i++)
            {
                Point2D pointA;
                Point2D pointB;


                if(EndPanelType.HEADER.equals(this.endType))
                {
                    pointA = new Point2D.Double(i*2+2, 4);
                    pointB = new Point2D.Double(i*2+2, 2);

                    pinHeads[i]  = new Ellipse2D.Double(
                            pointB.getX()-PIN_RADIUS,
                            pointB.getY()-PIN_RADIUS*2,
                            PIN_RADIUS*2,
                            PIN_RADIUS*2);
                }
                else // FOOTER
                {
                    pointA = new Point2D.Double(i*2+2, 0);
                    pointB = new Point2D.Double(i*2+2, 2);

                    pinHeads[i]  = new Ellipse2D.Double(
                            pointB.getX()-PIN_RADIUS,
                            3-PIN_RADIUS*2,
                            PIN_RADIUS*2,
                            PIN_RADIUS*2);
                }

                edgeLines[i] = new Line2D.Double(pointA, pointB);
            }
        }

        @Override
        public Dimension getMaximumSize()
        {
            return new Dimension(MAX_WIDTH_PX, (int)(MAX_WIDTH_PX*HEIGHT_RATIO));
        }

        @Override
        public void paintComponent(Graphics g)
        {
            Graphics2D g2 = (Graphics2D)g;
            super.paintComponent(g2);

            scaleTo(getSize());
            g2.scale(widthFactor, widthFactor);
            g2.setStroke(new BasicStroke((float)(2.0/widthFactor)));

            for(Line2D line: edgeLines)
            {
                g2.draw(line);
            }

            for(Ellipse2D pin: pinHeads)
            {
                g2.draw(pin);
            }
        }
    }

    private abstract class WebSubPanel extends JPanel
    {
        protected static final int MAX_WIDTH_PX = 800;

        public WebSubPanel()
        {
            super();
            setBorder(null);
            setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
        }

        public void scaleTo(Dimension d)
        {
            widthFactor  = d.getWidth()  / (double)widthMax;
            heightFactor = d.getHeight() / (double)heightMax;
        }

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

这里的最终目标是一个可调整大小的Web,其中页眉和页脚WebEndPanel可以是不可见的,但它们之间有0个空格,显示时WebMainPanel(就像它们是单个实体一样)

1 个答案:

答案 0 :(得分:2)

如果空间可用,BoxLayout会将组件的大小调整为最大大小。

因此,您首先需要实现组件的getPreferredSize()方法,以便按正常大小显示它。

然后,如果它具有增长能力(并且您的自定义绘画支持此功能),则覆盖getMaximumSize()方法以返回大小。

因此,如果您想让它与其他面板连续,那么绘画需要基于面板的实际尺寸。