最近寫一個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);
- }
- }