java 如何使用 fireTableDataChanged() 正确更新 AbstractTableModel?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7904708/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 21:55:37  来源:igfitidea点击:

How to correctly update AbstractTableModel with fireTableDataChanged()?

javaswingjtablelistener

提问by s.d

I'm stillstruggling with a JTablethat should be automatically updated.

仍在为一个JTable应该自动更新的问题而苦苦挣扎。

The situation is as follows: I instantiate MyTable(extends JTable), and set it in my UI class (MyView). The MyTableclass takes the UI class and an instance of the class that contains logic as parameters):

情况如下:我实例化MyTable(extends JTable),并将其设置在我的UI类(MyView)中。的MyTable类需要的UI类和包含逻辑作为参数)的类的实例:

...
private JPanel createTablePanel() {
    tablePanel = new JPanel();
    myTable = new MyTable(this,mymeth);
    setMyTable(myTable);
    JScrollPane scrollPane = new JScrollPane(getMyTable());
    tablePanel.add(scrollPane);
    return tablePanel;
}

MyTableitself looks like below. An extension of AbstractTableModel(MyTableModel) is set to it. An extension of TableModelListeneris set to the model. And finally an extension of ListSelectionListeneris set to the model's SelectionModel.

MyTable本身看起来像下面。为其设置了扩展名AbstractTableModel( MyTableModel)。TableModelListener为模型设置了扩展名。最后将 的扩展名ListSelectionListener设置为模型的SelectionModel.

public class MyTable extends JTable implements TableModelListener
{

public MyTable(MyView myView, MyMethods mymeth)
{

    AbstractTableModel tableModel = new MyTableModel(mymeth);
    setModel(tableModel);
    getModel().addTableModelListener(new MyTableModelListener());
    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    setCellSelectionEnabled(true);
    getColumnModel().getSelectionModel().addListSelectionListener(new MyTableSelectionListener(this, mymeth, myView));
    setPreferredScrollableViewportSize(this.getPreferredSize());

}

}

Let's have a quick look at the model's constructor.

让我们快速浏览一下模型的构造函数。

public MyTableModel(MyMethods mymeth) {
    dataObject = new MyData(mymeth);
    myData = dataObject.getMyData();
    colTitles = dataObject.getColTitles();
}

MyDatacompiles the data for the table: A Vector<Vector<Object>>, that consists of three Vector<Object>s (the table data) and a String[](The column titles). The data itself comes from a graph via mymeth, the logic class' instance.

MyData编译表的数据: A Vector<Vector<Object>>,它由三个Vector<Object>s(表数据)和 a String[](列标题)组成。数据本身来自一个图形,通过mymeth逻辑类的实例。

Whenever a table columnis clicked, a popup (i.e. JOptionPane) object is instantiated, which presents a selection of values for the 3rd row in the selected column. The user chooses a value and the value is set to the data model. Notethe way the table is updated after that.

每当单击表列时JOptionPane都会实例化一个弹出(即)对象,该对象显示所选列中第三行的值选择。用户选择一个值并将该值设置为数据模型请注意此后表格的更新方式。

public MyOptionPane(int i, MyMethods mymeth, MyView myView) {
    this.view = myView;
    String sourceString = mymeth.getSourceString(i); // Gets a String from a
String[]. In this Array, the order is the same as in the table.
    String tag = null;
    tag = (String) JOptionPane.showInputDialog(this, "Choose a tag for \"" + sourceString + "\"", "String tagging" , JOptionPane.PLAIN_MESSAGE, null, myView.getTags().toArray(), myView.getTags().get(0));
    mymeth.setTag(i, tag);

    // This is where fireTableDataChanged() didn't work, but this did
            MyTableModel model = new MyTableModel(mymeth); // New model instance
    view.getMyTable().setModel(model); // reset new model to table
}

This works. However, from what I've read I shold be able to simply call fireTableDataChanged()on the model and the table should update itself. However, this doesn't work. User kleopatra has commentedto an answer in a previous post:

这有效。但是,从我读过的内容来看,我应该能够简单地调用fireTableDataChanged()模型,并且表格应该会自行更新。但是,这不起作用。用户 kleopatra在之前的帖子中评论了一个答案:

do not call any of the model's fireXX methods from any code external to the model. Instead implement the model to do so when anything changed

不要从模型外部的任何代码调用模型的任何 fireXX 方法。而是实现模型以在任何更改时执行此操作

So: Can I call fireTableDataChanged()within such a structure at all?And if so, where and how?

所以:我可以fireTableDataChanged()在这样的结构中调用吗?如果是这样,在哪里以及如何?

Thanks in advance for all pieces of enlightenment!

在此先感谢所有的启发!

回答by camickr

from what I've read I shold be able to simply call fireTableDataChanged() on the model and the table should update itself

从我读过的内容来看,我可以简单地在模型上调用 fireTableDataChanged() 并且表格应该自行更新

Your program doesn't invoke the fireXXX methods on the model. The TableModel itself is responsible for invoking these methods whenever any of the data in the model is changed. Look at the Creating a Table Modelexample from the Swing tutorial. The setValueAt(...) method shows how to invoke the appropriate fireXXX method.

您的程序不会调用模型上的 fireXXX 方法。TableModel 本身负责在模型中的任何数据发生更改时调用这些方法。查看Swing 教程中的创建表模型示例。setValueAt(...) 方法显示了如何调用适当的 fireXXX 方法。

If you create a completely new TableModel, then you need to use the setModel() method.

如果您创建一个全新的 TableModel,那么您需要使用 setModel() 方法。

Kleopatra's comment was that all fireXXX methods showed be invoked from within the TableModel class itself. If you want to understand how this is done, then take a look at the source code for the DefaultTableModel. If has example of when you would invoke the fireTableDataChanged() method along with the other fireXXX methods.

Kleopatra 的评论是所有 fireXXX 方法都显示是从 TableModel 类本身内部调用的。如果您想了解这是如何完成的,请查看 DefaultTableModel 的源代码。如果有关于何时调用 fireTableDataChanged() 方法和其他 fireXXX 方法的示例。

回答by trashgod

You shouldn't have to replacethe entire model to update a single row. Instead, update the affected cell(s) and let setValueAt()fire the required event, as shown here. Alternatively, this related exampleuses a DefaultTableModelthat handles the events for you, as suggested by @mKorbel & @camickr.

您不必替换整个模型来更新单行。相反,更新受影响的单元格,让setValueAt()火所需的事件,如图所示这里。或者,此相关示例使用DefaultTableModel为您处理事件,如@mKorbel 和@camickr 所建议的。

If you stay with AbstractTableModel, consider List<List<MyData>>, unless you need Vectorfor some other reason.

如果您留在AbstractTableModel,请考虑List<List<MyData>>,除非您Vector出于其他原因需要。

回答by mKorbel

not to answer to your question, suggestion about using DefaultTableModel

不回答你的问题,关于使用的建议 DefaultTableModel

if you don't need to something to restrict for JTable, really good reason, then you couldn't to use AbstractTableModel, best job is still if you'd implement DefaultTableModel, by using DefaultTableModelyou'll never to care about that, which of FireXxxXxxChanged()you have to use for each from setXxx()methods,

如果你不需要限制某些东西JTable,真的有充分的理由,那么你不能使用AbstractTableModel,最好的工作仍然是如果你实现DefaultTableModel,通过使用DefaultTableModel你永远不会关心那个,FireXxxXxxChanged()你们中的哪一个必须为每个 fromsetXxx()方法使用,

回答by Abdul-Razak Adam

Here is a simple program that has an ArrayListof Person, populates a table with data from the ArrayListand updates AbstractTableModelwith fireTableDataChanged(). Hope this helps

这里是具有一个简单的程序ArrayListPerson,填充表从数据ArrayList和更新AbstractTableModelfireTableDataChanged()。希望这可以帮助

import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;

/**
 *
 * @author razak
 */
public class Table extends javax.swing.JFrame {

    ArrayList<Person> records; //arrayList of persons
    AbstractTable tableModel;  //table model --inner class

    /**
     * Creates new form Table
     */
    public Table() {
        records = new ArrayList<>();
        tableModel = new AbstractTable(records);
        addData();
        initComponents();
        recordTable.setModel(tableModel);
    }

    /**
     * Adds test values to the table
     */
    private void addData() {
        records.add(new Person("Tester", 21));
        records.add(new Person("Kofi", 20));
        records.add(new Person("Razak", 251));
        records.add(new Person("Joseph", 21));
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        recordTable = new javax.swing.JTable();
        nameLabel = new javax.swing.JLabel();
        ageLabel = new javax.swing.JLabel();
        nameTextField = new javax.swing.JTextField();
        ageTextField = new javax.swing.JTextField();
        addButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        recordTable.setModel(tableModel);
        jScrollPane1.setViewportView(recordTable);

        nameLabel.setText("Name");

        ageLabel.setText("Age");

        addButton.setText("Add");
        addButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
            .addGroup(layout.createSequentialGroup()
                .addGap(52, 52, 52)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(nameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(ageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGap(40, 40, 40)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 151, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(ageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(66, 66, 66)
                        .addComponent(addButton, javax.swing.GroupLayout.PREFERRED_SIZE, 109, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(39, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addGap(16, 16, 16)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(nameLabel)
                    .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(18, 18, 18)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(ageLabel)
                            .addComponent(ageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 31, Short.MAX_VALUE)
                        .addComponent(addButton)
                        .addGap(18, 18, 18)))
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 173, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

    /**
     * When add button is clicked
     *
     * @param evt
     */
    private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {                                          
        // TODO add your handling code here:
        String name = nameTextField.getText();
        int age = Integer.parseInt(ageTextField.getText());
        records.add(new Person(name, age));
        tableModel.fireTableDataChanged();

    }                                         

    /**
     * Inner class
     */
    class AbstractTable extends AbstractTableModel {

        String col[]; //column names
        ArrayList<Person> data; //arrayList to populate table

        AbstractTable(ArrayList<Person> record) {
            this.col = new String[]{"Name", "Age"};
            data = record;
        }

        //get number of records
        @Override
        public int getRowCount() {
            return data.size();
        }

        //get number of columns
        @Override
        public int getColumnCount() {
            return col.length;
        }

        //get a value form the table
        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Person person = data.get(rowIndex);
            if (columnIndex == 0) {
                return person.getName();
            } else if (columnIndex == 1) {
                return person.getAge();
            }
            return null;
        }

        //set value at a particular cell 
        public void setValueAt(Person person, int row, int column) {
            data.add(row, person);
            fireTableCellUpdated(row, column);
        }

        //get column name
        public String getColumnName(int column) {
            return col[column];
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(Table.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(Table.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(Table.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(Table.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Table().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JButton addButton;
    private javax.swing.JLabel ageLabel;
    private javax.swing.JTextField ageTextField;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JLabel nameLabel;
    private javax.swing.JTextField nameTextField;
    private javax.swing.JTable recordTable;
    // End of variables declaration                   
}


/**
 * Person class
 * @author razak
 */
public class Person {

    private final String name; //name
    private final int age; //age

    //constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //return name
    public String getName() {
        return name;
    }

    //returns age
    public int getAge() {
        return age;
    }
}