11

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

我从操作系统(资源管理器、查找器等)中删除文件工作得很好,但是一旦文件进入,我就无法重新排列表的行。问题是当我添加一个自定义的 TransferHandler 到 table,table 中拖动会立即被杀死。这是一些示例代码:

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 就永远不会开始拖动。我究竟做错了什么?

4

4 回答 4

6

我有同样的问题,它与您自定义的 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);
        }
    });
于 2010-03-03T23:35:54.973 回答
3

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

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

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

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

于 2009-04-07T19:16:18.797 回答
3

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

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

于 2009-04-07T19:26:01.363 回答
1

我不想深入了解正在发生的事情,所以我只是将我不感兴趣的方法委托给旧的 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。

于 2010-12-22T13:29:28.797 回答