当前位置: 首页 > 面试题库 >

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

谈渊
2023-03-14
问题内容

前一段时间,我阅读了这篇文章,该文章展示了一种在Swing中实现可调整鼠标大小的组件的方法。

作者使用null的LayoutManager以便进行绝对的组件定位。我知道不应该使用null布局,所以我的问题是:

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


问题答案:

布局管理器实际上执行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. 如果将组件拖动到顶部或左侧边界之外,则所有组件都将移动(向右或向下),请确保没有组件位于负位置。



 类似资料:
  • 我有一个包含多个的布局,底部有一个按钮。当键盘显示时,我希望我的布局保持在键盘上方。我搜索这个问题,发现应该在清单的标签中放入

  • 我有一个包含大量选项的JDialog,它工作得很好,但是我已经更改了它,默认情况下,除非用户单击Show Advanced按钮,否则某些选项是不可见的。 当他们这样做时,选项就会显示出来,但是因为对话框不够高,因为它的大小是基于那些选项被隐藏的,所以会添加一个垂直滚动条。 我希望对话框的大小足够大,当高级选项启用。我尝试创建显示高级选项的对话框,根据高级选项可见的情况调用pack()来适应 然后调

  • 我对JPanel和JFrame还是个新手,可能没有按照我应该的方式使用它们。我正在尝试创建Pong游戏,在创建JFrame并将JPanel添加到它之后,我可以调整JPanel的大小,但我不知道如何调整JFrame的大小以适应它。Game类扩展了JPanel。 主: 编辑: 将setSize更改为setPreferredSize,然后为JFrame调用pack()就可以解决所有问题。

  • 通常,列表可以实现为链表(遍历速度较慢),也可以实现为数组列表(插入元素时速度较慢)。 我想知道是否有可能使用处理器的MMU来更有效地实现列表,只要插入或删除一个元素,就可以重新映射而不是复制内存。这意味着数组中任何地方的索引和插入/删除速度都要达到O(1),比任何其他列表实现都要好。 我的问题是: 程序是否真的能够控制自己的虚拟内存,或者是否需要对操作系统进行更改 每个进程的页表条目数是否有限制

  • 我有定义了left、right和top约束的NSTextField(没有设置bottom约束)。我需要NSTextField增长,如果内容不能容纳在其中,并减少大小,如果有未使用的空间。 现在我有了:如果NSTextField有多行文本或太多内容,它会以奇怪的行为自动扩展,而且NSTextField在窗口调整大小时不会减小自己的大小。 在Swift上,我还没有找到任何简单的解决方案来解决这个问题(