【问题标题】:IndexOutOfBoundsException after row inserted, table sorted, then row deletedIndexOutOfBoundsException 插入行后,表排序,然后行删除
【发布时间】:2018-05-08 20:44:00
【问题描述】:

在上一个问题 (Convert modelRowIndex to viewRowIndex for sorted JTable) 中,我指出我正在尝试创建一个“简单”JTable,它使用 TableModel 将 ArrayList 与使用 TableModel 的 JTable 联系起来。我的目标是——现在仍然是——保留所有 java 的内置 JTable 功能,这些功能允许单元格编辑、行排序和列重新排列。感谢您的帮助,该功能现在可以使用。

我现在正在尝试添加插入和删除表格行的功能。我在这里提供的(更新的)示例有效......除了......在特定的操作序列下,抛出“IndexOutOfBoundsException”。这是我的代码:

package tableexample;

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

public final class TableExample extends JFrame {

        List<REItem> REList;
        JTable tblREList;
        JButton btnAddInsertRE, btnDeleteRE;
        JScrollPane spMain;
        JFrame frame;
        Container pane;

    public TableExample() {
        // create and populate the ArrayList
        REList = new ArrayList<>();
        REList.add(new REItem("Template1", "Comment1"));
        REList.add(new REItem("Template2", "Comment2"));
        RETableModel retm = new RETableModel(REList);  // Connect the List to the TableModel
        // create GUI components
        frame = new JFrame ("Table Example");
        btnAddInsertRE = new JButton("Add/Insert");
        btnDeleteRE = new JButton("Delete");
        tblREList = new JTable(retm); 
        tblREList.setAutoCreateRowSorter(true);
        spMain = new JScrollPane(tblREList);
        // add button ActionListeners
        btnAddInsertRE.addActionListener((ActionEvent evt) -> { btnAddInsertREActionPerformed(evt); });
        btnDeleteRE.addActionListener((ActionEvent evt) -> { btnDeleteREActionPerformed(evt); });
        // place GUI components and make the GUI visible
        pane = frame.getContentPane();
        pane.setLayout (null);        
        pane.add(btnAddInsertRE);
        pane.add(btnDeleteRE);
        pane.add(spMain);
        btnAddInsertRE.setBounds (10, 10, 100, 25);
        btnDeleteRE.setBounds (120, 10, 100, 25);
        spMain.setBounds (10, 45, spMain.getPreferredSize().width, spMain.getPreferredSize().height);
        frame.setSize(spMain.getWidth() + 40, spMain.getHeight() + 95);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
        frame.setVisible(true);
    } // end TableExample constructor

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            TableExample notUsed = new TableExample();
        });
    } //end main

    private void btnAddInsertREActionPerformed(ActionEvent evt) {                                             
        // Add a FileSelection object to the ArrayList
        int r = tblREList.getSelectedRow();                         // get row selection, if any
        if (r < 0) {                                                // no row selected
            REList.add(new REItem("NewTemplate", "NewComment"));    //   append new item to end
            r = REList.size()-1;                                    //   get index to new item
        } else {                                                    // else no row selected
            REList.add(r, new REItem("NewTemplate", "NewComment")); //   insert above selected row
        }                                                           // row selected or not
        spMain.setViewportView(tblREList);                          // repaint the updated table
        tblREList.getSelectionModel().setSelectionInterval(r, r);   // select the new row
    }                                            

    private void btnDeleteREActionPerformed(ActionEvent evt) {                                          
        int[] selRows = tblREList.getSelectedRows();                // see if any rows are selected
        if (selRows.length>0) {                                     // at least one row is selected
            for (int r=selRows.length-1; r>=0; r--) {               //   delete each row, from the bottom up,
                REList.remove(r);                                   //     so that indexes are correct and
            }                                                       //       don't change with each delete
            tblREList.clearSelection();                             // clear the row selection data
            spMain.setViewportView(tblREList);                      // repaint the updated table
        } else {                                                    // else no row(s) selected
            JOptionPane.showMessageDialog(null, "Must select at least one item to delete");
        }                                                           // no row selected
    } // end btnDeleteREActionPerformed

    public final class REItem {
        String template;
        String comment;

        public REItem(String tmp, String cmt) {
            this.template = tmp;
            this.comment = cmt;
        }
    } // end class REItem

    public class RETableModel extends AbstractTableModel {

        private List<REItem> reList = new ArrayList();
        private final String[] columnNames = { "Template", "Comment" };

        public RETableModel(List<REItem> list){
             this.reList = list;
        }

        @Override
        public String getColumnName(int column){
             return columnNames[column];
        }

        @Override     
        public int getRowCount() {
            return reList.size();
        }

        @Override        
        public int getColumnCount() {
            return columnNames.length; 
        }

        @Override
        public Object getValueAt(int row, int column) {
            switch (column) {
                case 0: return reList.get(row).template;
                case 1: return reList.get(row).comment;
               }
               return null; // default case
       }

        @Override
        public boolean isCellEditable(int row, int column) {
            return true;
        }

        @Override
        public Class<?> getColumnClass(int column){
            switch (column){
                case 0: return String.class;
                case 1: return String.class;
            }
            return null; // default case
        }

        @Override
        public void setValueAt(Object value, int row, int column) {
            switch (column) {
                case 0: reList.get(row).template = value.toString(); break;
                case 1: reList.get(row).comment = value.toString(); break;
               }
            // uncommenting the below often causes IndexOutOfBoundsException: Invalid range exception
            fireTableCellUpdated(row, column); 
        } // end setValueAt

    } // end RETableModel

} // end class TableExample

问题可以重现如下:运行上面的例子,点击“Add/Insert”按钮将新行追加到表格中,点击任一列标题重新排序表格,然后点击“Delete”按钮:抛出“IndexOutOfBoundsException”,表示TableModel的getValueAt方法指定的“行”索引有​​缺陷。

我认为这个问题与我的 TableModel.getValueAt(可能还有 .setValueAt ???)方法在 TableModel 列索引和 View 列索引之间转换的需要有关,但是,对于我的一生,我可以'不知道如何或在哪里进行转换。此外,这个问题 (Convert modelRowIndex to viewRowIndex for sorted JTable) 表明需要在 TableModel 和 View 行索引之间进行转换,并且表重新排序必须在索引转换完成之前发生。

尽我所能,我无法弄清楚如何进行转换和/或如何确保在表格更新和重新排序后发生转换。我需要听众吗?如果是这样,它应该是什么样子?

您能否提供一些澄清和帮助?

【问题讨论】:

    标签: java swing


    【解决方案1】:

    首先,变量名不应以大写字符开头。这是一个 Java 约定,它与您发布的代码格式混淆,使您的代码难以阅读。修复变量并遵循 Java 约定。

    ArrayList 应该只用于最初将数据添加到模型中。

    之后应该对 TableModel 进行更新,而不是 ArrayList。因此,您需要向 TableModel 添加方法,例如 addREItem(...)removeREItem(...)

    有关如何为给定对象构建自定义 TableModel 的分步示例,请参阅 Row Table Model,包括代码如何添加???(...)和删除???(... ) 方法。

    如果您想从表中删除选定的行,请查看:How to delete multiple rows from JTable , database at a time 的工作示例,展示如何使用 remove???(...) 方法完成此操作。

    【讨论】:

    • 按照你的建议做了。现在都在工作。 :-) 感谢有关 RowTableModel 和删除多个项目的优秀链接。但是 RowTableModel 提示表明“不是每次都从头开始手动创建 TableModel ...您可以使用 RowTableModel ...来实现所有功能(get/setValueAt 除外)。这是否表明它对我来说会更简单扩展 RowTableModel 而不是 AbstractTableModel?那么我只需要提供我的列表对象独有的 get/setValueAt 方法吗?
    • Is that suggesting that it would be simpler for me to extend RowTableModel rather than AbstractTableModel? - 您应该始终首先了解创建自己的 TableModel 的基础知识。您所经历的过程对此有所帮助。但现在在未来,您可能会发现每次需要新的自定义 TableModel 时都会重复相同的步骤。 RowTableModel 是我未来简化流程的方式。那就是 90% 的代码是通用的,只需要定制几个方法。因此出错的机会更少,也节省了时间。
    • 所以下次您需要自定义模型时,请尝试从 RowTableModel 扩展,然后决定您最喜欢哪种方法。链接的想法是给你一个选择。 RowTableModel 具有许多您可能不需要的额外功能。由于使用了泛型,RowTableModelL 更难理解。为了更好地理解泛型,我编写了代码作为学习工具。
    • 非常感谢您的帮助。最受赞赏的是您最初的声明,即“ArrayList 应该只用于最初向模型添加数据。之后应该对 TableModel 进行更新,而不是 ArrayList。”虽然事后看来这似乎很明显,但在我之前的所有研究中,我都没有看到它是这样清楚地表达出来的。您对 TableModel 的建议 - 先学习基础知识,然后扩展到高级技术 - 也很受欢迎,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2018-11-18
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 2023-04-03
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多