适用于可resize的组件的LayoutManager

前段时间我读到这篇文章 ,展示了一种在Swing中实现鼠标可resize的组件的方法。

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

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

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

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

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

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

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

我一直在玩一个接近你需要的布局管理器。 它被设计为与trashgod提供的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, px); y = Math.min(y, py); width = Math.max(width, px + d.width); height = Math.max(height, py + 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(px + x, py + 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类可以配置为允许您将comopnents拖动到父容器的边界之外,或者将组件保持在边界内。 如果允许将组件移动到边界之外,则会自动调整首选大小以考虑组件的新位置。

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

作为替代方案,也要考虑

  • 如何使用内部框架

  • 移动Windows一起调整组件大小

  • 一个现有的框架,如JGraph或JUNG 。

我想这取决于你想要它的表现方式。

不鼓励使用null布局管理器的主要原因是因为使用它构建的接口只能以它们设计的大小使用 – 您无法调整UI的大小。 如果这对您没问题,请使用它。

我所知道的另一个选项是Netbeans随附的AbsoluteLayout。 您可以在此处获取更多信息: http : //www.java-tips.org/other-api-tips/netbeans/can-i-distribute-absolutelayout-with-my-applica.html 。 我认为这可能正是您正在寻找的,但正如您从该链接中可以看到的,他们建议使用Null布局……我认为这不会产生太大的影响。

如果你需要能够允许用户定义组件的resize,那么你最终会建立类似Netbeans Matisse表单设计器的东西,这可能是一种过度杀伤,并没有给我带来太多乐趣:)

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

如果您正在手动编码,我已经成功使用了MIGLayout( http://www.miglayout.com/ )和TableLayout(这不是绝对的,但很容易使用 – http://java.sun.com/products/ jfc / tsc / articles / tablelayout / )

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