Менеджер по макету действительно делает 3 вещи:
Установить местоположение компонента. Поскольку вам нужна возможность перетаскивать компонент, вы не хотели бы, чтобы ваш менеджер компоновки делал это.
Установить размер компонента. Поскольку вам нужна возможность изменить размер компонента, вы не захотите этого делать. Однако может потребоваться задать для компонента размер по умолчанию на основе предпочтительного размера компонентов. Таким образом, вам не нужно указывать размер при создании компонента.
Определите предпочтительный размер родительской панели на основе добавленных к ней компонентов. Это позволит панелям прокрутки функционировать должным образом, так как полосы прокрутки могут быть добавлены / удалены по мере необходимости. Так что вам нужно определить, как должно работать перетаскивание. То есть разрешено ли перетаскивать компонент за пределы текущих границ панели. В этом случае предпочтительный размер панели должен автоматически увеличиться.
есть ли уже реализованный LayoutManager, позволяющий абсолютное позиционирование компонента
Я играл с менеджером по расположению, который близок к вашим потребностям. Он был разработан для использования с классом ComponentMover из ссылки Moving Windows
, предоставленной 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 );
}
}
Для этого макета размер всегда считается предпочтительным размером. Вам нужно изменить это. Может быть, установить размер, чтобы быть предпочтительным размером, когда размер (0, 0). Вам также потребуется использовать размер компонента (а не его предпочтительный размер) при определении предпочтительного размера родительского контейнера.
Класс ComponentMover можно настроить так, чтобы он мог перетаскивать компоненты за пределы родительского контейнера или держать компонент внутри границ. Если разрешить перемещение компонентов за пределы границ, предпочтительный размер автоматически корректируется с учетом нового расположения компонента.
Если вы перетаскиваете компонент за пределы верхней или левой границ, то все компоненты смещаются (вправо или вниз), убедитесь, что ни один из компонентов не имеет отрицательного расположения.