簡體   English   中英

從 JTable 中刪除正在編輯的行

[英]Remove row being edited from 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();
    }
}

但是如果一個單元格在它(和/或它上面的單元格)被刪除時處於被編輯的狀態,被編輯的單元格將保留而 rest 離開,如下所示:

然后試圖退出編輯拋出了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上有特殊邊框)被認為是編輯行的一部分,因此觸發了我的提前return

所以我真的不在乎解決方案是否涉及修復原始問題——當正在編輯的單元格被刪除時古怪的結果——或者這個新問題getEditingRow()的行為不像我預期的那樣,它只是我至少需要其中之一發生。 也就是說,出於學術好奇心,我很想聽聽這兩種解決方案。 提前致謝。

在從 model 中刪除任何行之前,嘗試包含以下行:

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

表格停止編輯有一個通用的解決方案,當您需要支持任意數量的按鈕時,它會很方便。

正如霍華德所說,在修改model之前,有必要停止單元格編輯。但也有必要檢查單元格是否真正被修改,以避免null指針異常。 這是因為如果此時未編輯表, 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);

如此所述。

雖然在應用任何更改之前停止任何和所有單元格的編輯是有效的,但這有點像使用大錘敲螺母。 例如,如果正在編輯的單元格不是被刪除的單元格,會發生什么情況? 這是您將遇到的下一個問題。 出於這個原因和其他原因,有更好的方法。 首先,使用框架為您完成繁重的工作。 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();
                }
            }
        }
    }
}

在上面的示例中,我將我自己的編輯器指定為表的默認編輯器table.setDefaultRenderer(ViewHolder.class, new Renderer()); table.setDefaultEditor(ViewHolder.class, new Editor()); 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 class 並傳入您的視圖 / controller / position 等,您就完成了。

這里需要注意的重要一點是,您不必在刪除或更改事件發生之前捕獲它。 實際上,您應該在 model 更改之后捕獲它。 為什么? 好吧,在更改之后您知道發生了什么更改,因為TableModelListener會告訴您,幫助您確定下一步要做什么。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM