从JTable中删除正在编辑的行

时间:2011-06-14 20:03:27

标签: java swing jtable

我有一个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表现不正常我预料到,只是我需要至少其中一个发生。也就是说,我有兴趣听到这两种解决方案只是出于学术上的好奇心。提前谢谢。

4 个答案:

答案 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 会告诉您,帮助您确定下一步要做什么。