Как перетащить строку в JTable?

Как настроить JTable, чтобы иметь возможность перетаскивать строку в другой индекс в таблице. Например, если у меня 5 строк, и я хочу перетащить 4-ю строку на 2-ю позицию?


person ShawnD    schedule 12.03.2009    source источник
comment
Помог ли какой-либо из ответов ниже?   -  person Ascalonian    schedule 16.03.2009


Ответы (5)


Ознакомьтесь с разделом перетаскивания на странице Java. Руководство. Есть несколько примеров того, как реализовать это для JTable.

person Bogdan    schedule 13.03.2009

Следующее позволяет JTable переупорядочивать одну перетаскиваемую строку:

  table.setDragEnabled(true);
  table.setDropMode(DropMode.INSERT_ROWS);
  table.setTransferHandler(new TableRowTransferHandler(table)); 

Ваша TableModel должна реализовать следующее, чтобы разрешить изменение порядка:

public interface Reorderable {
   public void reorder(int fromIndex, int toIndex);
}

Этот класс TransferHandler обрабатывает перетаскивание и вызывает reorder() в вашей TableModel, когда жест завершен.

/**
 * Handles drag & drop row reordering
 */
public class TableRowTransferHandler extends TransferHandler {
   private final DataFlavor localObjectFlavor = new ActivationDataFlavor(Integer.class, "application/x-java-Integer;class=java.lang.Integer", "Integer Row Index");
   private JTable           table             = null;

   public TableRowTransferHandler(JTable table) {
      this.table = table;
   }

   @Override
   protected Transferable createTransferable(JComponent c) {
      assert (c == table);
      return new DataHandler(new Integer(table.getSelectedRow()), localObjectFlavor.getMimeType());
   }

   @Override
   public boolean canImport(TransferHandler.TransferSupport info) {
      boolean b = info.getComponent() == table && info.isDrop() && info.isDataFlavorSupported(localObjectFlavor);
      table.setCursor(b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
      return b;
   }

   @Override
   public int getSourceActions(JComponent c) {
      return TransferHandler.COPY_OR_MOVE;
   }

   @Override
   public boolean importData(TransferHandler.TransferSupport info) {
      JTable target = (JTable) info.getComponent();
      JTable.DropLocation dl = (JTable.DropLocation) info.getDropLocation();
      int index = dl.getRow();
      int max = table.getModel().getRowCount();
      if (index < 0 || index > max)
         index = max;
      target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      try {
         Integer rowFrom = (Integer) info.getTransferable().getTransferData(localObjectFlavor);
         if (rowFrom != -1 && rowFrom != index) {
            ((Reorderable)table.getModel()).reorder(rowFrom, index);
            if (index > rowFrom)
               index--;
            target.getSelectionModel().addSelectionInterval(index, index);
            return true;
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
      return false;
   }

   @Override
   protected void exportDone(JComponent c, Transferable t, int act) {
      if ((act == TransferHandler.MOVE) || (act == TransferHandler.NONE)) {
         table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
   }

}
person Aaron Davidson    schedule 22.01.2011
comment
Я получал исключения при передаче, которые я исправил, изменив localObjectFlavor на: private final DataFlavor localObjectFlavor = new DataFlavor(Integer.class, "Integer Row Index"); - person Koobz; 28.11.2011
comment
+1 Но, к сожалению, это не сработает для выбора нескольких строк. - person Stephan; 30.01.2013
comment
+1 - пара простых модификаций и все отлично работает! - person pstanton; 05.05.2013
comment
Хороший пример! Хотя заметил баг в exportDone. Если вы перетащите выбранное значение за пределы компонента и отпустите, курсор сохранит значок копирования. Я исправил это, изменив оператор if в exportDone на act == TransferHandler.MOVE || акт == TransferHandler.NONE. - person David; 27.01.2014
comment
Отличный код. Одна проблема, с которой я столкнулся, заключалась в том, что я продолжал получать ошибку java.lang.ClassCastException: java.lang.Integer cannot be cast to java.io.InputStream. Использование вместо этого localObjectFlavor = new ActivationDataFlavor(Integer.class, "application/x-java-Integer;class=java.lang.Integer", "Integer Row Index") решило ошибку. - person Ignas2526; 08.03.2016
comment
Я считаю использование TransferHandler очень сложным и ненужным... Я бы предложил решение, в котором используются прослушиватели Mouse(Motion): stackoverflow.com/questions/58043707/ - person ImJustACowLol; 22.09.2019

возможно что-л. нравится:

    table.addMouseMotionListener(new MouseMotionListener() {
    public void mouseDragged(MouseEvent e) {
        e.consume();
        JComponent c = (JComponent) e.getSource();
        TransferHandler handler = c.getTransferHandler();
        handler.exportAsDrag(c, e, TransferHandler.MOVE);
    }

    public void mouseMoved(MouseEvent e) {
    }
});
person Tobias    schedule 16.03.2009

Только для записей и переупорядочения нескольких строк:

использовать где-то....

 JTable table = t_objects;
    table.setDragEnabled(true);
    table.setDropMode(DropMode.INSERT_ROWS);
    table.setTransferHandler(new TableRowTransferHandler(table));

Это основной класс в приведенном выше ответе, я изменил его, чтобы он соответствовал потребностям многорядного DnD. Все, что я сделал, это использовал первую выбранную строку, а затем вычислил строки над местом перетаскивания. Удалены выбранные элементы и сохранены в списке объектов (объект массива строк). затем вставьте их обратно в вычисляемую строку. и, наконец, выберите удаленные/перетащенные строки, чтобы завершить процесс.

public class TableRowTransferHandler extends TransferHandler {

    private final DataFlavor localObjectFlavor = new DataFlavor(Integer.class, "Integer Row Index");
    private JTable table = null;

    public TableRowTransferHandler(JTable table) {
        this.table = table;
    }

    @Override
    protected Transferable createTransferable(JComponent c) {
        assert (c == table);
        return new DataHandler(new Integer(table.getSelectedRow()), localObjectFlavor.getMimeType());
    }

    @Override
    public boolean canImport(TransferHandler.TransferSupport info) {
        boolean b = info.getComponent() == table && info.isDrop() && info.isDataFlavorSupported(localObjectFlavor);
        table.setCursor(b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
        return b;
    }

    @Override
    public int getSourceActions(JComponent c) {
        return TransferHandler.COPY_OR_MOVE;
    }

    @Override
    public boolean importData(TransferHandler.TransferSupport info) {
        JTable target = (JTable) info.getComponent();
        JTable.DropLocation dl = (JTable.DropLocation) info.getDropLocation();
        int index = dl.getRow();
        int max = table.getModel().getRowCount();
        if (index < 0 || index > max) {
            index = max;
        }
        target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

        try {
            Integer rowFrom = (Integer) info.getTransferable().getTransferData(localObjectFlavor);
            if (rowFrom != -1 && rowFrom != index) {

                int[] rows = table.getSelectedRows();
                int dist = 0;
                for (int row : rows) {
                    if (index > row) {
                        dist++;
                    }
                }
                index -= dist;

                //**TableUtil** is a simple class that just copy, remove and select rows.

                ArrayList<Object> list = TableUtil.getSelectedList(table);
                TableUtil.removeSelected(table);
                ArrayList<Integer> sels = new ArrayList<Integer>();
                for (Object obj : list) {
                    sels.add(index);
                    TableUtil.addRowAt(table, obj, index++);
                }
                TableUtil.selectMultipleRow(table, sels);


                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    protected void exportDone(JComponent c, Transferable t, int act) {
        if (act == TransferHandler.MOVE) {
            table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        }
    }
}
person Soley    schedule 05.11.2014

Мне нравятся модификации Соли, но его код опирается на внешнюю библиотеку, и я не уверен, откуда он ее взял, поэтому я переписал ее так, чтобы класс TableUtil не понадобился...

 @Override
   public boolean importData(TransferHandler.TransferSupport info) {
     JTable target = (JTable) info.getComponent();
        JTable.DropLocation dl = (JTable.DropLocation) info.getDropLocation();
        int index = dl.getRow();
        int max = table.getModel().getRowCount();
        if (index < 0 || index > max) {
            index = max;
        }
        target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

        try {
            Integer rowFrom = (Integer) info.getTransferable().getTransferData(localObjectFlavor);
            if (rowFrom != -1 && rowFrom != index) {

                int[] rows = table.getSelectedRows();
                int iter = 0;
                for (int row : rows) {
                    if (index > row) {
                        index--;
                        ((Reorderable) table.getModel()).reorder(row - iter, index);
                    }

                    else {
                        ((Reorderable) table.getModel()).reorder(row, index);
                    }
                    index++;
                    iter++;
                }

                target.getSelectionModel().addSelectionInterval(index, index);

                return true;
            }

      } catch (Exception e) {
            String error = e.getMessage();
            JOptionPane.showMessageDialog(null, error, "Error", JOptionPane.ERROR_MESSAGE);
      }
      return false;
   }
person tambascot    schedule 22.06.2019