【问题标题】:How to refresh data in JTable I am using TableModel如何刷新 JTable 中的数据我正在使用 TableModel
【发布时间】:2013-05-23 01:35:16
【问题描述】:

你好,

我已经创建了我的 TableModel,并希望在添加新行后刷新 JTable。应该在监听器中添加什么来“刷新”JTable?

public class MyTableModel implements TableModel  {
    private Set<TableModelListener> listeners = new HashSet<TableModelListener>();

    //List<Staff> staffs = Factory.getInstance().getStaffDAO().getAllStaff();
    private List<Staff> staffs;

    public MyTableModel(List<Staff> staffs){
        this.staffs = staffs;
    }

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

    @Override
    public int getColumnCount() {
        return 5;  
    }

    @Override
    public String getColumnName(int columnIndex) {
        switch (columnIndex){
            case 0:
                return "First Name";
            case 1:
                return "Second Name";
            case 2:
                return "Date";
            case 3:
                return "Position";
            case 4:
                return "Salary";
        }
        return "";  
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return Object.class;  
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true;  
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Staff staff = staffs.get(rowIndex);
        switch (columnIndex){
            case 0:
                return staff.getName();
            case 1:
                return staff.getSurname();
            case 2:
                return staff.getDate();
            case 3:
                return staff.getPosition();
            case 4:
                return staff.getSalary();
        }
        return "";  
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    }

    @Override
    public void addTableModelListener(TableModelListener l) {
    }

    @Override
    public void removeTableModelListener(TableModelListener l) {
    }
}

这是我添加行按钮的监听器:

 @Override
    public void actionPerformed(ActionEvent e) {
        Staff staff = new Staff();
        staff.setName(JOptionPane.showInputDialog("Enter First Name"));
        staff.setSurname(JOptionPane.showInputDialog("Enter Second Name"));
        staff.setDate(JOptionPane.showInputDialog("Enter Date"));
        staff.setPosition(JOptionPane.showInputDialog("Enter Position"));
        staff.setSalary(JOptionPane.showInputDialog("Enter Salary"));
        try {
            Factory.getInstance().getStaffDAO().addStaff(staff);
        } catch (SQLException e1) {
            e1.printStackTrace();  
        }
!!!Here should be some code that will be firing my table after adding new row!!!
}

我尝试在我的 actionPerformed() 中使用 AbstractTableModel 的 firetabledatachanged() 方法,但不幸的是,它出现了 ClassCastException。

更新 1

WorkPlaceGui.java

public class WorkPlaceGui extends JFrame implements ActionListener {

    AbstractTableModel model;
    JTable jTable;
    JScrollPane jScrollPane;

    public WorkPlaceGui()throws SQLException{


        List<Staff> staffs = Factory.getInstance().getStaffDAO().getAllStaff();
        for(int i = 0; i < 0; i++) {
                staffs.add(new Staff("First Name " + staffs.get(i).getName(), "Second Name " + staffs.get(i).getSurname(), "Date " + staffs.get(i).getDate(), "Position " + staffs.get(i).getPosition(), "Salary " + staffs.get(i).getSalary()));
        }

        model = new MyTableModel(staffs);
        jTable = new JTable(model);
        JButton jBtnAdd = new JButton("Добавить");
        JButton jBtnDel = new JButton("Удалить");
        JButton jBtnUpd = new JButton("Обновить");
        JButton jBtnAdmin = new JButton("Админка");
        JPanel panelNorth = new JPanel();
        JPanel panelCenter = new JPanel();
        JPanel panelSouth = new JPanel();
        jTable.setPreferredScrollableViewportSize(new Dimension(350, 150));
        jScrollPane = new JScrollPane(jTable);


        panelNorth.setLayout(new FlowLayout());
        panelNorth.add(jBtnAdd);
        panelNorth.add(jBtnDel);
        panelNorth.add(jBtnUpd);
        panelNorth.add(jBtnAdmin);
        panelCenter.add(jScrollPane);

        setLayout(new BorderLayout());
        add(panelNorth, BorderLayout.NORTH);
        add(panelCenter, BorderLayout.CENTER);

        jBtnAdd.addActionListener(this);

        setPreferredSize(new Dimension(550, 300));
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setTitle("Staff data base");
        pack();
        setVisible(true);
        setLocationRelativeTo(null);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        Staff staff = new Staff();
        staff.setName(JOptionPane.showInputDialog("Enter First Name"));
        staff.setSurname(JOptionPane.showInputDialog("Enter Second Name"));
        staff.setDate(JOptionPane.showInputDialog("Enter Date"));
        staff.setPosition(JOptionPane.showInputDialog("Enter Position"));
        staff.setSalary(JOptionPane.showInputDialog("Enter Salary"));
        try {
            Factory.getInstance().getStaffDAO().addStaff(staff);
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        model.fireTableDataChanged();
    }
}

MyTableModel.java

public class MyTableModel extends AbstractTableModel {

    private List<Staff> staffs;

    public MyTableModel(List<Staff> staffs){
        this.staffs = staffs;
    }

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

    @Override
    public int getColumnCount() {
        return 5;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Staff staff = staffs.get(rowIndex);
        switch (columnIndex){
            case 0:
                return staff.getName();
            case 1:
                return staff.getSurname();
            case 2:
                return staff.getDate();
            case 3:
                return staff.getPosition();
            case 4:
                return staff.getSalary();
        }
        return "";
    }
}

【问题讨论】:

  • 为了更好的帮助,请尽快发布SSCCE,简短,可运行,可编译,JFrame 与 JTable 包含 TableModel 的硬编码值,问题可能在任何地方

标签: java swing jtable tablemodel


【解决方案1】:

你辛苦了。

首先,您直接从TableModel 实现,其次您未能实现侦听器要求...

改为尝试从AbstractTableModel 扩展,它已经包含监听器注册和通知的实现。

您需要提供一种方法,允许您向表模型中添加一行。在此方法中,您需要使用fireTableRowsInserted 方法,该方法将通知使用该模型的任何表,已添加新行...

更新示例

这是一个非常非常基本的例子。它的唯一目的是演示fireTableRowsInserted 的使用。它使用 Swing Timer 每 125 毫秒添加一个新行,直到你杀死它;)

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;

public class DynamicTable {

    public static void main(String[] args) {
        new DynamicTable();
    }

    public DynamicTable() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }
                
                final MyTableModel model = new MyTableModel();
                JTable table = new JTable(model);
                
                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(table));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                
                Timer timer = new Timer(125, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        model.addRow();
                    }
                });
                timer.start();
            }
        });
    }
    
    public class MyTableModel extends AbstractTableModel {

        private List<String[]> rows;

        public MyTableModel() {
            rows = new ArrayList<>(25);
        }
        
        @Override
        public int getRowCount() {
            return rows.size();
        }

        @Override
        public int getColumnCount() {
            return 4;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return String.class;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            String[] row = rows.get(rowIndex);
            return row[columnIndex];
        }
        
        public void addRow() {
            int rowCount = getRowCount();
            String[] row = new String[getColumnCount()];
            for (int index = 0; index < getColumnCount(); index++) {
                row[index] = rowCount + "x" + index;
            }
            rows.add(row);
            fireTableRowsInserted(rowCount, rowCount);
        }            
    }                    
}

用另一个例子更新

因为您的表模型由其自己的List 提供支持,所以它与您的工厂无关。它不知道您何时向其中添加或删除对象。这意味着您将负责更新模型:

public class MyTableModel extends AbstractTableModel {

    private List<Staff> staffs;

    public MyTableModel(List<Staff> staffs){
        this.staffs = staffs;
    }

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

    @Override
    public int getColumnCount() {
        return 5;
    }
    
    public void add(Staff staff) {
        int size = getSize();
        staffs.add(staff);
        fireTableRowsInserted(size, size);
    }

    public void remove(Staff staff) {
        if (staffs.contains(staff) {
            int index = stafff.indexOf(staff);
            staffs.remove(staff);
            fireTableRowsDeleted(index, index);
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Staff staff = staffs.get(rowIndex);
        switch (columnIndex){
            case 0:
                return staff.getName();
            case 1:
                return staff.getSurname();
            case 2:
                return staff.getDate();
            case 3:
                return staff.getPosition();
            case 4:
                return staff.getSalary();
        }
        return "";
    }
}

还有你的actionPerformed

@Override
public void actionPerformed(ActionEvent e) {
    Staff staff = new Staff();
    staff.setName(JOptionPane.showInputDialog("Enter First Name"));
    staff.setSurname(JOptionPane.showInputDialog("Enter Second Name"));
    staff.setDate(JOptionPane.showInputDialog("Enter Date"));
    staff.setPosition(JOptionPane.showInputDialog("Enter Position"));
    staff.setSalary(JOptionPane.showInputDialog("Enter Salary"));
    try {
        Factory.getInstance().getStaffDAO().addStaff(staff);
        ((MyTableModel)model).add(staff);
    } catch (SQLException e1) {
        e1.printStackTrace();  
    }
}

【讨论】:

  • 我已切换到扩展 AbstractTableModel 的解决方案,添加了方法 fireTableDataChanged(),但它不会重新绘制我的表格。
  • 我假设模型已附加到有问题的表格中?
  • 是的,这样jTable = new JTable(model);
  • MadProgrammer,我已经在UPDATE1中添加了
  • 问题是,你实际上并没有在模型中添加任何东西。您将新员工添加到工厂,但工厂或模型之间没有任何联系。看看我做的他们的例子。我直接向模型添加了一个新行。您至少有 2 个选项。您将模型直接连接到工厂,以便模型直接从工厂提取数据。这将需要您为工厂提供一些机制来通知模型(和其他相关方)工厂气体发生了变化,或者,您还需要直接将人员添加到模型中,就像我在我的示例中那样
【解决方案2】:

使用这种类型的转换。

((AbstractTableModel)student.getModel()).fireTableCellUpdated();

【讨论】:

    【解决方案3】:

    对于更通用的解决方案,您可以使用 Row Table Model 并只实现 getValueAt() 和 setValueAt() 方法。

    这里应该有一些代码会在添加新行后触发我的表!

    模型负责调用正确的 fireXXX 方法。

    【讨论】:

      【解决方案4】:

      您的类MyTableModel 实现了TableModel,但它没有将模型连接到视图的事件处理机制。而是扩展AbstractTableModel,如herehere 所示。 AbstractTableModel 提供了所需的 fireTable* 方法。

      public void setValueAt(Object value, int row, int col) {
          data[row][col] = value;
          fireTableCellUpdated(row, col);
      }
      

      【讨论】:

      • 我不能再投票了,但是关于在setValueAt 方法中使用fireTableCellUpdated 的好建议!
      • 无耻改编自tutorial cited。 :-)
      【解决方案5】:

      首先,了解更多关于观察者模式 (http://en.wikipedia.org/wiki/Observer_pattern)。

      我建议您创建一个 ObservableModel 类,该类将包含一个 PropertyChangeListener 列表。您的 StaffDAO 应该扩展这个 ObservableModel。添加新员工时(即调用 addStaff),您应该调用 ObservableModel 的 firePorpertyChange 或类似的名称。 firePropertyChangenotifyies 所有 propertyChangeListeners。其中一个侦听器应在您的表中注册,其propertyChanged 方法应通过刷新表来实现(hd1)有一个很好的答案。

      【讨论】:

      • 有趣的想法,但我不确定PropertyChangeListener 是最合适的方法,因为它表明对象的单个属性已更改,而添加或删除则表明更改要求更大-恕我直言。如果可能的话,提供一个自定义界面会更有用,该界面可以提供有关模型状态更改的更有意义的信息,并包括对受影响的所有行的引用,从而更容易确定发生了什么变化(如有问题的行可能已被插入)
      【解决方案6】:

      myTableModel.fireTableDataChanged(); 应该刚好够force a refresh of your table。像往常一样,如果您还有其他问题,请随时发表评论。

      【讨论】:

      • 但是TableModel 没有fireTableDataChanged,我知道的唯一实现是protected
      • 你应该扩展DefaultTableModel,而不是自己实现。
      • 不一定。 fireTableDataChanged 来自AbstractTableModel,这可能更适合(恕我直言)人们的要求。我的观点是,OP 使用TableModel 作为那里的表模型的基础,并且它没有fireTableDataChanged,因此您当前的建议将不起作用。正确的想法...
      • @MadProgrammer 是正确的; DefaultTableModel 继承了 AbstractTableModel 的事件管道,但它会引诱您进入 java.util.Vector 的技术死胡同。
      猜你喜欢
      • 1970-01-01
      • 2014-12-10
      • 1970-01-01
      • 1970-01-01
      • 2017-10-29
      • 2013-09-20
      • 1970-01-01
      • 2011-06-01
      • 1970-01-01
      相关资源
      最近更新 更多