java/swing:两个子窗口的拖放问题
Posted
技术标签:
【中文标题】java/swing:两个子窗口的拖放问题【英文标题】:java/swing: drag and drop problems with two child windows 【发布时间】:2011-01-14 20:39:36 【问题描述】:我有两个子窗口,我想以两种不同的方式接受拖放对象的拖放。
一个是 JPanel,我想接受一个文件列表。它工作正常。
另一个是我想接受字符串列表的 JTable。如果我不启用 JPanel 作为放置目标,它可以正常工作。
当我尝试同时启用两者时,JPanel 放置目标似乎掩盖了 JTable 放置目标,即使 JTable 不是 JPanel 的子窗口并且两个组件位于不重叠的区域中。
同时包含它们的 JFrame 似乎也有下拉图标...不知道为什么。
有什么建议吗?这很难调试。
下面是类似的示例应用程序,这里我根本无法让 JTable 接受丢弃。 :/
我想了解另一个问题:如果我从“拖动源 1”拖动到“放置目标 1”但不释放鼠标,则每次拖动源 1 标签更改时我的光标都会闪烁(每秒一次在这个测试应用程序中)。 为什么要这样做?有没有办法阻止它闪烁?
package com.example.test.gui;
import java.awt.Component;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
public class DragAndDropQuestion
public enum Color
RED, ORANGE, YELLOW, GREEN, BLUE, VIOLET, WHITE, BLACK;
@Override public String toString()
return this.name().toLowerCase();
static public Color randomColor(Random r)
Color[] colors = Color.values();
return colors[r.nextInt(colors.length)];
public static class SampleBean
final private Color color;
final private char letter;
final static public DataFlavor dataFlavor =
new DataFlavor(SampleBean.class, "SampleBean");
public Color getColor() return this.color;
public char getLetter() return this.letter;
private SampleBean(Color color, char letter)
this.color = color;
this.letter = letter;
static public SampleBean randomBean(Random r)
String letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return new SampleBean(Color.randomColor(r),
letters.charAt(r.nextInt(letters.length())));
@Override public String toString()
return this.color.toString()+" "+this.letter;
public static class BorderPanel extends JPanel
public BorderPanel(String title)
setBorder(BorderFactory.createTitledBorder(title));
public static class BeanTransferable implements Transferable
final private SampleBean value;
static final private List<DataFlavor> flavors = Arrays.asList(
DataFlavor.stringFlavor,
SampleBean.dataFlavor
);
public BeanTransferable(SampleBean x) this.value=x;
@Override public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException
if (flavors.get(0).equals(flavor))
return this.value.toString();
else if (flavors.get(1).equals(flavor))
return this.value;
else
throw new UnsupportedFlavorException(flavor);
@Override public DataFlavor[] getTransferDataFlavors()
return flavors.toArray(new DataFlavor[0]);
@Override public boolean isDataFlavorSupported(DataFlavor flavor)
return flavors.contains(flavor);
public abstract static class SimpleDropTarget
implements DropTargetListener
final private String debugString;
public SimpleDropTarget(String debugString)
this.debugString=debugString;
@Override public void dragEnter(DropTargetDragEvent event)
System.out.println(this.debugString+":dragEnter");
@Override public void dragExit(DropTargetEvent event)
System.out.println(this.debugString+":dragExit");
@Override public void dragOver(DropTargetDragEvent event)
@Override public void dropActionChanged(DropTargetDragEvent event)
public void install(JComponent component)
new DropTarget(component, this);
public abstract static class SimpleDragSource
implements DragSourceListener, DragGestureListener
final private String debugString;
final private DragSource ds = new DragSource();
public SimpleDragSource(String debugString)
this.debugString=debugString;
@Override public void dragDropEnd(DragSourceDropEvent event)
@Override public void dragEnter(DragSourceDragEvent event)
System.out.println(this.debugString+":dragEnter");
@Override public void dragExit(DragSourceEvent event)
System.out.println(this.debugString+":dragExit");
@Override public void dragOver(DragSourceDragEvent event)
@Override public void dropActionChanged(DragSourceDragEvent event)
public void install(JComponent component)
DragGestureRecognizer dgr =
this.ds.createDefaultDragGestureRecognizer(component,
DnDConstants.ACTION_COPY, this);
abstract public Transferable prepareTransferable();
@Override public void dragGestureRecognized(DragGestureEvent dge)
this.ds.startDrag(dge,
DragSource.DefaultCopyDrop, prepareTransferable(), this);
public static class BeanListModel implements TableModel
final private List<SampleBean> list = new ArrayList<SampleBean>();
enum ColumnType
COLOR(Color.class, "color")
@Override public Color extractValue(SampleBean sampleBean)
return sampleBean.getColor();
,
LETTER(String.class, "letter")
@Override public String extractValue(SampleBean sampleBean)
return Character.toString(sampleBean.getLetter());
,
;
final private Class<?> cl;
final private String name;
public Class<?> getColumnClass() return this.cl;
public String getColumnName() return this.name;
ColumnType(Class<?> cl, String name)
this.cl=cl; this.name=name;
abstract public Object extractValue(SampleBean sampleBean);
final static private ColumnType[] columns
= new ColumnType[]ColumnType.COLOR, ColumnType.LETTER;
@Override
public void addTableModelListener(TableModelListener arg0)
@Override
public void removeTableModelListener(TableModelListener arg0)
@Override public Class<?> getColumnClass(int column)
return columns[column].getColumnClass();
@Override public int getColumnCount()
return columns.length;
@Override public String getColumnName(int column)
return columns[column].getColumnName();
@Override public int getRowCount() return list.size();
@Override public Object getValueAt(int row, int column)
return columns[column].extractValue(list.get(row));
@Override public boolean isCellEditable(int row, int column)
return false;
@Override public void setValueAt(Object obj, int row, int column)
throw new UnsupportedOperationException();
public void addBean(SampleBean bean)
this.list.add(bean);
public static class BeanTablePanel extends BorderPanel
final private JTable table;
final private BeanListModel tableModel;
public BeanTablePanel(String title)
super(title);
this.table = new JTable();
this.tableModel = new BeanListModel();
this.table.setModel(this.tableModel);
add(new JScrollPane(this.table));
SimpleDropTarget dt = new SimpleDropTarget("BeanTable")
@Override public void drop(DropTargetDropEvent dtde)
Transferable tr = dtde.getTransferable();
if (tr.isDataFlavorSupported(SampleBean.dataFlavor))
try
SampleBean b = (SampleBean)
tr.getTransferData(SampleBean.dataFlavor);
addBean(b);
catch (UnsupportedFlavorException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
;
dt.install(this.table);
// !!! This doesn't seem to work...
void addBean(SampleBean b)
this.tableModel.addBean(b);
public static class BeanLabelPanel extends BorderPanel
final private JLabel label;
public BeanLabelPanel(String title)
super(title);
this.label = new JLabel("drop item here");
add(this.label);
SimpleDropTarget dt = new SimpleDropTarget("BeanLabel")
@Override public void drop(DropTargetDropEvent dtde)
Transferable tr = dtde.getTransferable();
if (tr.isDataFlavorSupported(DataFlavor.stringFlavor))
try
String s = (String)
tr.getTransferData(DataFlavor.stringFlavor);
setLabel(s);
catch (UnsupportedFlavorException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
;
dt.install(this.label);
void setLabel(String s) this.label.setText(s);
public static class BeanSourcePanel extends BorderPanel
final private JLabel label;
final private Random randomizer;
private SampleBean x;
public BeanSourcePanel(
String title, ScheduledExecutorService scheduler)
super(title);
this.label = new JLabel(" ");
this.randomizer = new Random();
add(this.label);
scheduler.scheduleAtFixedRate(new Runnable()
public void run() changeBean(); ,
0, 1000, TimeUnit.MILLISECONDS);
(new SimpleDragSource("RandomBean")
@Override public Transferable prepareTransferable()
return new BeanTransferable(getBean());
).install(this.label);
public SampleBean getBean() return this.x;
void changeBean()
this.x = SampleBean.randomBean(this.randomizer);
this.label.setText(this.x.toString());
public static class DNDQFrame extends JFrame
public DNDQFrame(String title, ScheduledExecutorService scheduler)
setTitle(title);
getContentPane().setLayout(
new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS)
);
add(new JLabel("Drag and Drop Question"));
add(new BeanSourcePanel("drag source 1", scheduler));
add(new BeanLabelPanel("drop target 1"));
add(new BeanTablePanel("drop target 2"));
@Override public Component add(Component component)
if (component instanceof JComponent)
((JComponent) component).setAlignmentX(0.0f);
return super.add(component);
static public void main(String[] args)
ScheduledExecutorService scheduler =
new ScheduledThreadPoolExecutor(1);
DNDQFrame frame = new DNDQFrame("DragAndDropQuestion", scheduler);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【问题讨论】:
你能发布一段简化的代码吗?即使它是您的 DnD 代码的摘录,也会有所帮助。如果代码很多,请使用 pastebin。使用 UI 代码,如果我可以构建并运行它,我可以更快地调试它。 现在正在处理它......(顺便说一句,什么是 pastebin?) 一个网站,您可以在其中粘贴较大的代码摘录,它们会为您提供一个 URL - 例如 mysticpaste.com/new 或 pastebin.com。这样,您的问题在没有 2 页内联代码的情况下仍然可以阅读。 【参考方案1】:改为将放置侦听器添加到 JScrollPane。您已经嵌套了表格,因此它没有接收事件。此外,在您的 addBean() 方法中,您应该添加一个 table.revalidate() 否则它不会在您的 tablemodel 中显示更新的数据。
【讨论】:
另外,我没有体验到您所看到的闪烁。 谢谢。我改用了包含表格 + 滚动窗格的 JPanel,也从实现 TableModel 切换到扩展 AbstractTableModel 并使用对 fireTableRowsInserted() 的调用。以上是关于java/swing:两个子窗口的拖放问题的主要内容,如果未能解决你的问题,请参考以下文章