【问题标题】:Remove row being edited from JTable从 JTable 中删除正在编辑的行
【发布时间】:2021-03-13 21:46:52
【问题描述】:

我有一个 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();
    }
}

但这永远不会删除任何东西。从printlns 来看,最后一个要突出显示的单元格(在spam 上看到的特殊边框)被认为是编辑行的一部分,因此触发了我早期的return

所以我真的不在乎解决方案是否涉及修复原始问题 - 删除正在编辑的单元格时的古怪结果 - 或这个新问题 - getEditingRow() 的行为不符合我的预期,只是我需要至少其中一个发生。也就是说,出于学术好奇心,我很想听听这两种解决方案。提前致谢。

【问题讨论】:

    标签: java swing jtable


    【解决方案1】:

    在从模型中删除任何行之前尝试包含以下行:

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

    【讨论】:

    • 正确但不完整:不涵盖编辑器拒绝停止的情况,需要像 f.i. 这样的后备。在这种情况下取​​消
    【解决方案2】:

    正如霍华德所说,在修改模型之前必须停止单元格编辑。但也有必要检查单元格是否真的被修改以避免空指针异常。 这是因为如果当前没有编辑表格,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】:

      虽然在应用任何更改之前停止编辑任何和所有单元格是可行的,但这有点像使用大锤敲击坚果。例如,如果正在编辑的单元格不是被删除的单元格,会发生什么?这是您将遇到的下一个问题。出于这个原因和其他原因,有更好的方法。 首先,使用框架为您完成繁重的工作。将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 会告诉您,帮助您确定下一步该做什么。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-09
        • 2016-11-28
        • 1970-01-01
        • 1970-01-01
        • 2014-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多