如何在Java拖拽释放操作中显示JPopupMenu?
问题原因与解决方案
你的问题出在拖拽事件的回调线程不是Swing事件调度线程(EDT),Swing的所有UI操作都必须在EDT中执行,否则会出现组件无法正常渲染的情况——这就是你调用show()后弹窗没显示的原因。不需要改用JOptionPane,只需要把弹窗的创建和显示逻辑放到EDT中执行即可。
修改后的代码
import javax.swing.*; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDropEvent; import static java.awt.dnd.DnDConstants.ACTION_COPY; public class DNDTest { DNDTest() { JFrame f = new JFrame("Test dnd"); Container c = f.getContentPane(); final JPanel jp = new JPanel(new BorderLayout()); jp.setBackground(Color.RED); jp.setToolTipText("Drop a string here"); jp.setPreferredSize(new Dimension(200,200)); c.add(jp); DropTarget dt = new DropTarget(jp, ACTION_COPY, new DropTargetAdapter() { @Override public void drop(DropTargetDropEvent dtde) { if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { dtde.acceptDrop(ACTION_COPY); String trns = (String) dtde.getTransferable().getTransferData(DataFlavor.stringFlavor); System.err.println("Dropped string "+trns); final Point loc = dtde.getLocation(); // 将弹窗显示逻辑放到EDT中执行 SwingUtilities.invokeLater(() -> { JPopupMenu jpm2 = new JPopupMenu("Popup menu"); JMenuItem jmi2 = new JMenuItem("Popup option 1"); jpm2.add(jmi2); System.err.println("About to show popup after drop at "+loc.getX()+","+loc.getY()); jpm2.show(jp, (int)loc.getX(), (int)loc.getY()); }); } catch (Exception ex) { ex.printStackTrace(); } return; } dtde.rejectDrop(); } }, true); f.pack(); f.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(DNDTest::new); } }
补充说明
- Swing的线程规则:所有涉及组件创建、修改、渲染的操作都必须在EDT中完成,非EDT线程直接操作UI会导致不可预测的行为,比如组件不显示、界面卡顿等。
SwingUtilities.invokeLater()会把传入的Runnable任务放到EDT的事件队列中,等待EDT空闲时执行,确保UI操作的线程安全性。
内容的提问来源于stack exchange,提问作者opeongo
相关产品推荐
相关产品推荐

