适用于可调整大小的组件的 LayoutManager

发布于 2024-12-09 08:17:14 字数 249 浏览 0 评论 0原文

不久前,我读了这篇文章,它展示了在 Swing 中实现鼠标可调整大小的组件的方法。

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

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

sometime ago I read this article that shows a way to implement mouse resizable components in Swing.

The author uses a null LayoutManager in order to allow absolute component positioning.
I know that a null layout should never be used, so my question is:

is there any already implemented LayoutManager that allow component's absolute positioning, or I have to implement it my own?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧时光的容颜 2024-12-16 08:17:14

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

As alternatives, also consider

献世佛 2024-12-16 08:17:14

布局管理器实际上做了三件事:

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

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

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

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

我一直在使用接近您的需求的布局管理器。它设计为与 移动 Windows< 中的 ComponentMover 类一起使用/code>链接由trashgod 提供。

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

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. 如果您将组件拖动到顶部或左侧边界之外,则所有组件都会移动(向右或向下),请确保没有组件具有负位置。

A layout manager really does 3 things:

  1. Set the location of a component. Since you need the ability to drag the component around, you would not want your layout manager to do this.

  2. Set the size of a component. Since you need the ability to resize the component then you would not want to do this. However, you might want to give the component a default size based on the components preferred size. This way you don't need to specify the size when you create the component.

  3. Determine the preferred size of the parent panel based on the components added to it. This will allow scroll panes to function properly as scrollbars can be added/removed as required. So you need to determine the behaviour of how dragging should work. That is, are you allowed to drag the component outside the current bounds of the panel. If so the the preferred size of the panel should automatically increase.

is there any already implemented LayoutManager that allow component's absolute positioning

I've been playing around with a layout manager that is close to your needs. It was designed to be used with the ComponentMover class from the Moving Windows link provided by trashgod.

Here is my test code for this class:

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. For this layout the size is always assumed to be the preferred size. You would need to change this. Maybe set the size to be the preferred size when the size is (0, 0). You will also need to use the size of the component (not its preferred size) when determining the preferred size of the parent container.

  2. The ComponentMover class can be configured to allow you to drag comopnents outside the bounds of the parent container or to keep the component inside the bounds. If you allow components to be moved outside the bounds, then the preferred size is automatically adjusted to take into account the new location of the component.

  3. If you drag a component outside the top or left bounds, then all the components are shifted (right or down) do make sure no component has a negative location.

ˇ宁静的妩媚 2024-12-16 08:17:14

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

不鼓励使用 null 布局管理器的主要原因是使用它构建的界面只能按其设计的尺寸使用 - 您无法调整 UI 的大小。如果这适合您,请使用它。

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

如果您还需要能够允许用户定义组件如何调整大小,那么您最终将构建类似 Netbeans Matisse 表单设计器的东西,这可能有点过头了,而且对我来说也没有那么有趣:)

I guess it would depend on the specifics of how you wanted it to behave.

The main reason the null layout manager is discouraged is because of the fact that interfaces built using that can only be used in the size they were designed - You can't resize the UI. If this is fine for you, use it.

Another option I know of is the AbsoluteLayout that Netbeans is distributed with. You can get more info here:
http://www.java-tips.org/other-api-tips/netbeans/can-i-distribute-absolutelayout-with-my-applica.html.
I think this might be exactly what you are looking for, but as you can see from that link, they recommend rather using a Null layout... I don't think it makes much of a difference either way.

If you need to be able to allow users to define how the components will resize as well, you'll end up building something like the Netbeans Matisse form designer, which is probably overkill and does not strike me as much fun :)

梦冥 2024-12-16 08:17:14

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

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

如果您正在使用某些表单设计器,那么使用 GroupLayout 可能是一个不错的选择,但您不想手动编码。看到这个问题:
GroupLayout:值得学习吗?

The question is somewhat vague, so I might be missing the point completely. I assume that you are looking for a layout that will allow you to use absolute positioning, but will still allow you to resize the component and use all available space.

If you are handcoding it, I've had success with MIGLayout (http://www.miglayout.com/) and TableLayout (Which is less absolute but very easy to use - http://java.sun.com/products/jfc/tsc/articles/tablelayout/)

If you are using some Form designer, using GroupLayout might be a good choice, but you do not want to hand-code it. See this question :
GroupLayout: Is it worth learning?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文