【问题标题】:Move row with mouse in JTable在 JTable 中用鼠标移动行
【发布时间】:2017-03-02 13:03:17
【问题描述】:

我有一些行的 JTable。我希望当用鼠标移动一行(拖放)时,该行与鼠标和表格行一起移动,以便在鼠标移动时重新排序(如移动 JTable 的列)。 jsfiddle 正是我想要的,但我必须在 java swing 中完成(或下面的 gif 图像。示例来自 here,但我无法理解)。

我的 TransferHandler :

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;

    private static final Logger logger = Logger.getLogger(TableRowTransferHandler.class.getName());

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

    @Override
    protected Transferable createTransferable(JComponent c) {
        assert (c == table);
        return new DataHandler(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 (UnsupportedFlavorException | IOException e) {
            e.printStackTrace();
            logger.log(Level.SEVERE, null, e);
        }
        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));
        }
    }
}

这是我用于 JTable 的简单设置:

    childrenTable.setDragEnabled(true);
    childrenTable.setDropMode(DropMode.INSERT);
    childrenTable.setTransferHandler(new TableRowTransferHandler(childrenTable));
    childrenTable.setRowSelectionAllowed(true);
    childrenTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

当您在行之间用鼠标移动行时,只有一条粗线(如下图红色椭圆所示)。如果不可能像这样移动行,我希望这条粗线出现在整行(不仅在一个单元格中)。

【问题讨论】:

    标签: java swing jtable


    【解决方案1】:

    阅读 Swing 教程中有关拖放的部分以了解基础知识。

    这是我在网上某处找到的一些旧代码,带有自定义 TransferHandler 以支持 JTable:

    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.table.*;
    import java.awt.*;
    import java.awt.datatransfer.*;
    import java.awt.event.*;
    import java.io.IOException;
    public class DnD_Demo extends JFrame {
        public DnD_Demo() {
            setTitle("DnD Demo (Version 3)");
            JTextArea tips = new JTextArea("1. Select a row in Table A. " +
                    "Press the row again and drag. \n     " +
                    "As you drag the cursor icon over Table B, the row that is currently under the cursor highlights " +
                    "- the new data will be inserted after the selected row. \n     " +
                    "Drop the row onto Table B. Note that the row has been removed from Table A, " +
                    "and now appears in Table B. \n" +
                    "2. Select two rows from Table A and drop onto Table B. " +
                    "Now there are two new rows in Table B. ");
            tips.setEditable(false);
            tips.setBackground(new Color(255,255,204));
            tips.setBorder(new LineBorder(Color.orange,5));
            getContentPane().add(tips,BorderLayout.NORTH);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new JPanel(new GridLayout(2,1));
            panel.add(createTable("Table A"));
            panel.add(createTable("Table B"));
            getContentPane().add(panel,BorderLayout.CENTER);
            pack();
        }
        private JPanel createTable(String tableId) {
            DefaultTableModel model = new DefaultTableModel();
            model.addColumn("Column 0");
            model.addColumn("Column 1");
            model.addColumn("Column 2");
            model.addColumn("Column 3");
            model.addRow(new String[]{tableId+" 00", tableId+" 01", tableId+" 02", tableId+" 03"});
            model.addRow(new String[]{tableId+" 10", tableId+" 11", tableId+" 12", tableId+" 13"});
            model.addRow(new String[]{tableId+" 20", tableId+" 21", tableId+" 22", tableId+" 23"});
            model.addRow(new String[]{tableId+" 30", tableId+" 31", tableId+" 32", tableId+" 33"});
            JTable table = new JTable(model);
            table.getTableHeader().setReorderingAllowed(false);
            table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
            JScrollPane scrollPane = new JScrollPane(table);
            scrollPane.setPreferredSize(new Dimension(400,100));
            table.setDragEnabled(true);
            table.setTransferHandler(new TableTransferHandler());
            JPanel panel = new JPanel();
            panel.add(scrollPane);
            panel.setBorder(BorderFactory.createTitledBorder(tableId));
            return panel;
        }
        public static void main(String[] args) {
            new DnD_Demo().setVisible(true);
        }
    
        abstract class StringTransferHandler extends TransferHandler {
            protected abstract String exportString(JComponent c);
            protected abstract void importString(JComponent c, String str);
            protected abstract void cleanup(JComponent c, boolean remove);
            protected Transferable createTransferable(JComponent c) {
                return new StringSelection(exportString(c));
            }
            public int getSourceActions(JComponent c) {
                return COPY_OR_MOVE;
            }
            public boolean importData(JComponent c, Transferable t) {
                if (canImport(c, t.getTransferDataFlavors())) {
                    try {
                        String str = (String)t.getTransferData(DataFlavor.stringFlavor);
                        importString(c, str);
                        return true;
                    } catch (UnsupportedFlavorException ufe) {
                    } catch (IOException ioe) {
                    }
                }
                return false;
            }
            protected void exportDone(JComponent c, Transferable data, int action) {
                cleanup(c, action == MOVE);
            }
            public boolean canImport(JComponent c, DataFlavor[] flavors) {
                for (int i = 0; i < flavors.length; i++) {
                    if (DataFlavor.stringFlavor.equals(flavors[i])) {
                        return true;
                    }
                }
                return false;
            }
        }
        class TableTransferHandler extends StringTransferHandler {
            public JTable target;
            public int[] rows = null;
            public int addIndex = -1; //Location where items were added
            public int addCount = 0;  //Number of items added.
            protected String exportString(JComponent c) {
                JTable table = (JTable)c;
                rows = table.getSelectedRows();
                int colCount = table.getColumnCount();
                StringBuffer buff = new StringBuffer();
                for (int i = 0; i < rows.length; i++) {
                    for (int j = 0; j < colCount; j++) {
                        Object val = table.getValueAt(rows[i], j);
                        buff.append(val == null ? "" : val.toString());
                        if (j != colCount - 1) {
                            buff.append(",");
                        }
                    }
                    if (i != rows.length - 1) {
                        buff.append("\n");
                    }
                }
                return buff.toString();
            }
            protected void importString(JComponent c, String str) {
                target = (JTable)c;
                DefaultTableModel model = (DefaultTableModel)target.getModel();
                int index = target.getSelectedRow();
                //Prevent the user from dropping data back on itself.
                //For example, if the user is moving rows #4,#5,#6 and #7 and
                //attempts to insert the rows after row #5, this would
                //be problematic when removing the original rows.
                //So this is not allowed.
                if (rows != null && index >= rows[0] - 1 &&
                        index <= rows[rows.length - 1]) {
                    rows = null;
                    return;
                }
                int max = model.getRowCount();
                if (index < 0) {
                    index = max;
                } else {
                    index++;
                    if (index > max) {
                        index = max;
                    }
                }
                addIndex = index;
                String[] values = str.split("\n");
                addCount = values.length;
                int colCount = target.getColumnCount();
                for (int i = 0; i < values.length ; i++) {
                    model.insertRow(index++, values[i].split(","));
                }
                //If we are moving items around in the same table, we
                //need to adjust the rows accordingly, since those
                //after the insertion point have moved.
                if (rows!= null && addCount > 0) {
                    for (int i = 0; i < rows.length; i++) {
                        if (rows[i] > addIndex) {
                            rows[i] += addCount;
                        }
                    }
                }
            }
            protected void cleanup(JComponent c, boolean remove) {
                JTable source = (JTable)c;
                if (remove && rows != null) {
                    DefaultTableModel model =
                            (DefaultTableModel)source.getModel();
                    for (int i = rows.length - 1; i >= 0; i--) {
                        model.removeRow(rows[i]);
                    }
                }
                rows = null;
                addCount = 0;
                addIndex = -1;
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我认为ListTransferHandler.javaSwing tutorial 中用于JList 的项目交换也会有所帮助。

      这是我将ListTransferHandler 修改为JTable 的示例:

      import java.awt.*;
      import java.awt.datatransfer.*;
      import java.awt.dnd.*;
      import java.awt.event.*;
      import java.io.IOException;
      import java.util.*;
      import java.util.List;
      import javax.activation.*;
      import javax.swing.*;
      import javax.swing.table.*;
      
      public final class TableRowsDnDTest {
        private final TransferHandler handler = new TableRowTransferHandler();
        private final String[] columnNames = {"String", "Integer", "Boolean"};
        private final Object[][] data = {
          {"AAA", 12, true}, {"aaa", 1, false},
          {"BBB", 13, true}, {"bbb", 2, false},
          {"CCC", 15, true}, {"ccc", 3, false},
          {"DDD", 17, true}, {"ddd", 4, false},
          {"EEE", 18, true}, {"eee", 5, false},
          {"FFF", 19, true}, {"fff", 6, false},
          {"GGG", 92, true}, {"ggg", 0, false}
        };
        private final TableModel model = new DefaultTableModel(data, columnNames) {
          @Override public Class<?> getColumnClass(int column) {
            switch (column) {
            case 0:
              return String.class;
            case 1:
              return Number.class;
            case 2:
              return Boolean.class;
            default:
              return super.getColumnClass(column);
            }
          }
        };
        private final JTable table = new JTable(model);
      
        public JComponent makeUI() {
          table.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
          table.setTransferHandler(handler);
          table.setDropMode(DropMode.INSERT_ROWS);
          table.setDragEnabled(true);
          table.setFillsViewportHeight(true);
          //table.setAutoCreateRowSorter(true); //XXX
      
          //Disable row Cut, Copy, Paste
          ActionMap map = table.getActionMap();
          Action dummy = new AbstractAction() {
            @Override public void actionPerformed(ActionEvent e) { /* Dummy action */ }
          };
          map.put(TransferHandler.getCutAction().getValue(Action.NAME),   dummy);
          map.put(TransferHandler.getCopyAction().getValue(Action.NAME),  dummy);
          map.put(TransferHandler.getPasteAction().getValue(Action.NAME), dummy);
      
          JPanel p = new JPanel(new BorderLayout());
          p.add(new JScrollPane(table));
          p.setBorder(BorderFactory.createTitledBorder("Drag & Drop JTable"));
          return p;
        }
        public static void main(String... args) {
          EventQueue.invokeLater(() -> {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            f.getContentPane().add(new TableRowsDnDTest().makeUI());
            f.setSize(320, 240);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
          });
        }
      }
      
      //Demo - BasicDnD (Drag and Drop and Data Transfer) https://docs.oracle.com/javase/tutorial/uiswing/dnd/basicdemo.html
      //Demo - DropDemo (Drag and Drop and Data Transfer) https://docs.oracle.com/javase/tutorial/uiswing/dnd/dropmodedemo.html
      //@see https://docs.oracle.com/javase/tutorial/uiswing/examples/dnd/DropDemoProject/src/dnd/ListTransferHandler.java
      //@see https://github.com/aterai/java-swing-tips/blob/master/DnDReorderTable/src/java/example/TableRowsDnDTest.java
      class TableRowTransferHandler extends TransferHandler {
        private final DataFlavor localObjectFlavor;
        private int[] indices;
        private int addIndex = -1; //Location where items were added
        private int addCount; //Number of items added.
      
        protected TableRowTransferHandler() {
          super();
          localObjectFlavor = new ActivationDataFlavor(Object[].class, DataFlavor.javaJVMLocalObjectMimeType, "Array of items");
        }
        @Override protected Transferable createTransferable(JComponent c) {
          JTable table = (JTable) c;
          DefaultTableModel model = (DefaultTableModel) table.getModel();
          List<Object> list = new ArrayList<>();
          indices = table.getSelectedRows();
          for (int i : indices) {
            list.add(model.getDataVector().get(i));
          }
          Object[] transferedObjects = list.toArray();
          return new DataHandler(transferedObjects, localObjectFlavor.getMimeType());
        }
        @Override public boolean canImport(TransferHandler.TransferSupport info) {
          JTable table = (JTable) info.getComponent();
          boolean isDropable = info.isDrop() && info.isDataFlavorSupported(localObjectFlavor);
          table.setCursor(isDropable ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop);
          return isDropable;
        }
        @Override public int getSourceActions(JComponent c) {
          return TransferHandler.MOVE;
        }
        @Override public boolean importData(TransferHandler.TransferSupport info) {
          if (!canImport(info)) {
            return false;
          }
          TransferHandler.DropLocation tdl = info.getDropLocation();
          if (!(tdl instanceof JTable.DropLocation)) {
            return false;
          }
          JTable.DropLocation dl = (JTable.DropLocation) tdl;
          JTable target = (JTable) info.getComponent();
          DefaultTableModel model = (DefaultTableModel) target.getModel();
          int index = dl.getRow();
          int max = model.getRowCount();
          if (index < 0 || index > max) {
            index = max;
          }
          addIndex = index;
          target.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
          try {
            Object[] values = (Object[]) info.getTransferable().getTransferData(localObjectFlavor);
            addCount = values.length;
            for (int i = 0; i < values.length; i++) {
              int idx = index++;
              model.insertRow(idx, (Vector) values[i]);
              target.getSelectionModel().addSelectionInterval(idx, idx);
            }
            return true;
          } catch (UnsupportedFlavorException | IOException ex) {
            ex.printStackTrace();
          }
          return false;
        }
        @Override protected void exportDone(JComponent c, Transferable data, int action) {
          cleanup(c, action == TransferHandler.MOVE);
        }
      
        //If the remove argument is true, the drop has been
        //successful and it's time to remove the selected items
        //from the list. If the remove argument is false, it
        //was a Copy operation and the original list is left
        //intact.
        protected void cleanup(JComponent c, boolean remove) {
          if (remove && indices != null) {
            c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            DefaultTableModel model = (DefaultTableModel)((JTable) c).getModel();
            //If we are moving items around in the same list, we
            //need to adjust the indices accordingly, since those
            //after the insertion point have moved.
            if (addCount > 0) {
              for (int i = 0; i < indices.length; i++) {
                if (indices[i] >= addIndex) {
                  indices[i] += addCount;
                }
              }
            }
            for (int i = indices.length - 1; i >= 0; i--) {
              model.removeRow(indices[i]);
            }
          }
          indices  = null;
          addCount = 0;
          addIndex = -1;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2013-01-09
        • 2010-12-03
        • 2022-11-22
        • 1970-01-01
        • 2023-03-22
        • 2011-09-29
        • 1970-01-01
        • 1970-01-01
        • 2021-07-02
        相关资源
        最近更新 更多