在 JTable 中选择第 0 行时,选择处理程序变得疯狂

When selecting row 0 in JTable the selection handler goes mad

我在使用 JTable 的 selection 处理程序时遇到问题。

table 从控件中的刷新线程每 15 秒更新一次。

我想 select 我的 JTable 中的一行并提取我将用于构建文件名的列内容。

只要第 0 行不在刷新期间 selected,一切正常。但是,如果在触发刷新时第 0 行被 selected,它看起来像是在 setAppserverData 过程和事件处理程序之间跳转,直到它超过 table 的 rowCount 并且我得到一个 IndexOutOfBoundsException。

我是 Java 新手,这在我的知识范围内,所以我很难找出代码有什么问题。我的直觉是,这与我试图实现MVC结构有关,而我未能以正确的方式分离代码。

//控制器

// Appserver worker
class AppserverWorker extends SwingWorker<Integer, Integer>{
    protected Integer doInBackground() throws Exception{

        // Check appservers
        while(true){
            theModel.readAppservData();

            // Update action buttons
            try {
                if(!theModel.isStatusOk()){
                    theGui.actionButtonPanel.setAppServBtnColor("RED");
                    //              theGui.actionButtonPanel.setButtonFlashOn();
                }else theGui.actionButtonPanel.setAppServBtnColor("GREEN");
            } catch (IllegalArgumentException e1) {
                sysLogger.logMsg("SEVERE",e1.getMessage());
                JOptionPane.showMessageDialog(null, e1.getMessage());
            }

            // Update GUI
            theGui.extAppServPanel.setAppserverData(theModel.getAppservData());

            // Sleep refresh time
            // TODO read refresh from init table
            try {
                Thread.sleep(appServRefreshTime);
            } catch (InterruptedException e) {}
        }
    }

// List selection handler
class SharedListSelectionHandler implements ListSelectionListener {
    public void valueChanged(ListSelectionEvent e) {
        if (!e.getValueIsAdjusting()){                  // To avoid double trigger of listener
            System.out.println(">>>>>>>>>  Selection list handler >>>>>>>>");
            String fileName = null;
            String fileExt  = ".txt";
            ListSelectionModel lsm = (ListSelectionModel)e.getSource();
            try {       
                int selectedRow = lsm.getAnchorSelectionIndex();                                // Get the row clicked
                if (selectedRow>=0){                                                            // If -1 no row selected
                    fileName = theGui.extAppServPanel.getAppservName(selectedRow)+fileExt;      // Get appserver name and build file name
                    theModel.readCollectFile(InitParameters.collectFilePath, fileName);         // Read collect file
                    theGui.extAppServPanel.setAppservInfo(theModel.getCollectFileContent());    // Fill the text panel with collect file
                }
            }
            catch(FileNotFoundException e1){
                sysLogger.logMsg("SEVERE",this.getClass().getSimpleName()+": Collect file "+fileName+" is missing");
                JOptionPane.showMessageDialog(null, "Collect file "+fileName+" is missing");
            }

            catch(IOException e1){
                sysLogger.logMsg("SEVERE",this.getClass().getSimpleName()+": System error: An I/O error occurred");
                System.err.println("System error: An I/O error occurred");
                System.exit(0);;
            }
        }
    }
}

// 视图

public class ExtAppServPanel extends JPanel {

private JTable table;
private DefaultTableModel model;

private JTextArea textAreaInfo;
private JButton btnSaveInfo;
private List colData;

public boolean ignoreTableChanges = false;

/**
 * Constructor
 */
public ExtAppServPanel() {
    System.out.println(this.getClass().getSimpleName()+": Constructor");

    setLayout(new MigLayout("", "[350:376.00,grow,leading]", "[100px:100,grow][][48.00,grow][]"));
    this.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Application servers info", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));

    // Set up table
    String[] columnNames = {"Server name", "Type","Status"};
    model = new DefaultTableModel(null,columnNames);
    table = new  JTable(model){ 

        // Color code rows
        @Override
        public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
            Component comp = super.prepareRenderer(renderer, row, col);

            if (!isRowSelected(row)){
                comp.setBackground(getBackground());
                int modelRow = convertRowIndexToModel(row);
                String type = (String)getModel().getValueAt(modelRow, 2);
                if ("FAIL".equals(type)) comp.setBackground(Color.RED);
                if ("WARNING".equals(type)) comp.setBackground(Color.YELLOW);
            }
            return comp;
        }
    };

    // Set grid
    table.setGridColor(Color.LIGHT_GRAY);

    // Set width and alignment
    table.getColumnModel().getColumn(1).setMinWidth(200);
    table.getColumnModel().getColumn(1).setMaxWidth(200);
    table.getColumnModel().getColumn(1).setPreferredWidth(200);

    table.getColumnModel().getColumn(2).setMinWidth(75);
    table.getColumnModel().getColumn(2).setMaxWidth(75);
    table.getColumnModel().getColumn(2).setPreferredWidth(75);


    // Add scrollpane
    JScrollPane scrollPane = new JScrollPane(table);
    scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
    table.setFillsViewportHeight(true);
    this.add(scrollPane, "cell 0 0,grow");

    // Set up text area
    JLabel lblDetails = new JLabel("Details");
    this.add(lblDetails, "cell 0 1,alignx leading");

    textAreaInfo = new JTextArea();
    textAreaInfo.setBackground(Color.BLACK);
    textAreaInfo.setForeground(Color.GREEN);
    textAreaInfo.setEditable(false);
    textAreaInfo.setFont(new Font("Lucida Console",Font.PLAIN,10));

    JScrollPane scrollPane_1 = new JScrollPane();
    scrollPane_1.setViewportView(textAreaInfo);
    this.add(scrollPane_1, "cell 0 2,grow");

    // Save button
    btnSaveInfo = new JButton("Save");
    add(btnSaveInfo, "cell 0 3,alignx right");
}

/*
 * Setters and getters
 */
public void setAppserverData(ArrayList<ArrayList<String>> dataRecord) {
    ArrayList<String> dataCol = new ArrayList<String>();

    // Init values array from result set
    try {
        model.setRowCount(0);   // <<<<<<<<<<  Problem
        String[] arrayRow;
        String status = "Ok";

        // Get first row from list
        dataCol = dataRecord.get(0);                    
        arrayRow = new String[dataCol.size()];  
        arrayRow[0] = dataCol.get(2);
        arrayRow[1] = dataCol.get(1);
        arrayRow[2] = dataCol.get(4);

        for (int i = 0; i < dataRecord.size(); i++){
            dataCol = dataRecord.get(i);

            // Check status
            if (dataCol.get(4).toUpperCase().equals("FAIL")){
                status = "FAIL";
            }else if (dataCol.get(4).toUpperCase().equals("WARNING")){
                status = "WARNING";
            }

            if (!dataCol.get(2).toUpperCase().equals(arrayRow[0].toUpperCase())){   
                arrayRow[2] = status;                           // Override status
                System.out.println(">>>>>>>>>  Updating table >>>>>>>>");
                model.addRow(arrayRow);                         // Add row to table
                arrayRow = new String[dataCol.size()];  
                arrayRow[0] = dataCol.get(2);
                arrayRow[1] = dataCol.get(1);
                arrayRow[2] = dataCol.get(4);
                status = "Ok";
            }
        }
        setAppservName();
    } catch (Exception e) {
        e.printStackTrace();
    }   
}

//  Get row count
public int getRowCount(){   
    return model.getRowCount();
}

//  Set table data
public void setAppservName(){   
    Vector data = model.getDataVector();
    Vector row = (Vector) data.elementAt(1);

    // Copy the first column
    int mColIndex = 0;-
    colData = new ArrayList(table.getRowCount()+1);

    for (int i = 0; i < table.getRowCount(); i++) {
      row = (Vector) data.elementAt(i);
      colData.add(row.get(mColIndex));  
    }
}

//  Get table data
public String getAppservName(int rowNum){   
    return (String) colData.get(rowNum);
}

// Set appserver info
public void setAppservInfo(String appservInfo){
    textAreaInfo.setText(appservInfo);
    textAreaInfo.setCaretPosition(0);       // Set cursor at top of text
}

// Get appserver info
public String getAppservInfo(){
    return textAreaInfo.getText();
}

/**
 * Action listeners
 */

public void addTableRowListener(ListSelectionListener listSelectionEvent) {
    table.getSelectionModel().addListSelectionListener(listSelectionEvent);
}

public void addButtonListener(ActionListener buttonEvent) {
    btnSaveInfo.addActionListener(buttonEvent);
    btnSaveInfo.setActionCommand("saveAppServInfo");
}

}

非零行被 selected 并且刷新线程是 运行

时的跟踪输出
** Appserver worker **
CmtModel: readAppservData
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>  Rowcount = 8

当零行被 selected 并且刷新线程是 运行

时跟踪输出
** Appserver worker **
CmtModel: readAppservData
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 0 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 1 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 2 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 3 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 4 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 5 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 6 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 7 <<
>>>>>>>>>  Updating the table >>>>>>>>
>>>>>>>>>  Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 8 <<
java.lang.IndexOutOfBoundsException: Index: 8, Size: 8
    at java.util.ArrayList.rangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
    at panels.ExtAppServPanel.getAppservName(ExtAppServPanel.java:219)
    at Control$SharedListSelectionHandler.valueChanged(Control.java:330)
    at javax.swing.DefaultListSelectionModel.fireValueChanged(Unknown Source)
    at javax.swing.DefaultListSelectionModel.fireValueChanged(Unknown Source)
    at javax.swing.DefaultListSelectionModel.fireValueChanged(Unknown Source)
    at javax.swing.DefaultListSelectionModel.insertIndexInterval(Unknown Source)
    at javax.swing.JTable.tableRowsInserted(Unknown Source)
    at javax.swing.JTable.tableChanged(Unknown Source)
    at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
    at javax.swing.table.AbstractTableModel.fireTableRowsInserted(Unknown Source)
    at javax.swing.table.DefaultTableModel.insertRow(Unknown Source)
    at javax.swing.table.DefaultTableModel.addRow(Unknown Source)
    at javax.swing.table.DefaultTableModel.addRow(Unknown Source)
    at panels.ExtAppServPanel.setAppserverData(ExtAppServPanel.java:172)
    at Control$AppserverWorker.doInBackground(Control.java:434)
    at Control$AppserverWorker.doInBackground(Control.java:1)
    at javax.swing.SwingWorker.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at javax.swing.SwingWorker.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
// Update GUI
theGui.extAppServPanel.setAppserverData(theModel.getAppservData());

不要在 doInBackground() 方法中更新模型或 GUI。

GUI 的所有更新都应在 EventDispatchThread (EDT) 上完成。

相反,当数据发生变化时,您需要 "publish" 结果,以便可以在 SwingWorkerprocess(...) 方法中执行代码,该方法确实在 EDT 上执行,因此GUI 将在美国东部时间更新。

阅读 Concurrency 上的 Swing 教程部分了解更多信息。 Tasks That Have Intermediate Results 部分有发布方法的示例。