【问题标题】:suitable LayoutManager for resizable components适用于可调整大小组件的 LayoutManager
【发布时间】:2011-12-06 17:22:32
【问题描述】:

前段时间我读到了这个article,它展示了一种在 Swing 中实现鼠标可调整大小组件的方法。

作者使用一个空的 LayoutManager 来允许绝对的组件定位。 我知道不应该使用空布局,所以我的问题是:

是否有任何已经实现的 LayoutManager 允许组件的绝对定位,或者我必须自己实现它?

【问题讨论】:

  • 您是否正在尝试制作鼠标可调整大小的组件?如果是,为什么?
  • @Andrew Thompson:我有一种画布,用户应该能够在其中插入不同形状的组件,对其进行定位和调整大小。如果您知道一种更好的方法来做到这一点而不允许组件调整鼠标大小,请让我朝着正确的方向前进。 (我更喜欢使用组件而不是在 JPanel 中绘制形状,因为我可以使用鼠标事件来移动和调整它们的大小)
  • 对于最复杂的 GUI,我使用 GridBagLaout,但是我将 50-80 个小 JLabels 放在第一行(水平矩阵),然后我永远不必解决 AbsolutePositioning,然后我将 JComponent 放在行中- 列和高度/重量大小再次与列和行数,

标签: java swing css-position layout-manager null-layout-manager


【解决方案1】:

作为替代方案,也可以考虑

【讨论】:

  • +1:非常感谢。我认为调整组件大小和移动窗口正是我想要的!
【解决方案2】:

布局管理器确实做了 3 件事:

  1. 设置组件的位置。由于您需要能够拖动组件,因此您不希望布局管理器执行此操作。

  2. 设置组件的大小。由于您需要调整组件大小的能力,因此您不想这样做。但是,您可能希望根据组件的首选大小为组件指定默认大小。这样在创建组件时就不需要指定大小了。

  3. 根据添加到其中的组件确定父面板的首选大小。这将允许滚动窗格正常运行,因为可以根据需要添加/删除滚动条。因此,您需要确定拖动应该如何工作的行为。也就是说,您是否允许将组件拖动到面板的当前边界之外。如果是这样,面板的首选尺寸应该会自动增加。

是否有任何已经实现的 LayoutManager 允许组件的绝对定位

我一直在使用一个接近您需求的布局管理器。它被设计用于与垃圾神提供的Moving Windows 链接中的 ComponentMover 类一起使用。

这是我对这个类的测试代码:

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 */
public class DragLayout implements LayoutManager, java.io.Serializable
{
    public DragLayout()
    {
    }

    /**
     * Adds the specified component with the specified name to the layout.
     * @param name the name of the component
     * @param comp the component to be added
     */
    @Override
    public void addLayoutComponent(String name, Component comp) {}


    /**
     * Removes the specified component from the layout.
     *
     * @param comp the component to be removed
     */
    @Override
    public void removeLayoutComponent(Component component)
    {
    }

    /**
     *  Determine the minimum size on the Container
     *
     *  @param   target   the container in which to do the layout
     *  @return  the minimum dimensions needed to lay out the
     *           subcomponents of the specified container
     */
    @Override
    public Dimension minimumLayoutSize(Container parent)
    {
        synchronized (parent.getTreeLock())
        {
            return preferredLayoutSize(parent);
        }
    }

    /**
     *  Determine the preferred size on the Container
     *
     *  @param   parent   the container in which to do the layout
     *  @return  the preferred dimensions to lay out the
     *           subcomponents of the specified container
     */
    @Override
    public Dimension preferredLayoutSize(Container parent)
    {
        synchronized (parent.getTreeLock())
        {
            return getLayoutSize(parent);
        }
    }

    /*
     *  The calculation for minimum/preferred size it the same. The only
     *  difference is the need to use the minimum or preferred size of the
     *  component in the calculation.
     *
     *  @param   parent  the container in which to do the layout
     */
    private Dimension getLayoutSize(Container parent)
    {
        Insets parentInsets = parent.getInsets();
        int x = parentInsets.left;
        int y = parentInsets.top;
        int width = 0;
        int height = 0;

        //  Get extreme values of the components on the container

        for (Component component: parent.getComponents())
        {
            if (component.isVisible())
            {
                Point p = component.getLocation();
                Dimension d = component.getPreferredSize();
                x = Math.min(x, p.x);
                y = Math.min(y, p.y);
                width = Math.max(width, p.x + d.width);
                height = Math.max(height, p.y + d.height);
            }
        }

        // Width/Height is adjusted if any component is outside left/top edge

        if (x < parentInsets.left)
            width += parentInsets.left - x;

        if (y < parentInsets.top)
            height += parentInsets.top - y;

        //  Adjust for insets

        width += parentInsets.right;
        height += parentInsets.bottom;
        Dimension d = new Dimension(width, height);

        return d;
//      return new Dimension(width, height);
    }

    /**
     * Lays out the specified container using this layout.
     *
     * @param     target   the container in which to do the layout
     */
    @Override
    public void layoutContainer(Container parent)
    {
    synchronized (parent.getTreeLock())
    {
        Insets parentInsets = parent.getInsets();

        int x = parentInsets.left;
        int y = parentInsets.top;

        //  Get X/Y location outside the bounds of the panel

        for (Component component: parent.getComponents())
        {
            if (component.isVisible())
            {
                Point location = component.getLocation();
                x = Math.min(x, location.x);
                y = Math.min(y, location.y);
            }
        }

        x = (x < parentInsets.left) ? parentInsets.left - x : 0;
        y = (y < parentInsets.top) ? parentInsets.top - y : 0;

        //  Set bounds of each component

        for (Component component: parent.getComponents())
        {
            if (component.isVisible())
            {
                Point p = component.getLocation();
                Dimension d = component.getPreferredSize();

                component.setBounds(p.x + x, p.y + y, d.width, d.height);
            }
        }
    }}

    /**
     * Returns the string representation of this column layout's values.
     * @return   a string representation of this layout
     */
    public String toString()
    {
        return "["
            + getClass().getName()
            + "]";
    }

    public static void main( String[] args )
    {
        ComponentMover cm = new ComponentMover();
        cm.setEdgeInsets( new Insets(-100, -100, -100, -100) );
//      cm.setEdgeInsets( new Insets(10, 10, 10, 10) );
        cm.setAutoLayout(true);

        JPanel panel = new JPanel( new DragLayout() );
        panel.setBorder( new MatteBorder(10, 10, 10, 10, Color.YELLOW) );

        createLabel(cm, panel, "North", 150, 0);
        createLabel(cm, panel, "West", 0, 100);
        createLabel(cm, panel, "East", 300, 100);
        createLabel(cm, panel, "South", 150, 200);
        createLabel(cm, panel, "Center", 150, 100);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new JScrollPane(panel) );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }

    public static void createLabel(ComponentMover cm, JPanel panel, String text, int x, int y)
    {
        JLabel label = new JLabel( text );
        label.setOpaque(true);
        label.setBackground( Color.ORANGE );
        label.setLocation(x, y);
        panel.add( label );
        cm.registerComponent( label );
    }

}
  1. 对于此布局,尺寸始终假定为首选尺寸。你需要改变这个。当大小为 (0, 0) 时,可能将大小设置为首选大小。在确定父容器的首选大小时,您还需要使用组件的大小(不是其首选大小)。

  2. ComponentMover 类可以配置为允许您将组件拖动到父容器的边界之外或将组件保持在边界内。如果您允许将组件移动到边界之外,则会自动调整首选大小以考虑组件的新位置。

  3. 如果您将组件拖到顶部或左侧边界之外,那么所有组件都会移动(向右或向下),请确保没有组件具有负位置。

【讨论】:

  • +1:谢谢,真的很棒!当我有时间时,我将使用您的 ComponentResizer 重构我的所有代码。它的许可证呢?我也可以在闭源商业应用程序中使用它吗?
  • 查看Drag Layout 以获取此代码的最新版本。代码按原样提供,您可以随意使用代码。
【解决方案3】:

我想这取决于您希望它如何表现的细节。

不鼓励使用 null 布局管理器的主要原因是,使用它构建的界面只能以它们设计的大小使用 - 您无法调整 UI 的大小。如果这对您来说很好,请使用它。

我知道的另一个选项是 Netbeans 分发时使用的 AbsoluteLayout。您可以在此处获取更多信息: http://www.java-tips.org/other-api-tips/netbeans/can-i-distribute-absolutelayout-with-my-applica.html。 我认为这可能正是您正在寻找的东西,但正如您从该链接中看到的那样,他们建议宁愿使用 Null 布局......我认为这两种方式都没有太大区别。

如果您还需要允许用户定义组件的大小调整方式,那么您最终会构建 Netbeans Matisse 表单设计器之类的东西,这可能有点矫枉过正,并没有让我觉得很有趣 :)

【讨论】:

    【解决方案4】:

    这个问题有点模糊,所以我可能完全没有抓住重点。我假设您正在寻找一种允许您使用绝对定位但仍允许您调整组件大小并使用所有可用空间的布局。

    如果您手动编码,我已经成功使用 MIGLayout (http://www.miglayout.com/) 和 TableLayout(这不是绝对但非常易于使用 - http://java.sun.com/products/jfc/tsc/articles/tablelayout/

    如果您使用某些表单设计器,使用 GroupLayout 可能是一个不错的选择,但您不想手动编码。看到这个问题: GroupLayout: Is it worth learning?

    【讨论】:

    • 感谢您的回答,但我正在寻找不同的东西。看看我发布的链接。我需要绝对定位组件并动态改变它们的边界。我已经以类似于链接文章的方式实现了它,但我知道不鼓励使用空布局管理器。所以我想知道是否有一个布局管理器允许这样的操作或者我必须实现它。
    • 对不起,我做了太多假设并回答了错误的问题......我添加了一个新答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 2013-08-09
    • 1970-01-01
    相关资源
    最近更新 更多