Java: 如何在不使用 TransferHandler 的情况下拖动 JTable 行?
Java: How to drag JTable rows without usage of TransferHandler?
在 Java 中,JTable
默认不提供拖动 JTable
行以重新排序 table 的功能。我在网上看到的许多答案都建议您应该使用自定义 TransferHandler
实现来实现此行为。但是,我发现这会使事情过于复杂,需要有一种更简单的方法来做到这一点。谁能就如何更有效地拖放 table 行以重新排序 table 提出建议?
为了实现此行为,您可以使用 MouseListener
和 MouseMotionListener
的组合。 DefaultTableModel
提供的功能可以将事件的 Point
转换为事件发生的行。使用此功能,我们可以有效地拖动 table 行。下面的代码示例显示了实时拖动 table 行的基本实现。请注意 table
和 tableModel
属性是有意弱化的:我们不希望 MouseHandler
保持对 table
或 [=16= 的强引用].
public class MouseHandler implements MouseListener, MouseMotionListener {
private Integer row = null;
private final WeakReference<JTable> table;
private final WeakReference<DefaultTableModel> tableModel;
public MouseHandler(JTable table, DefaultTableModel model) {
this.table = new WeakReference<>(table);
this.tableModel = new WeakReference<>(model);
}
@Override
public void mouseClicked(MouseEvent event) {}
@Override
public void mousePressed(MouseEvent event) {
JTable table;
if((table = this.table.get()) == null) {
return;
}
int viewRowIndex = table.rowAtPoint(event.getPoint());
row = table.convertRowIndexToModel(viewRowIndex);
}
@Override
public void mouseReleased(MouseEvent event) {
row = null;
}
@Override
public void mouseEntered(MouseEvent event) {}
@Override
public void mouseExited(MouseEvent event) {}
@Override
public void mouseDragged(MouseEvent event) {
JTable table;
DefaultTableModel tableModel;
if((table = this.table.get()) == null || (tableModel = this.tableModel.get()) == null) {
return;
}
int viewRowIndex = table.rowAtPoint(event.getPoint());
int currentRow = table.convertRowIndexToModel(viewRowIndex);
if(row == null || currentRow == row) {
return;
}
tableModel.moveRow(row, row, currentRow);
row = currentRow;
table.setRowSelectionInterval(viewRowIndex, viewRowIndex);
}
@Override
public void mouseMoved(MouseEvent event) {}
}
在我看来,与大多数使用 TransferHandler
.
的建议相比,这是一种更简洁、更友好的实现方式
更新 2019 年 9 月 22 日 11.51 CEST
正如 @MadProgrammer 所建议的那样,原始示例在处理 filtered/sorted table 时存在问题。该示例现已更新为也支持这些。在排序 table 中移动行时,移动的行将在下一个可见行之后插入一个索引。这意味着在底层模型中,行可能一次移动多个索引。
重要提示:如果您想确保您的 table 在移动行后保持 filtered/sorted,请确保您在 table 的排序器上调用了 setSortsOnUpdates(true)
。
在 Java 中,JTable
默认不提供拖动 JTable
行以重新排序 table 的功能。我在网上看到的许多答案都建议您应该使用自定义 TransferHandler
实现来实现此行为。但是,我发现这会使事情过于复杂,需要有一种更简单的方法来做到这一点。谁能就如何更有效地拖放 table 行以重新排序 table 提出建议?
为了实现此行为,您可以使用 MouseListener
和 MouseMotionListener
的组合。 DefaultTableModel
提供的功能可以将事件的 Point
转换为事件发生的行。使用此功能,我们可以有效地拖动 table 行。下面的代码示例显示了实时拖动 table 行的基本实现。请注意 table
和 tableModel
属性是有意弱化的:我们不希望 MouseHandler
保持对 table
或 [=16= 的强引用].
public class MouseHandler implements MouseListener, MouseMotionListener {
private Integer row = null;
private final WeakReference<JTable> table;
private final WeakReference<DefaultTableModel> tableModel;
public MouseHandler(JTable table, DefaultTableModel model) {
this.table = new WeakReference<>(table);
this.tableModel = new WeakReference<>(model);
}
@Override
public void mouseClicked(MouseEvent event) {}
@Override
public void mousePressed(MouseEvent event) {
JTable table;
if((table = this.table.get()) == null) {
return;
}
int viewRowIndex = table.rowAtPoint(event.getPoint());
row = table.convertRowIndexToModel(viewRowIndex);
}
@Override
public void mouseReleased(MouseEvent event) {
row = null;
}
@Override
public void mouseEntered(MouseEvent event) {}
@Override
public void mouseExited(MouseEvent event) {}
@Override
public void mouseDragged(MouseEvent event) {
JTable table;
DefaultTableModel tableModel;
if((table = this.table.get()) == null || (tableModel = this.tableModel.get()) == null) {
return;
}
int viewRowIndex = table.rowAtPoint(event.getPoint());
int currentRow = table.convertRowIndexToModel(viewRowIndex);
if(row == null || currentRow == row) {
return;
}
tableModel.moveRow(row, row, currentRow);
row = currentRow;
table.setRowSelectionInterval(viewRowIndex, viewRowIndex);
}
@Override
public void mouseMoved(MouseEvent event) {}
}
在我看来,与大多数使用 TransferHandler
.
更新 2019 年 9 月 22 日 11.51 CEST 正如 @MadProgrammer 所建议的那样,原始示例在处理 filtered/sorted table 时存在问题。该示例现已更新为也支持这些。在排序 table 中移动行时,移动的行将在下一个可见行之后插入一个索引。这意味着在底层模型中,行可能一次移动多个索引。
重要提示:如果您想确保您的 table 在移动行后保持 filtered/sorted,请确保您在 table 的排序器上调用了 setSortsOnUpdates(true)
。