JTable - 拖放

Posted

技术标签:

【中文标题】JTable - 拖放【英文标题】:JTable - drag and drop 【发布时间】:2010-10-17 10:50:01 【问题描述】:

好的,这个问题不在我的范围内。我正在尝试在 swing 中实现一个 GUI 小部件,它允许将文件拖放到 JTable 上,并允许拖动 JTable 的行以进行重新排序。想想 VLC 的播放列表或 iTunes 中的播放列表。

我从操作系统(Explorer、Finder 等)中删除文件工作得很好,但是一旦文件进入,我就无法重新安排表格的行。问题是当我向表格添加自定义 TransferHandler 时,从表格中拖动 会立即被杀死。下面是一些示例代码:

import javax.swing.*;

public class TableTest

    public static void main (String [] argv)
    
        // setup table data
        String [] columns = new String [] "Foo", "Bar", "Baz", "Quux";
        String [][] data = new String [][] "A", "B", "C", "D",
                        "1", "2", "3", "4",
                        "i", "ii", "iii", "iv";
        // create table
        JTable table = new JTable(data, columns);

        // set up drag and drop
        table.setDragEnabled(true);
        table.setDropMode(DropMode.INSERT_ROWS);
        table.setFillsViewportHeight(true);
        TransferHandler dnd = new TransferHandler() 
            // here be code to handle drops, and one would
            // presume drag exporting, too
        ;
        table.setTransferHandler(dnd);
        JScrollPane scroll = new JScrollPane(table);

        // create and show window
        JFrame window = new JFrame();
        window.getContentPane().add(scroll);
        window.pack();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setVisible(true);
    

按原样运行此代码,您将看到无法在表格上启动拖动。如果您在表格上注释掉对 setTransferHandler() 的调用,则可以进行拖动(即,当我开始拖动表格行时) ,我得到 X'd out 圆形光标说我不能放在那里)。但是一旦为表设置了 TransferHandler,我就不能拖动任何行。问题必须出在 TransferHandler 中,但我已经彻底解决并调试了它,并确定一旦桌子上有 TransferHandler 就永远不会开始拖动。我做错了什么?

【问题讨论】:

多么奇怪。我遇到同样的问题。我试图记住我是否做过任何工作,我有一个支持从和到拖的表格。 【参考方案1】:

我遇到了同样的问题,这与您自定义的 TransferHandler 实现无关。当您替换 TransferHandler 时,您还需要获取默认的 DragSource 并告诉它识别拖动手势。您可能还需要实现自己的 Transferable,因为您需要将其传递给 DragGestureEvent.startDrag() 方法。

    table.setTransferHandler(new MyTransferHandler());
    table.setDragEnabled(true);
    DragSource source = DragSource.getDefaultDragSource();
    source.createDefaultDragGestureRecognizer(table, DnDConstants.ACTION_COPY, new DragGestureListener() 

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) 
            //grab the selected files from the table model
            ArrayList<File> files = new ArrayList<File>();
            for (int row : table.getSelectedRows()) 
                files.add((File) dm.getValueAt(row, 1));
            

            //FileTransferable is a custom Transferable implementation
            Transferable transferable = new FileTransferable(files); 

            //and this is the magic right here
            dge.startDrag(null,transferable);
        
    );

【讨论】:

如果我省略了前两行 - setTransferHandlersetDragEnabled,对我有用。事实上,启用拖动设置为我产生了InvalidDnDOperationException: Drag and drop in progress。同样,我假设dm 指的是table.getModel() 完美无瑕。与我之前的海报相同的评论,省略前两行。【参考方案2】:

您似乎没有正确使用 TransferHandler。尝试通读教程here。

请参阅 TransferHandler 文档here。空的构造函数看起来不像是要在 TransferHandler 的子类之外使用。

而且您没有实现 Swing 组件提供的标准 TransferHandler 中提供的任何功能。请参阅 DnD 教程的摘录here(我的粗体字):

注意:如果您将自定义 TransferHandler 安装到 Swing 组件上,则会替换默认支持。例如,如果您将 JTextField 的 TransferHandler 替换为只处理颜色的,您将禁用它支持文本导入和导出的能力。 如果您必须替换默认的 TransferHandler(例如,处理文本的),您将需要重新实现文本导入和导出功能。这不需要像 Swing 提供的那样广泛——它可以像支持 StringFlavor 数据风格一样简单,具体取决于您的应用程序的需要。

【讨论】:

【参考方案3】:

我认为问题在于空的 TransferHandler 实际上阻止了 DnD 事件的发生。这里有一个可能相关的示例。

http://www.java2s.com/Code/Java/Swing-JFC/ExtendedDnDDragandDropDemo.htm

【讨论】:

谢谢。这个例子很棒.. :)【参考方案4】:

我不想深入了解正在发生的事情,所以我只是将我不感兴趣的方法委托给旧的 TransferHandler。

tree.setDragEnabled(true);
tree.setDropMode(DropMode.XXXX);
tree.setTransferHandler(new MyTransferHandler(tree.getTransferHandler());

从标准设置开始,但将旧的 TransferHandler 传递给您的自定义 TransferHandler。

private class MyTransferHandler extends TransferHandler 
  private TransferHandler delegate;

  public MyTransferHandler(TransferHandler delegate) 
    this.delegate = delegate;
  

  public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) 
    return delegate.canImport(comp, transferFlavors);
  

  public boolean canImport(TransferSupport support) 
    return true;
  

  protected Transferable createTransferable(JComponent c) 
    try 
      Method method = delegate.getClass().getDeclaredMethod("createTransferable", JComponent.class);
      method.setAccessible(true);
      return (Transferable) method.invoke(delegate, c);
     catch (Exception e) 
      return super.createTransferable(c);
    
  

  public void exportAsDrag(JComponent comp, InputEvent event, int action) 
    delegate.exportAsDrag(comp, event, action);
  

  protected void exportDone(JComponent source, Transferable data, int action) 
    try 
      Method method = delegate.getClass().getDeclaredMethod("exportDone", JComponent.class, Transferable.class,
          int.class);
      method.setAccessible(true);
      method.invoke(delegate, source, data, action);
     catch (Exception e) 
      super.exportDone(source, data, action);
    
  

  public int getSourceActions(JComponent c) 
    return delegate.getSourceActions(c);
  

  public Icon getVisualRepresentation(Transferable t) 
    return delegate.getVisualRepresentation(t);
  

  public boolean importData(JComponent comp, Transferable t) 
    return delegate.importData(comp, t);
  

  public boolean importData(TransferHandler.TransferSupport support) 
    return delegate.importData(support);
  

一个问题是 createTransferable(JComponent) 和 exportDone(JComponent, Transferable, int) 方法受到保护,因此您需要进行反射才能委托给这些方法。当我没有做这个反射委托时,这个策略就不起作用了。一旦我完成了这个委托,拖放就可以按预期工作,而无需更改 DragSource 或编写新的 Transferable。

【讨论】:

草率的示例代码。 boolean 返回类型 canImport 不返回任何内容,而 void 返回类型 exportDone 则返回。 为什么这么马虎?在问题中,他特别指出他正在尝试将 TransferHandler 添加到表中以支持丢弃。他的全部问题是,当他这样做时,桌子的阻力支撑停止工作。幕后发生的事情是,当他提供自己的 TransferHandler 时,他破坏了桌面上安装的默认设置。这是一个如何从表中委托给原始 TransferHandler 的示例,秘密是使用反射来实现 createTransferable() 和 exportDone()。其他方法可以随意覆盖或委托。

以上是关于JTable - 拖放的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JTable 中拖放一行?

如何隐藏 Mac/OSX 拖放 JTable 选择框

chapter9 拖放

HTML5之拖拽和拖放

在 Mac OS X 上使用 JTable 进行拖放

#yyds干活盘点# 4.2 HTML5 拖放(Drag和Drop)