java JTable - 拖放

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/715332/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 13:29:17  来源:igfitidea点击:

JTable - drag and drop

javaswingdrag-and-dropjtable

提问by stdave

OK, this problem is out of my league. I'm trying to implement a GUI widget in swing that allows files to be dropped onto a JTable, and allows the rows of the JTable to be dragged for re-sorting. Think VLC's playlists or the one in iTunes.

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

I got dropping files from the OS (Explorer, Finder, etc.) working just fine, but I'm having an impossible time with re-arranging the rows of the table, once the files are in. The problem is that when I add a custom TransferHandler to the table, dragging fromthe table is instantly killed. Here is some example code:

我从操作系统(资源管理器、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);
    }
}

Run this code as-is and you'll see that you cannot initiate a drag on the table.If you comment out the call to setTransferHandler() on the table, dragging works (i.e., when I start dragging a table row, I get the X'd out circle cursor saying I can't drop there). But as soon as a TransferHandler is set for the table, I can't drag any rows. The problem has to be in the TransferHandler, but I've thoroughly troubleshot and debugged it, and have determined that dragging is never started once there is a TransferHandler on the table. What am I doing wrong?

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

回答by Gubatron

I had the same problem, it's got nothing to do with your custom implementation of the TransferHandler. When you replace the TransferHandler you also need to get a hold of the default DragSource and tell it to recognize drag gesture. You may also need to implement your own Transferable because you'll need to pass it to the DragGestureEvent.startDrag() method.

我遇到了同样的问题,这与您对 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);
        }
    });

回答by Erik A. Brandstadmoen

It doesn't look like you are using the TransferHandler properly. Try to read through the tutorial here.

看起来您没有正确使用 TransferHandler。尝试通读此处的教程。

See the TransferHandler doc here. The empty constructor doesn't look like it's meant for usage outside a subclass of TransferHandler.

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

And you don't implement any of the functionality provided in the standard TransferHandler provided on Swing components. See exerpt from the DnD tutorial here(my bold):

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

Note: If you install a custom TransferHandler onto a Swing component, the default support is replaced. For example, if you replace JTextField's TransferHandler with one that handles colors only, you will disable its ability to support import and export of text. If you must replace a default TransferHandler — for example, one that handles text — you will need to re-implement the text import and export ability. This does not need to be as extensive as what Swing provides — it could be as simple as supporting the StringFlavor data flavor, depending on your application's needs.

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

回答by Dave

I think the problem is that the empty TransferHandler actually prevents DnD events from occurring. There is a sample here which may be relevant.

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

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

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

回答by rancidfishbreath

I did not want to get into the nuts and bolts of what was going on so I just delegated the methods I wasn't interested to the old TransferHandler.

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

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

Start with a standard setup but pass the old TransferHandler into your custom TransferHandler.

从标准设置开始,但将旧的 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);
  }
}

One gotcha is that createTransferable(JComponent) and exportDone(JComponent, Transferable, int) methods are protected so you need to do reflection in order to delegate to those methods. When I didn't do this reflection delegation the strategy did not work. Once I did this delegation drag and drop worked as expected without changing the DragSource or having to write a new Transferable.

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