我注意到,如果我在处理丢弃DropTargetListener.drop()
时执行removeAll()
,add()
,revalidate()
,则丢弃将成功,更改将生效但我赢了能够开始第二次拖动。任何后续拖动尝试都将抛出java.awt.dnd.InvalidDnDOperationException: Drag and drop in progress
。
似乎如果对层次结构进行更改,则不会清除拖动,并且Swing认为仍有一个正在进行的拖动等待完成。即使在调用DropTargetDropEvent.acceptDrop()
和DropTargetDropEvent.dropComplete()
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.dnd.*;
import javax.activation.*;
import java.awt.datatransfer.*;
import java.util.TooManyListenersException;
class MyWidget extends JComponent {
MyWidget() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
updateState();
}
void updateState() {
Runnable runnable = new Runnable() {
@Override
public void run() {
// System.out.println("Hello World on " + Thread.currentThread());
removeAll();
add(newButton("aaa"));
add(newButton("bbb"));
revalidate();
}
};
if (SwingUtilities.isEventDispatchThread()) {
runnable.run();
} else {
SwingUtilities.invokeLater(runnable);
}
}
JButton newButton(String text) {
JButton theButton = new JButton(text);
DragSource ds = new DragSource();
DragGestureRecognizer dgr = ds.createDefaultDragGestureRecognizer(theButton, DnDConstants.ACTION_COPY, new DragGestureListener() {
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
Transferable transferable = new DataHandler("my text", "text/plain");
dge.startDrag(DragSource.DefaultCopyDrop, transferable);
}
});
DropTarget dt = new DropTarget();
dt.setComponent(theButton);
try {
dt.addDropTargetListener(new DropTargetAdapter() {
@Override
public void drop(DropTargetDropEvent dtde) {
System.out.println("drop accepted");
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
dtde.dropComplete(true);
updateState();
}
});
} catch (TooManyListenersException e) {}
return theButton;
}
}
public class App
{
static void createGUI() {
JFrame frame = new JFrame();
frame.setTitle("my app");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MyWidget(), BorderLayout.CENTER);
frame.setSize(500,500);
// frame.pack();
frame.setVisible(true);
}
public static void main( String[] args )
{
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.out.println("Hello World on " + Thread.currentThread());
createGUI();
}
});
}
}
答案 0 :(得分:1)
问题是在拖动尚未完成时发生组件层次结构更改。调用DropTargetDropEvent.dropComplete(true)
后,可能会完成删除但不会拖动部分。在当前DropTargetListener.drop()
(当前正在运行的EDT)将控制权返回到EDT主循环之后,拖拽侧可能会在EDT中运行回调。
因此必须延迟组件层次结构更改,以便在拖动操作完成后运行它们。
所以而不是
if (SwingUtilities.isEventDispatchThread()) {
runnable.run();
} else {
SwingUtilities.invokeLater(runnable);
}
运行
SwingUtilities.invokeLater(runnable); // unconditionally
将调度runnable在拖动后运行,因为dropComplete()
已经在EDT事件队列中安排了一个事件来处理拖动完成。因此,invokeLater()
将在将事件标记为已完成的事件之后放置更改组件层次结构的代码。