最近写一个java UI,需要用到垂直流布局管理器,要求该管理器能够实现内部组件的宽度自适应。看了swing提供的5个布局管理器,尝试的实现效果都不理想,看来只能自己搞一个了,好在网上已有实现,其测试效果如下图:
图一 垂直流布局管理器实现效果
具体代码如下:
清单一:
import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Insets; import javax.swing.JButton; import javax.swing.JFrame; /** * VerticalFlowLayout is similar to FlowLayout except it lays out components * vertically. Extends FlowLayout because it mimics much of the behavior of the * FlowLayout class, except vertically. An additional feature is that you can * specify a fill to edge flag, which causes the VerticalFlowLayout manager to * resize all components to expand to the column width Warning: This causes * problems when the main panel has less space that it needs and it seems to * prohibit multi-column output. Additionally there is a vertical fill flag, * which fills the last component to the remaining height of the container. */ public class VFlowLayout extends FlowLayout { /** * */ private static final long serialVersionUID = 1L; /** * Specify alignment top. */ public static final int TOP = 0; /** * Specify a middle alignment. */ public static final int MIDDLE = 1; /** * Specify the alignment to be bottom. */ public static final int BOTTOM = 2; int hgap; int vgap; boolean hfill; boolean vfill; public static void main(String[] args) { System.out.println("Just for test ..."); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setBounds(0, 0, 600, 600); frame.setLayout(new VFlowLayout()); int i = 0; frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.add(new JButton(String.valueOf(i++))); frame.setVisible(true); } /** * Construct a new VerticalFlowLayout with a middle alignment, and the fill * to edge flag set. */ public VFlowLayout() { this(TOP, 5, 5, true, false); } /** * Construct a new VerticalFlowLayout with a middle alignment. * * @param hfill * the fill to edge flag * @param vfill * the vertical fill in pixels. */ public VFlowLayout(boolean hfill, boolean vfill) { this(TOP, 5, 5, hfill, vfill); } /** * Construct a new VerticalFlowLayout with a middle alignment. * * @param align * the alignment value */ public VFlowLayout(int align) { this(align, 5, 5, true, false); } /** * Construct a new VerticalFlowLayout. * * @param align * the alignment value * @param hfill * the horizontalfill in pixels. * @param vfill * the vertical fill in pixels. */ public VFlowLayout(int align, boolean hfill, boolean vfill) { this(align, 5, 5, hfill, vfill); } /** * Construct a new VerticalFlowLayout. * * @param align * the alignment value * @param hgap * the horizontal gap variable * @param vgap * the vertical gap variable * @param hfill * the fill to edge flag * @param vfill * true if the panel should vertically fill. */ public VFlowLayout(int align, int hgap, int vgap, boolean hfill, boolean vfill) { setAlignment(align); this.hgap = hgap; this.vgap = vgap; this.hfill = hfill; this.vfill = vfill; } /** * Returns the preferred dimensions given the components in the target * container. * * @param target * the component to lay out */ public Dimension preferredLayoutSize(Container target) { Dimension tarsiz = new Dimension(0, 0); for (int i = 0; i < target.getComponentCount(); i++) { Component m = target.getComponent(i); if (m.isVisible()) { Dimension d = m.getPreferredSize(); tarsiz.width = Math.max(tarsiz.width, d.width); if (i > 0) { tarsiz.height += hgap; } tarsiz.height += d.height; } } Insets insets = target.getInsets(); tarsiz.width += insets.left + insets.right + hgap * 2; tarsiz.height += insets.top + insets.bottom + vgap * 2; return tarsiz; } /** * Returns the minimum size needed to layout the target container. * * @param target * the component to lay out. * @return the minimum layout dimension. */ public Dimension minimumLayoutSize(Container target) { Dimension tarsiz = new Dimension(0, 0); for (int i = 0; i < target.getComponentCount(); i++) { Component m = target.getComponent(i); if (m.isVisible()) { Dimension d = m.getMinimumSize(); tarsiz.width = Math.max(tarsiz.width, d.width); if (i > 0) { tarsiz.height += vgap; } tarsiz.height += d.height; } } Insets insets = target.getInsets(); tarsiz.width += insets.left + insets.right + hgap * 2; tarsiz.height += insets.top + insets.bottom + vgap * 2; return tarsiz; } /** * Set true to fill vertically. * * @param vfill * true to fill vertically. */ public void setVerticalFill(boolean vfill) { this.vfill = vfill; } /** * Returns true if the layout vertically fills. * * @return true if vertically fills the layout using the specified. */ public boolean getVerticalFill() { return vfill; } /** * Set to true to enable horizontally fill. * * @param hfill * true to fill horizontally. */ public void setHorizontalFill(boolean hfill) { this.hfill = hfill; } /** * Returns true if the layout horizontally fills. * * @return true if horizontally fills. */ public boolean getHorizontalFill() { return hfill; } /** * places the components defined by first to last within the target * container using the bounds box defined. * * @param target * the container. * @param x * the x coordinate of the area. * @param y * the y coordinate of the area. * @param width * the width of the area. * @param height * the height of the area. * @param first * the first component of the container to place. * @param last * the last component of the container to place. */ private void placethem(Container target, int x, int y, int width, int height, int first, int last) { int align = getAlignment(); if (align == MIDDLE) { y += height / 2; } if (align == BOTTOM) { y += height; } for (int i = first; i < last; i++) { Component m = target.getComponent(i); Dimension md = m.getSize(); if (m.isVisible()) { int px = x + (width - md.width) / 2; m.setLocation(px, y); y += vgap + md.height; } } } /** * Lays out the container. * * @param target * the container to lay out. */ public void layoutContainer(Container target) { Insets insets = target.getInsets(); int maxheight = target.getSize().height - (insets.top + insets.bottom + vgap * 2); int maxwidth = target.getSize().width - (insets.left + insets.right + hgap * 2); int numcomp = target.getComponentCount(); int x = insets.left + hgap, y = 0; int colw = 0, start = 0; for (int i = 0; i < numcomp; i++) { Component m = target.getComponent(i); if (m.isVisible()) { Dimension d = m.getPreferredSize(); // fit last component to remaining height if ((this.vfill) && (i == (numcomp - 1))) { d.height = Math.max((maxheight - y), m.getPreferredSize().height); } // fit component size to container width if (this.hfill) { m.setSize(maxwidth, d.height); d.width = maxwidth; } else { m.setSize(d.width, d.height); } if (y + d.height > maxheight) { placethem(target, x, insets.top + vgap, colw, maxheight - y, start, i); y = d.height; x += hgap + colw; colw = d.width; start = i; } else { if (y > 0) { y += vgap; } y += d.height; colw = Math.max(colw, d.width); } } } placethem(target, x, insets.top + vgap, colw, maxheight - y, start, numcomp); } }