JTable - перетаскивание

Хорошо, эта проблема из моей лиги. Я пытаюсь реализовать виджет GUI в swing, который позволяет удалять файлы на 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 circle, говорящий, что я не могу туда попасть). Но как только TransferHandler установлен для таблицы, я не могу перетаскивать любые строки. Проблема должна быть в TransferHandler, но я тщательно устранил и отладил ее, и решил, что перетаскивание никогда не запускается, если на столе есть TransferHandler. Что я делаю неправильно?

Ответ 1

У меня была такая же проблема, она не имела никакого отношения к вашей пользовательской реализации TransferHandler. Когда вы заменяете TransferHandler, вам также необходимо удержать исходный DragSource и сообщить ему, чтобы распознать жест перетаскивания. Вам также может потребоваться реализовать свой собственный переносимый, потому что вам нужно передать его методу 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);
        }
    });

Ответ 2

Не похоже, что вы правильно используете TransferHandler. Попытайтесь прочитать учебник здесь.

См. документ TransferHandler здесь. Пустой конструктор не похож на использование вне подкласса TransferHandler.

И вы не реализуете никаких функций, предоставляемых в стандартном TransferHandler, поставляемом на компонентах Swing. См. Exerpt из учебника DnD здесь (мой жирный шрифт):

Примечание. Если вы устанавливаете пользовательский TransferHandler на компонент Swing, заменяется поддержка по умолчанию. Например, если вы замените JTextField TransferHandler на тот, который обрабатывает только цвета, вы отключите его способность поддерживать импорт и экспорт текста. Если вы должны заменить установленный по умолчанию TransferHandler - например, тот, который обрабатывает текст - вам нужно будет повторно реализовать возможности импорта и экспорта текста. Это не должно быть настолько обширным, как то, что предлагает Swing, - это может быть так же просто, как поддержка вкуса данных StringFlavor, в зависимости от потребностей вашего приложения.

Ответ 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.