我有一个JTable和一个旁边的按钮,调用deleteSelectedRows()
,它听起来完全像:
public void deleteSelectedRows() {
int[] selected = jTable.getSelectedRows();
for(int i = selected.length - 1; i >= 0; i--) {
model.removeRow(selected[i]);
}
if(model.getRowCount() < 1) {
addEmptyRow();
}
}
但如果某个单元格被删除时(和/或其上方的单元格)被删除,则编辑的单元格会在剩下的时间内停留,如下所示:
然后尝试退出编辑时抛出ArrayIndexOutOfBoundsException
,因为第5行正在尝试访问,并且表中只剩下一行。
然后我用jTable.getEditingRow()
尝试了各种有趣的游戏。首先,在删除之前添加if(selected[i] != editing)
似乎有效,但是删除已编辑单元格上方的行会导致问题。
然后我尝试了这个:
public void deleteSelectedRows() {
int[] selected = jTable.getSelectedRows();
int editing = jTable.getEditingRow();
for(int s : selected) { //PS: Is there a better way of doing a linear search?
if(s == editing) {
return;
}
}
for(int i = selected.length - 1; i >= 0; i--) {
model.removeRow(selected[i]);
}
if(model.getRowCount() < 1) {
addEmptyRow();
}
}
但这并没有删除任何东西。从我散布的println
来看,最后一个要突出显示的单元格(spam
上有特殊边框)被认为是编辑行的一部分,从而触发我的早期{{1} }。
所以我真的不在乎解决方案是否涉及解决原始问题 - 当正在编辑的单元格被删除时的古怪结果 - 或者这个新问题 - return
表现不正常我预料到,只是我需要至少其中一个发生。也就是说,我有兴趣听到这两种解决方案只是出于学术上的好奇心。提前谢谢。
答案 0 :(得分:6)
在从模型中删除任何行之前,请尝试包含以下行:
if (table.isEditing()) {
table.getCellEditor().stopCellEditing();
}
答案 1 :(得分:1)
Table Stop Editing有一个通用的解决方案,当你有任意数量的按钮需要支持时,它会很方便。
答案 2 :(得分:0)
正如Howard所说,在修改模型之前必须停止单元格编辑。但是还必须检查单元是否实际被修改以避免空指针异常。 这是因为如果目前没有编辑表, getCellEditor()方法将返回 null :
if (myTable.isEditing()) // Only if it's is being edited
myTable.getCellEditor().stopCellEditing();
...
有些情况下,单元格编辑器可能会拒绝停止编辑, 这可能会发生,即如果您正在使用某个复杂的编辑器等待对话框上的用户输入。在这种情况下,您应该添加额外的支票:
if (myTable.isEditing())
if (!myTable.getCellEditor().stopCellEditing()) {
// If your update is user-generated:
JOptionPane.showMessageDialog(null, "Please complete cell edition first.");
// Either way return without doing the update.
return;
}
在您的代码中,您尝试仅删除未编辑的行,但在单元格编辑器停止编辑时也会抛出ArrayOutOfBounds异常。最好是在刷新之前停止它。
最后,您可以在表格中设置一个属性:
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
解释here。
答案 3 :(得分:0)
在应用任何更改之前停止编辑任何和所有单元格是有效的,这有点像使用大锤敲碎坚果。例如,如果正在编辑的单元格不是被删除的单元格,会发生什么情况?这是您将遇到的下一个问题。出于这个原因和其他原因,有更好的方法。
首先,使用框架为您完成繁重的工作。将 TableModelListener
附加到您的表模型 table.getModel().addTableModelListener()...
然后在您的侦听器实现中捕获删除事件并进行如下处理:
/**
* Implements {@link TableModelListener}. This fine grain notification tells listeners
* the exact range of cells, rows, or columns that changed.
*
* @param e the event, containing the location of the changed model.
*/
@Override
public void tableChanged(TableModelEvent e) {
if (TableModelEvent.DELETE == e.getType()) {
// If the cell or cells beng edited are within the range of the cells that have
// been been changed, as declared in the table event, then editing must either
// be cancelled or stopped.
if (table.isEditing()) {
TableCellEditor editor = table.getDefaultEditor(ViewHolder.class);
if (editor != null) {
// the coordinate of the cell being edited.
int editingColumn = table.getEditingColumn();
int editingRow = table.getEditingRow();
// the inclusive coordinates of the cells that have changed.
int changedColumn = e.getColumn();
int firstRowChanged = e.getFirstRow();
int lastRowChanged = e.getLastRow();
// true, if the cell being edited is in the range of cells changed
boolean editingCellInRangeOfChangedCells =
(TableModelEvent.ALL_COLUMNS == changedColumn ||
changedColumn == editingColumn) &&
editingRow >= firstRowChanged &&
editingRow <= lastRowChanged;
if (editingCellInRangeOfChangedCells) {
editor.cancelCellEditing();
}
}
}
}
}
在上面的示例中,我已将自己的编辑器指定为表 table.setDefaultRenderer(ViewHolder.class, new Renderer()); table.setDefaultEditor(ViewHolder.class, new Editor());
的默认编辑器。
此外,我没有使用特定视图,而是使用 ViewHolder
。这样做的原因是使表格在其显示的视图方面具有通用性。这是通用的 ViewHolder.class
:
/**
* Holds the view in a table cell. It is used by both the {@link Renderer}
* and {@link Editor} as a generic wrapper for the view.
*/
public static abstract class ViewHolder {
private static final String TAG = "ViewHolder" + ": ";
// the position (index) of the model data in the model list
protected final int position;
// the model
protected Object model;
// the view to be rendered
protected final Component view;
// the views controller
protected final Object controller;
/**
* @param view the view to be rendered
* @param position the position (index) of the data
*/
public ViewHolder(int position,
Object model,
Component view,
Object controller) {
this.position = position;
if (view == null) {
throw new IllegalArgumentException("item view may not be null");
}
if (model == null) {
throw new IllegalArgumentException("model may not be null");
}
this.controller = controller;
this.model = model;
this.view = view;
}
现在,每次调用渲染器或编辑器时,构建一个 ViewHolder
类并传入视图/控制器/位置等,就大功告成了。
这里要注意的重要一点是,您不必在删除或更改事件发生之前捕获它。实际上,您应该在模型更改后捕获它。为什么?更改之后您就知道发生了什么变化,因为 TableModelListener
会告诉您,帮助您确定下一步要做什么。