我有一些行的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);
当您在行之间移动行时,只有一条粗线(如图中的红色椭圆中所示)。如果不可能像这样移动行,我希望这条粗线显示为整行(不仅仅是在一个单元格中)。
答案 0 :(得分:3)
阅读基于拖放的Swing教程中的基础知识部分。
以下是我在网络上找到的一些旧代码,其中包含一个支持JTable的自定义TransferHandler
:
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;
}
}
}
答案 1 :(得分:3)
我认为ListTransferHandler.java中JList
的{{1}}项目交换的Swing tutorial也会有所帮助。
以下是我为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;
}
}