【问题标题】:Java - Vertical "FlowLayout" with Horizontal ScrollingJava - 带有水平滚动的垂直“FlowLayout”
【发布时间】:2012-01-02 01:11:51
【问题描述】:

如标题所述,我一直在尝试设置一种带有水平滚动的垂直流布局。布局中的组件将是 JLabels。让我画个图:

+-------------------------+   <--- window
|Label1   Label4    Label7|
|Label2   Label5    Label8|   <--- labels
|Label3   Label6    Label9|
|<===============>========|   <--- scrollbar
+-------------------------+

同一个窗口,垂直展开

+--------------------------+   <--- window
|Label1   Label5    Label9 |
|Label2   Label6    Label10|   <--- labels
|Label3   Label7    Label11|
|Label4   Label8    Label12|
|<===================>=====|   <--- scrollbar
+--------------------------+

因此,标签将填充可用的垂直空间,然后创建一个新列。一旦可用的水平空间用完,就会出现一个水平滚动条。

垂直滚动条通常不应该出现;但是,如果窗口的垂直高度非常小,最好有一个垂直滚动条。

非常感谢任何帮助。我是 Java 新手,所以任何额外的解释都会很棒。谢谢!

编辑:

根据以下回复,我现在正在处理: http://tips4java.wordpress.com/2008/11/06/wrap-layout/http://code.google.com/p/verticalflowlayout/

我有 WrapLayout 像这样扩展 VerticalFlowLayout:

package LogicSim;

import java.awt.*;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

/**
 *  FlowLayout subclass that fully supports wrapping of components.
 */
public class VerticalWrapLayout extends VerticalFlowLayout
{
private Dimension preferredLayoutSize;

/**
* Constructs a new <code>WrapLayout</code> with a left
* alignment and a default 5-unit horizontal and vertical gap.
*/
public VerticalWrapLayout()
{
    super();
}

/**
* Constructs a new <code>FlowLayout</code> with the specified
* alignment and a default 5-unit horizontal and vertical gap.
* The value of the alignment argument must be one of
* <code>WrapLayout</code>, <code>WrapLayout</code>,
* or <code>WrapLayout</code>.
* @param align the alignment value
*/
public VerticalWrapLayout(int align)
{
    super(align);
}

/**
* Creates a new flow layout manager with the indicated alignment
* and the indicated horizontal and vertical gaps.
* <p>
* The value of the alignment argument must be one of
* <code>WrapLayout</code>, <code>WrapLayout</code>,
* or <code>WrapLayout</code>.
* @param align the alignment value
* @param hgap the horizontal gap between components
* @param vgap the vertical gap between components
*/
public VerticalWrapLayout(int align, int hgap, int vgap)
{
    super(align, hgap, vgap);
}

/**
* Returns the preferred dimensions for this layout given the
* <i>visible</i> components in the specified target container.
* @param target the component which needs to be laid out
* @return the preferred dimensions to lay out the
* subcomponents of the specified container
*/
@Override
public Dimension preferredLayoutSize(Container target)
{
    return layoutSize(target, true);
}

/**
* Returns the minimum dimensions needed to layout the <i>visible</i>
* components contained in the specified target container.
* @param target the component which needs to be laid out
* @return the minimum dimensions to lay out the
* subcomponents of the specified container
*/
@Override
public Dimension minimumLayoutSize(Container target)
{
    Dimension minimum = layoutSize(target, false);
    minimum.width -= (getHgap() + 1);
    return minimum;
}

/**
* Returns the minimum or preferred dimension needed to layout the target
* container.
*
* @param target target to get layout size for
* @param preferred should preferred size be calculated
* @return the dimension to layout the target container
*/
private Dimension layoutSize(Container target, boolean preferred)
{
        synchronized (target.getTreeLock())
        {
    //  Each row must fit with the width allocated to the containter.
    //  When the container width = 0, the preferred width of the container
    //  has not yet been calculated so lets ask for the maximum.

    int targetWidth = target.getSize().width;

    if (targetWidth == 0)
        targetWidth = Integer.MAX_VALUE;

    int hgap = getHgap();
    int vgap = getVgap();
    Insets insets = target.getInsets();
    int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
    int maxWidth = targetWidth - horizontalInsetsAndGap;

    //  Fit components into the allowed width

    Dimension dim = new Dimension(0, 0);
    int rowWidth = 0;
    int rowHeight = 0;

    int nmembers = target.getComponentCount();

    for (int i = 0; i < nmembers; i++)
    {
        Component m = target.getComponent(i);

        if (m.isVisible())
        {
            Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();

            //  Can't add the component to current row. Start a new row.

            if (rowWidth + d.width > maxWidth)
            {
                addRow(dim, rowWidth, rowHeight);
                rowWidth = 0;
                rowHeight = 0;
            }

            //  Add a horizontal gap for all components after the first

            if (rowWidth != 0)
            {
                rowWidth += hgap;
            }

            rowWidth += d.width;
            rowHeight = Math.max(rowHeight, d.height);
        }
    }

    addRow(dim, rowWidth, rowHeight);

    dim.width += horizontalInsetsAndGap;
    dim.height += insets.top + insets.bottom + vgap * 2;

    //  When using a scroll pane or the DecoratedLookAndFeel we need to
    //  make sure the preferred size is less than the size of the
    //  target containter so shrinking the container size works
    //  correctly. Removing the horizontal gap is an easy way to do this.

    Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);

    if (scrollPane != null)
    {
        dim.width -= (hgap + 1);
    }

    return dim;
}
}

/*
 *  A new row has been completed. Use the dimensions of this row
 *  to update the preferred size for the container.
 *
 *  @param dim update the width and height when appropriate
 *  @param rowWidth the width of the row to add
 *  @param rowHeight the height of the row to add
 */
private void addRow(Dimension dim, int rowWidth, int rowHeight)
{
    dim.width = Math.max(dim.width, rowWidth);

    if (dim.height > 0)
    {
        dim.height += getVgap();
    }

    dim.height += rowHeight;
}
}

这是我的框架设置:

    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(300, 300);
    frame.setVisible(true);


    JPanel panel = new JPanel();
    panel.setLayout( new VerticalWrapLayout(0) );

    JScrollPane pane = new JScrollPane(panel);
    frame.add( pane, BorderLayout.CENTER );

    for (int i=0; i < 80; i++ ) {
        panel.add( new JLabel( "Label" + i ) );
    }

现在,这会按照我所追求的方式在垂直列中设置标签,但它仍然会创建垂直滚动条。在修改 VerticalWrapLayout 类时,我很不安。另外,我真的不明白 JScrollPane 如何与这些类交互。有关如何进行的任何建议?


已解决!请参阅下面的答案以及我的答案。

【问题讨论】:

    标签: java swing jframe jpanel layout-manager


    【解决方案1】:

    这是一个 VerticalFlowLayout 的示例。它是 FlowLayout 类的副本,其中一些逻辑更改为“垂直”方向而不是“水平”方向:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    /**
     * A flow layout arranges components in a directional flow, much
     * like lines of text in a paragraph. The flow direction is
     * determined by the container's <code>componentOrientation</code>
     * property and may be one of two values:
     * <ul>
     * <li><code>ComponentOrientation.TOP_TO_BOTTOM</code>
     * <li><code>ComponentOrientation.BOTTOM_TO_TOP</code>
     * </ul>
     * Flow layouts are typically used
     * to arrange buttons in a panel. It arranges buttons
     * horizontally until no more buttons fit on the same line.
     * The line alignment is determined by the <code>align</code>
     * property. The possible values are:
     * <ul>
     * <li>{@link #TOP TOP}
     * <li>{@link #BOTTOM BOTTOM}
     * <li>{@link #CENTER CENTER}
     * <li>{@link #LEADING LEADING}
     * <li>{@link #TRAILING TRAILING}
     * </ul>
     * <p>
     */
    public class VerticalFlowLayout implements LayoutManager, java.io.Serializable
    {
        /**
         * This value indicates that each row of components
         * should be left-justified.
         */
        public static final int TOP     = 0;
    
        /**
         * This value indicates that each row of components
         * should be centered.
         */
        public static final int CENTER    = 1;
    
        /**
         * This value indicates that each row of components
         * should be right-justified.
         */
        public static final int BOTTOM     = 2;
    
        /**
         * <code>align</code> is the property that determines
         * how each column distributes empty space.
         * It can be one of the following three values:
         * <ul>
         * <code>TOP</code>
         * <code>BOTTOM</code>
         * <code>CENTER</code>
         * </ul>
         *
         * @see #getAlignment
         * @see #setAlignment
         */
        int align;     // This is the one we actually use
    
        /**
         * The flow layout manager allows a seperation of
         * components with gaps.  The horizontal gap will
         * specify the space between components and between
         * the components and the borders of the
         * <code>Container</code>.
         *
         * @see #getHgap()
         * @see #setHgap(int)
         */
        int hgap;
    
        /**
         * The flow layout manager allows a seperation of
         * components with gaps.  The vertical gap will
         * specify the space between rows and between the
         * the rows and the borders of the <code>Container</code>.
         *
         * @see #getHgap()
         * @see #setHgap(int)
         */
        int vgap;
    
        /**
         * Constructs a new <code>VerticalFlowLayout</code> with a centered alignment and a
         * default 5-unit horizontal and vertical gap.
         */
        public VerticalFlowLayout()
        {
            this(CENTER, 5, 5);
        }
    
        /**
         * Constructs a new <code>VerticalFlowLayout</code> with the specified
         * alignment and a default 5-unit horizontal and vertical gap.
         * The value of the alignment argument must be one of
         * <code>VerticalFlowLayout.TOP</code>, <code>VerticalFlowLayout.BOTTOM</code>,
         * or <code>VerticalFlowLayout.CENTER</code>
         * @param align the alignment value
         */
        public VerticalFlowLayout(int align)
        {
            this(align, 5, 5);
        }
    
        /**
         * Creates a new flow layout manager with the indicated alignment
         * and the indicated horizontal and vertical gaps.
         * <p>
         * The value of the alignment argument must be one of
         * <code>VerticalFlowLayout.TOP</code>, <code>VerticalFlowLayout.BOTTOM</code>,
         * or <code>VerticalFlowLayout.CENTER</code>.
         * @param     align   the alignment value
         * @param     hgap  the horizontal gap between components
         *                   and between the components and the
         *                   borders of the <code>Container</code>
         * @param     vgap  the vertical gap between components
         *                   and between the components and the
         *                   borders of the <code>Container</code>
         */
        public VerticalFlowLayout(int align, int hgap, int vgap)
        {
            this.hgap = hgap;
            this.vgap = vgap;
            setAlignment(align);
        }
    
        /**
         * Gets the alignment for this layout.
         * Possible values are <code>VerticalFlowLayout.TOP</code>,
         * <code>VerticalFlowLayout.BOTTOM</code> or <code>VerticalFlowLayout.CENTER</code>,
         * @return   the alignment value for this layout
         * @see     java.awt.VerticalFlowLayout#setAlignment
         * @since     JDK1.1
         */
        public int getAlignment()
        {
            return align;
        }
    
        /**
         * Sets the alignment for this layout. Possible values are
         * <ul>
         * <li><code>VerticalFlowLayout.TOP</code>
         * <li><code>VerticalFlowLayout.BOTTOM</code>
         * <li><code>VerticalFlowLayout.CENTER</code>
         * </ul>
         * @param     align one of the alignment values shown above
         * @see     #getAlignment()
         * @since     JDK1.1
         */
        public void setAlignment(int align)
        {
            this.align = align;
        }
    
        /**
         * Gets the horizontal gap between components
         * and between the components and the borders
         * of the <code>Container</code>
         *
         * @return   the horizontal gap between components
         *           and between the components and the borders
         *           of the <code>Container</code>
         * @see     java.awt.VerticalFlowLayout#setHgap
         * @since     JDK1.1
         */
        public int getHgap() {
            return hgap;
        }
    
        /**
         * Sets the horizontal gap between components and
         * between the components and the borders of the
         * <code>Container</code>.
         *
         * @param hgap the horizontal gap between components
         *           and between the components and the borders
         *           of the <code>Container</code>
         * @see     java.awt.VerticalFlowLayout#getHgap
         * @since     JDK1.1
         */
        public void setHgap(int hgap) {
            this.hgap = hgap;
        }
    
        /**
         * Gets the vertical gap between components and
         * between the components and the borders of the
         * <code>Container</code>.
         *
         * @return   the vertical gap between components
         *           and between the components and the borders
         *           of the <code>Container</code>
         * @see     java.awt.VerticalFlowLayout#setVgap
         * @since     JDK1.1
         */
        public int getVgap() {
            return vgap;
        }
    
        /**
         * Sets the vertical gap between components and between
         * the components and the borders of the <code>Container</code>.
         *
         * @param vgap the vertical gap between components
         *           and between the components and the borders
         *           of the <code>Container</code>
         * @see     java.awt.VerticalFlowLayout#getVgap
         */
        public void setVgap(int vgap) {
            this.vgap = vgap;
        }
    
        /**
         * Adds the specified component to the layout.
         * Not used by this class.
         * @param name the name of the component
         * @param comp the component to be added
         */
        public void addLayoutComponent(String name, Component comp) {
        }
    
        /**
         * Removes the specified component from the layout.
         * Not used by this class.
         * @param comp the component to remove
         * @see    java.awt.Container#removeAll
         */
        public void removeLayoutComponent(Component comp) {
        }
    
        /**
         * Returns the preferred dimensions for this layout given the
         * <i>visible</i> components in the specified target container.
         *
         * @param target the container that needs to be laid out
         * @return  the preferred dimensions to lay out the
         *          subcomponents of the specified container
         * @see Container
         * @see #minimumLayoutSize
         * @see    java.awt.Container#getPreferredSize
         */
        public Dimension preferredLayoutSize(Container target)
        {
        synchronized (target.getTreeLock())
        {
            Dimension dim = new Dimension(0, 0);
            int nmembers = target.getComponentCount();
            boolean firstVisibleComponent = true;
    
            for (int i = 0 ; i < nmembers ; i++)
            {
                Component m = target.getComponent(i);
    
                if (m.isVisible())
                {
                    Dimension d = m.getPreferredSize();
                    dim.width = Math.max(dim.width, d.width);
    
                    if (firstVisibleComponent)
                    {
                        firstVisibleComponent = false;
                    }
                    else
                    {
                        dim.height += vgap;
                    }
    
                    dim.height += d.height;
                }
            }
    
            Insets insets = target.getInsets();
            dim.width += insets.left + insets.right + hgap*2;
            dim.height += insets.top + insets.bottom + vgap*2;
            return dim;
        }
        }
    
        /**
         * Returns the minimum dimensions needed to layout the <i>visible</i>
         * components contained in the specified target container.
         * @param target the container that needs to be laid out
         * @return  the minimum dimensions to lay out the
         *          subcomponents of the specified container
         * @see #preferredLayoutSize
         * @see    java.awt.Container
         * @see    java.awt.Container#doLayout
         */
        public Dimension minimumLayoutSize(Container target)
        {
        synchronized (target.getTreeLock())
        {
            Dimension dim = new Dimension(0, 0);
            int nmembers = target.getComponentCount();
            boolean firstVisibleComponent = true;
    
            for (int i = 0 ; i < nmembers ; i++)
            {
                Component m = target.getComponent(i);
                if (m.isVisible())
                {
                    Dimension d = m.getMinimumSize();
                    dim.width = Math.max(dim.width, d.width);
    
                    if (firstVisibleComponent)
                    {
                        firstVisibleComponent = false;
                    }
                    else
                    {
                        dim.height += vgap;
                    }
    
                    dim.height += d.height;
                }
            }
    
    
            Insets insets = target.getInsets();
            dim.width += insets.left + insets.right + hgap*2;
            dim.height += insets.top + insets.bottom + vgap*2;
            return dim;
        }
        }
    
        /**
         * Lays out the container. This method lets each
         * <i>visible</i> component take
         * its preferred size by reshaping the components in the
         * target container in order to satisfy the alignment of
         * this <code>VerticalFlowLayout</code> object.
         *
         * @param target the specified component being laid out
         * @see Container
         * @see    java.awt.Container#doLayout
         */
        public void layoutContainer(Container target)
        {
        synchronized (target.getTreeLock())
        {
            Insets insets = target.getInsets();
            int maxHeight = target.getSize().height - (insets.top + insets.bottom + vgap*2);
            int nmembers = target.getComponentCount();
            int x = insets.left + hgap;
            int y = 0;
            int columnWidth = 0;
            int start = 0;
    
            boolean ttb = target.getComponentOrientation().isLeftToRight();
    
            for (int i = 0 ; i < nmembers ; i++)
            {
                Component m = target.getComponent(i);
    
                if (m.isVisible())
                {
                    Dimension d = m.getPreferredSize();
                    m.setSize(d.width, d.height);
    
                    if ((y == 0) || ((y + d.height) <= maxHeight))
                    {
                        if (y > 0)
                        {
                            y += vgap;
                        }
    
                        y += d.height;
                        columnWidth = Math.max(columnWidth, d.width);
                    }
                    else
                    {
                        moveComponents(target, x, insets.top + vgap, columnWidth, maxHeight - y, start, i, ttb);
                        y = d.height;
                        x += hgap + columnWidth;
                        columnWidth = d.width;
                        start = i;
                    }
                }
            }
    
            moveComponents(target, x, insets.top + vgap, columnWidth, maxHeight - y, start, nmembers, ttb);
        }
        }
    
        /**
         * Centers the elements in the specified row, if there is any slack.
         * @param target the component which needs to be moved
         * @param x the x coordinate
         * @param y the y coordinate
         * @param width the width dimensions
         * @param height the height dimensions
         * @param columnStart the beginning of the column
         * @param columnEnd the the ending of the column
         */
        private void moveComponents(
            Container target, int x, int y, int width, int height, int columnStart, int columnEnd, boolean ttb)
        {
            switch (align)
            {
                case TOP:
                    y += ttb ? 0 : height;
                    break;
                case CENTER:
                    y += height / 2;
                    break;
                case BOTTOM:
                    y += ttb ? height : 0;
                    break;
            }
    
            for (int i = columnStart ; i < columnEnd ; i++)
            {
                Component m = target.getComponent(i);
    
                if (m.isVisible())
                {
                    int cx;
                    cx = x + (width - m.getSize().width) / 2;
    
                    if (ttb)
                    {
                        m.setLocation(cx, y);
                    }
                    else
                    {
                        m.setLocation(cx, target.getSize().height - y - m.getSize().height);
                    }
    
                    y += m.getSize().height + vgap;
                }
            }
        }
    
        /**
         * Returns a string representation of this <code>VerticalFlowLayout</code>
         * object and its values.
         * @return   a string representation of this layout
         */
        public String toString()
        {
            String str = "";
    
            switch (align)
            {
                case TOP:    str = ",align=top"; break;
                case CENTER: str = ",align=center"; break;
                case BOTTOM: str = ",align=bottom"; break;
            }
    
            return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";
        }
    
    
        public static void main(String[] args)
        {
            JPanel main = new JPanel( new BorderLayout() );
    
            final JPanel buttons = new JPanel(new VerticalFlowLayout() );
    //      buttons.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
            main.add(buttons, BorderLayout.CENTER);
    
            for (int i = 0; i < 7; i++)
            {
                buttons.add( new JRadioButton("button " + i) );
            }
    
            JButton button = new JButton("Add Radio Button");
            main.add(button, BorderLayout.SOUTH);
            button.addActionListener( new ActionListener()
            {
                private int i = 8;
    
                public void actionPerformed(ActionEvent e)
                {
                    buttons.add( new JRadioButton("button R Us" + i++) );
                    buttons.revalidate();
    //              pack();
                }
            });
    
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(main);
            frame.setSize(300, 300);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
    
    }
    

    理论上你应该能够使用 WrapLayout 并让它扩展这个类,然后将代码自定义为垂直方向。

    编辑:

    我的 WrapLayout 像这样扩展了 VerticalFlowLayout:

    您不能只扩展 VerticalFlowLayout。 WrapLayout 代码旨在根据父容器的大小计算固定宽度。您需要更改计算固定高度的行为。我还没有尝试过,但基本上你需要将“宽度”相关变量引用更改为“高度”,将“高度”相关变量引用更改为“宽度”,以便代码将在垂直维度而不是水平维度上工作。

    【讨论】:

    • +1 非常好的代码,它回答了 OP 正在寻找的内容,尽管它缺少 JScrollPane :)
    • 是的,很好。 +1 我很少需要这样的东西(如果有人要求我这样做,我会使用 BoxLayout),但这是一个很好的课程。 :)
    • +1 谢谢。我还找到了WrapLayout 类,我正在尝试扩展VerticalFlowLayout。我已经编辑了我原来的问题。
    • 未来的访客查看基于此的 OPs 解决方案! ready to go VerticalWrapLayout
    【解决方案2】:

    这是我修改后的VerticalWrapLayout,以防有人感兴趣!

    package LogicSim;
    
    import java.awt.*;
    import javax.swing.JScrollPane;
    import javax.swing.SwingUtilities;
    
    /**
     *  FlowLayout subclass that fully supports wrapping of components.
     */
    public class VerticalWrapLayout extends VerticalFlowLayout
    {
    private Dimension preferredLayoutSize;
    
    /**
    * Constructs a new <code>WrapLayout</code> with a left
    * alignment and a default 5-unit horizontal and vertical gap.
    */
    public VerticalWrapLayout()
    {
        super();
    }
    
    /**
    * Constructs a new <code>FlowLayout</code> with the specified
    * alignment and a default 5-unit horizontal and vertical gap.
    * The value of the alignment argument must be one of
    * <code>WrapLayout</code>, <code>WrapLayout</code>,
    * or <code>WrapLayout</code>.
    * @param align the alignment value
    */
    public VerticalWrapLayout(int align)
    {
        super(align);
    }
    
    /**
    * Creates a new flow layout manager with the indicated alignment
    * and the indicated horizontal and vertical gaps.
    * <p>
    * The value of the alignment argument must be one of
    * <code>WrapLayout</code>, <code>WrapLayout</code>,
    * or <code>WrapLayout</code>.
    * @param align the alignment value
    * @param hgap the horizontal gap between components
    * @param vgap the vertical gap between components
    */
    public VerticalWrapLayout(int align, int hgap, int vgap)
    {
        super(align, hgap, vgap);
    }
    
    /**
    * Returns the preferred dimensions for this layout given the
    * <i>visible</i> components in the specified target container.
    * @param target the component which needs to be laid out
    * @return the preferred dimensions to lay out the
    * subcomponents of the specified container
    */
    @Override
    public Dimension preferredLayoutSize(Container target)
    {
        return layoutSize(target, true);
    }
    
    /**
    * Returns the minimum dimensions needed to layout the <i>visible</i>
    * components contained in the specified target container.
    * @param target the component which needs to be laid out
    * @return the minimum dimensions to lay out the
    * subcomponents of the specified container
    */
    @Override
    public Dimension minimumLayoutSize(Container target)
    {
        Dimension minimum = layoutSize(target, false);
        minimum.height -= (getVgap() + 1);
        return minimum;
    }
    
    /**
    * Returns the minimum or preferred dimension needed to layout the target
    * container.
    *
    * @param target target to get layout size for
    * @param preferred should preferred size be calculated
    * @return the dimension to layout the target container
    */
    private Dimension layoutSize(Container target, boolean preferred)
    {
            synchronized (target.getTreeLock())
            {
        //  Each row must fit with the width allocated to the containter.
        //  When the container width = 0, the preferred width of the container
        //  has not yet been calculated so lets ask for the maximum.
    
        int targetHeight = target.getSize().height;
    
        if (targetHeight == 0)
            targetHeight = Integer.MAX_VALUE;
    
        int hgap = getHgap();
        int vgap = getVgap();
        Insets insets = target.getInsets();
        int verticalInsetsAndGap = insets.top + insets.bottom + (vgap * 2);
        int maxHeight = targetHeight - verticalInsetsAndGap;
    
        //  Fit components into the allowed height
    
        Dimension dim = new Dimension(0, 0);
        int rowWidth = 0;
        int rowHeight = 0;
    
        int nmembers = target.getComponentCount();
    
        for (int i = 0; i < nmembers; i++)
        {
            Component m = target.getComponent(i);
    
            if (m.isVisible())
            {
                Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
    
                //  Can't add the component to current row. Start a new row.
    
                if (rowHeight + d.height > maxHeight)
                {
                    addColumn(dim, rowWidth, rowHeight);
                    rowWidth = 0;
                    rowHeight = 0;
                }
    
                //  Add a horizontal gap for all components after the first
    
                if (rowHeight != 0)
                {
                    rowHeight += vgap;
                }
    
                                // ******************************************************
                rowHeight += d.height;
                                rowWidth = Math.max(rowWidth, d.width);
            }
        }
    
        addColumn(dim, rowWidth, rowHeight);
    
        dim.height += verticalInsetsAndGap;
        dim.width += insets.left + insets.right + hgap * 2;
    
        //When using a scroll pane or the DecoratedLookAndFeel we need to
        //  make sure the preferred size is less than the size of the
        //  target containter so shrinking the container size works
        //  correctly. Removing the horizontal gap is an easy way to do this.
    
        Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
    
        if (scrollPane != null)
        {
            dim.height -= (vgap + 1);
        }
    
        return dim;
    }
    }
    
    /*
     *  A new row has been completed. Use the dimensions of this row
     *  to update the preferred size for the container.
     *
     *  @param dim update the width and height when appropriate
     *  @param rowWidth the width of the row to add
     *  @param rowHeight the height of the row to add
     */
    private void addColumn(Dimension dim, int rowWidth, int rowHeight)
    {
        dim.height = Math.max(dim.height, rowHeight);
    
        if (dim.width > 0)
        {
            dim.width += getHgap();
        }
    
        dim.width += rowWidth;
    }
    }
    

    谢谢大家!

    【讨论】:

    • +1 Shiiiiiiit(就像“The Wire”中的 Clay Davis)这很好用! :D
    • 此解决方案基于 tips4java.wordpress.com/2008/11/06/wrap-layout,最初由 Rob Camick 编写。该服务器上的 About 链接讨论了该代码的 IP 权利。
    【解决方案3】:

    你可以使用BoxLayout:

    例如:

    import javax.swing.BorderFactory;
    import javax.swing.Box;
    import javax.swing.BoxLayout;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.SwingUtilities;
    
    public class CustomFrame extends JFrame
    {
    
        private int labelCounter = 0;
        private int maxLabels = 3;
        private Box box;
        private JPanel pane;
    
        public CustomFrame()
        {
            super("Custom JFrame");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            pane = new JPanel();
            pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
            JScrollPane scr = new JScrollPane(pane);
            add(scr);
    
            for(int i = 1; i <= 15; i++) addNewLabel("Label " + i);
    
            setSize(200,130);
            setVisible(true);
        }
    
        private void addNewLabel(String s)
        {
            if(labelCounter % maxLabels == 0)
            {
                box = Box.createVerticalBox();
                box.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
            }
            box.add(new JLabel(s));
            pane.add(box);
            labelCounter++;
        }
    
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(new Runnable()
            {
                public void run()
                {
                    new CustomFrame();
                }
            });
    
        }
    }
    

    【讨论】:

    • 感谢您的帮助,但这并不是我想要的,因为在调整窗口大小时标签应该填充可用的垂直空间。可能可以尝试确定窗口的大小和标签的高度,然后看看有多少可以排成一行,但最好使用类似 flowlayout 的东西。
    • 也许 OP 不喜欢它,但我喜欢。 +1 我最喜欢您的回答的方面是 1)小屏幕截图 2)代码 a)示例 b)在 EDT 上开始(没有节省)c)具有漂亮的边框。 :)
    【解决方案4】:

    我调查了您的问题,据我所知,有多个 VerticalFlowLayouts 不可包装,还有wrappableFlowLayouts,它们不是垂直的。

    我没有将这些放在一起(聪明人会做的),而是根据您的答案写了一个糟糕的解决方法。它完成了工作,但遗憾的是它并没有我想要的那么顺利/可靠。但我想我还是发布了它。

    public class CustomFrame extends JFrame {
    
    private int borderWidth = 10;
    private int labelCounter = 0;
    private Box box;
    private JPanel pane;
    private List<JLabel> registeredLabels = new ArrayList<JLabel>();
    
    public CustomFrame() {
        super("Custom JFrame");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pane = new JPanel();
        pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
        JScrollPane scrollPane = new JScrollPane(pane);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        add(scrollPane);
        setSize(200, 130);
    
        // this just calls the method reAddAllLabels() upon resizing
        this.addComponentListener(new ComponentListener() {
    
            @Override
            public void componentShown(ComponentEvent e) {
                // ignore
            }
    
            @Override
            public void componentResized(ComponentEvent e) {
                reAddAllLabels();
                repaint();
            }
    
            @Override
            public void componentMoved(ComponentEvent e) {
                // ignore
            }
    
            @Override
            public void componentHidden(ComponentEvent e) {
                // ignore
            }
        });
    
        setVisible(true);
    }
    
    /**
     * Build the Label, register the Label in the Label-List, add the Label through further method
     * 
     * @param text
     *            for the new Label
     */
    public void addNewLabel(String text) {
        JLabel myJLabel = new JLabel(text);
        registeredLabels.add(myJLabel);
        addLabel(myJLabel);
    }
    
    /**
     * Reset stuff, add all registered Labels
     * 
     */
    private void reAddAllLabels() {
        labelCounter = 0;
        pane.removeAll();
        if (registeredLabels.size() > 0) {
            for (JLabel label : registeredLabels) {
                addLabel(label);
            }
        }
    }
    
    /**
     * Calculate max-Labels per Column, eventually create new Box, add Label to box
     * 
     * @param label
     */
    private void addLabel(JLabel label) {
        int maxLabels = (pane.getHeight() - borderWidth * 2) / label.getPreferredSize().height;
        if (labelCounter % maxLabels == 0) {
            box = Box.createVerticalBox();
            box.setBorder(BorderFactory.createEmptyBorder(borderWidth, borderWidth, borderWidth, borderWidth));
        }
        box.add(label);
        pane.add(box);
        labelCounter++;
    }
    
    /**
     * How to use the frame
     * 
     * @param args
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                CustomFrame customFrame = new CustomFrame();
    
                for (int i = 1; i <= 20; i++) {
                    customFrame.addNewLabel("Label " + i);
                }
            }
        });
    }
    }
    

    【讨论】:

    • +1 用于将 OP 指向正确的方向。更好的解决方案确实是VerticalFlowLayoutWrapLayout 的组合以创建VerticalWrapLayout
    • +1 感谢您抽出宝贵时间回复,非常感谢。 @camickr 我现在正在努力。
    • 未来的访客根据答案查看 OPs 解决方案! ready to go VerticalWrapLayout
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-09
    • 2016-05-28
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    相关资源
    最近更新 更多