简体   繁体   English

从 JTable 中删除正在编辑的行

[英]Remove row being edited from JTable

I have a JTable and a button next to it that calls deleteSelectedRows() , which does exactly what it sounds like:我有一个 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();
    }
}

But if a cell was in the act of being edited when it (and/or cells above it) were deleted, the edited cell stayed while the rest left, like this:但是如果一个单元格在它(和/或它上面的单元格)被删除时处于被编辑的状态,被编辑的单元格将保留而 rest 离开,如下所示:

And then trying to exit out of the editing threw an ArrayIndexOutOfBoundsException since row 5 was trying to be accessed and there was only one row left in the table.然后试图退出编辑抛出了ArrayIndexOutOfBoundsException ,因为第 5 行试图被访问并且表中只剩下一行。

I then tried all sorts of fun and games with jTable.getEditingRow() .然后我尝试了各种有趣的游戏jTable.getEditingRow() At first, adding an if(selected[i] != editing) before the removal seemed to work, but then removing rows above the edited cell caused problems.起初,在删除之前添加一个if(selected[i] != editing)似乎可行,但随后删除编辑单元格上方的行导致了问题。

Then I tried this:然后我尝试了这个:

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();
    }
}

But that doesn't delete anything, ever.但这不会删除任何东西。 Judging from println s I sprinkled around, the last cell to be highlighted (that has the special border seen here on spam ) is considered part of the editing row, and thus triggers my early return .从我散布的println判断,最后一个要突出显示的单元格(在spam上有特殊边框)被认为是编辑行的一部分,因此触发了我的提前return

So I don't really care whether the solution involves fixing the original problem--that of the wacky results when a cell being edited is deleted--or this new problem--that of getEditingRow() not behaving as I expected, it's just that I need at least one of those to happen.所以我真的不在乎解决方案是否涉及修复原始问题——当正在编辑的单元格被删除时古怪的结果——或者这个新问题getEditingRow()的行为不像我预期的那样,它只是我至少需要其中之一发生。 That said, I would be interested to hear both solutions just out of academic curiosity.也就是说,出于学术好奇心,我很想听听这两种解决方案。 Thanks in advance.提前致谢。

Try to include the following lines before removing any rows from your model:在从 model 中删除任何行之前,尝试包含以下行:

if (table.isEditing()) {
    table.getCellEditor().stopCellEditing();
}

Table Stop Editing has a general solution which is handy when you have any number of buttons to support.表格停止编辑有一个通用的解决方案,当您需要支持任意数量的按钮时,它会很方便。

As Howard stated, it is necessary to stop the cell editing before modifying the model. But it is also necessary to check if the cell is actually being modified to avoid null pointer exceptions.正如霍华德所说,在修改model之前,有必要停止单元格编辑。但也有必要检查单元格是否真正被修改,以避免null指针异常。 This is because the getCellEditor() method will return null if the table isn't being edited at the moment:这是因为如果此时未编辑表, getCellEditor()方法将返回null

if (myTable.isEditing()) // Only if it's is being edited
         myTable.getCellEditor().stopCellEditing();
    ...

there are cases where the cell editor may refuse to stop editing, that can happen ie if you are using some complex editor that is waiting for user input on a dialog.在某些情况下,单元格编辑器可能会拒绝停止编辑,这种情况可能会发生,即如果您正在使用一些正在等待用户在对话框中输入的复杂编辑器。 In that case you should add an extra check:在这种情况下,您应该添加一个额外的检查:

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;
   }

In your code, you are trying to delete only the rows that are not being edited, but that would also throw an ArrayOutOfBounds Exception when the cell editor stops editing.在您的代码中,您试图仅删除未被编辑的行,但是当单元格编辑器停止编辑时,这也会引发 ArrayOutOfBounds 异常。 The best is to stop it before the refresh.最好是在刷新之前停止它。

Finally, there seems to be also a property you can set in your table:最后,您似乎还可以在表中设置一个属性:

 table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

as explained here .如此所述。

Whilst stopping any and all cells from editing before applying any changes works, it's a bit like using a sledge hammer to crack a nut.虽然在应用任何更改之前停止任何和所有单元格的编辑是有效的,但这有点像使用大锤敲螺母。 What happens, for example, if the cell that is editing is not the one being deleted?例如,如果正在编辑的单元格不是被删除的单元格,会发生什么情况? This is the next problem you'll encounter.这是您将遇到的下一个问题。 For that reason and others there is a better way.出于这个原因和其他原因,有更好的方法。 Firstly, use the framework to do the heavy lifting for you.首先,使用框架为您完成繁重的工作。 Attach a TableModelListener to your table model table.getModel().addTableModelListener()... then in your listeners implementation catch the delete event and process as follows:TableModelListener附加到您的表 model 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();
                }
            }
        }
    }
}

In the example above I've assigned my own editor as the default editor for the table table.setDefaultRenderer(ViewHolder.class, new Renderer()); table.setDefaultEditor(ViewHolder.class, new Editor());在上面的示例中,我将我自己的编辑器指定为表的默认编辑器table.setDefaultRenderer(ViewHolder.class, new Renderer()); table.setDefaultEditor(ViewHolder.class, new Editor()); table.setDefaultRenderer(ViewHolder.class, new Renderer()); table.setDefaultEditor(ViewHolder.class, new Editor()); . .

Additionally instead of using a specific view I use a ViewHolder .此外,我没有使用特定的视图,而是使用了ViewHolder The reason for this is to make the table generic in terms of the views it displays.这样做的原因是为了使表格在其显示的视图方面具有通用性。 Here is the generic ViewHolder.class :这是通用的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;
    }

Now, each time your renderer or editor is called, construct a ViewHolder class and pass in your view / controller / position etc, and you're done.现在,每次调用您的渲染器或编辑器时,构造一个ViewHolder class 并传入您的视图 / controller / position 等,您就完成了。

The important thing to note here is that you do not have to catch the delete or change event before it happens.这里需要注意的重要一点是,您不必在删除或更改事件发生之前捕获它。 You should, in fact, catch it after the model changes.实际上,您应该在 model 更改之后捕获它。 Why?为什么? Well after a change you know what has changed, because the TableModelListener tells you, helping you determine as to what to do next.好吧,在更改之后您知道发生了什么更改,因为TableModelListener会告诉您,帮助您确定下一步要做什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM