我有一个容器,可以在其中拖动组件。我遇到的问题是,每当我拿起一个组件并将其自动添加到窗口时,即使鼠标仍在手柄上拖动,该组件也会停止触发拖动事件。然后下次我点击并拖动手柄(现在在浮动窗口中(时,它将继续拖动。以下是一些基本代码:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Box.Filler;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
/**
*
*/
@SuppressWarnings("serial")
public class DraggableDemo extends JPanel {
private class DraggablePanel extends JPanel {
private class MyMouseAdapter extends MouseAdapter {
private DraggablePanel floater;
private Point dragOffset;
@Override
public void mouseDragged(MouseEvent e) {
if (floater == null) {
startDragging(e);
}
Point transformedEventPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), owner);
updateWindowLocation(transformedEventPoint);
}
private void startDragging(MouseEvent e) {
Component component = e.getComponent();
floater = (DraggablePanel) SwingUtilities.getAncestorOfClass(DraggablePanel.class, component);
Point floaterAbsoluteLocation = SwingUtilities.convertPoint(floater.getParent(), floater.getLocation(), owner);
Point transformedEventPoint = SwingUtilities.convertPoint(component, e.getPoint(), owner);
// designate a drag offset so the component's corner doesn't teleport to mouse location
dragOffset = new Point(transformedEventPoint.x - floaterAbsoluteLocation.x, transformedEventPoint.y - floaterAbsoluteLocation.y);
swapComponents(getFiller(), floater);
// place the floating component in a window
window.add(floater);
window.pack();
floater.setBorder(new LineBorder(Color.YELLOW));
updateWindowLocation(transformedEventPoint);
window.setVisible(true);
}
private void updateWindowLocation(Point point) {
Point p = new Point(point.x - dragOffset.x, point.y - dragOffset.y);
SwingUtilities.convertPointToScreen(p, owner);
window.setLocation(p);
}
private JComponent getFiller() {
Dimension dimension = floater.getSize();
JComponent filler = (JComponent) Box.createRigidArea(dimension);
// border
Border dashedBorder = BorderFactory.createDashedBorder(Color.gray, 3f, 3f, 2f, true);
filler.setBorder(dashedBorder);
filler.setOpaque(true);
return filler;
}
@Override
public void mouseReleased(MouseEvent e) {
Point transformedEventPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), owner);
Component compDroppedOn = SwingUtilities.getDeepestComponentAt(owner, transformedEventPoint.x, transformedEventPoint.y);
if (compDroppedOn instanceof Filler) {
window.remove(floater);
swapComponents(floater, compDroppedOn);
window.setVisible(false);
floater = null;
}
}
}
private JWindow window;
private MyMouseAdapter mouseAdapter;
private DraggableDemo owner;
DraggablePanel(DraggableDemo owner) {
this.owner = owner;
JPanel smallPanel = new JPanel();
smallPanel.setPreferredSize(new Dimension(200, 100));
smallPanel.setBackground(Color.green);
JPanel bigPanel = new JPanel();
bigPanel.setPreferredSize(new Dimension(200, 400));
bigPanel.setBackground(Color.red);
setLayout(new BorderLayout());
add(smallPanel, BorderLayout.NORTH);
add(bigPanel, BorderLayout.CENTER);
setBorder(new LineBorder(Color.blue));
mouseAdapter = new MyMouseAdapter();
smallPanel.addMouseListener(mouseAdapter);
smallPanel.addMouseMotionListener(mouseAdapter);
owner.addMouseListener(mouseAdapter);
owner.addMouseMotionListener(mouseAdapter);
window = new JWindow();
window.setAlwaysOnTop(true);
}
}
private GridBagConstraints gbc;
/**
*
*/
public DraggableDemo() {
setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
for (int i = 0; i < 2; i++) {
add(new DraggablePanel(this), gbc);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DraggableDemo newContentPane = new DraggableDemo();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
/**
*/
private void swapComponents(Component toAdd, Component toRemove) {
Container parent = toRemove.getParent();
int index = parent.getComponentZOrder(toRemove);
parent.remove(toRemove);
parent.add(toAdd, gbc, index);
revalidate();
repaint();
}
}
我尝试过的:
原始容器每列包含几个组件,每个组件都会触发不同的鼠标事件。
最初,我将鼠标侦听器注册到主容器中,并根据坐标获取和移动组件,但这不满足鼠标进入/退出较小组件本身触发事件的条件。
之后,我尝试注册多个鼠标侦听器,每个侦听器都做自己的事情,但我了解到这些侦听器会占用层次结构中发生的事件。
最后,我决定为我需要注册的每个组件注册一个侦听器,并根据事件返回的组件实例,将这些事件委托给适当的鼠标"适配器"。
如何修复零部件以便在拾取时正确拖动?正如我所说,我不能只注册到主容器,因为这样我就无法访问由较小组件触发的事件,也不能注册多个侦听器,因为事件会被第一个触发的侦听器吃掉。
每当我拿起一个组件并将其自动添加到窗口中时,该组件就会停止触发拖动事件
停止触发拖动事件的不仅仅是组件。似乎没有为任何组件生成拖动事件:
在您的createAndShowGUI()
方法中,在帧可见后,我添加了:
long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK + AWTEvent.MOUSE_EVENT_MASK;
Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
{
public void eventDispatched(AWTEvent e)
{
System.out.println(e.getID());
}
}, eventMask);
它应该显示为任何组件生成的所有鼠标事件。但是,一旦显示窗口,就不会为任何组件生成进一步的事件,从而确认您的问题。
接下来,我删除了上面的代码,并将其替换为自定义EventQueue,以简单地显示生成的每个事件:
EventQueue queue = new EventQueue()
{
protected void dispatchEvent(AWTEvent event)
{
System.out.println(event);
super.dispatchEvent(event);
}
};
Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue);
现在我看到了所有的鼠标拖动事件。
因此,也许您可以创建一个自定义EventQueue来处理在DraggablePanel上生成的鼠标事件?