简体   繁体   中英

Unable to change values in JTable with custom TableModel using setValueAt

I'm coding small schedule of visits. It bases on JTable with custom TableModel extending AbstractTableModel as shown on image below.

The problem is that it is possible to initializate Table and to get desired look - but after data change using setValueAt() there is no change in appearance of Table. Button click takes sample String and sets fields in columns "Type" and "State" at 9:00 (more precisely depending on given hour and date of reservation)

What is more I'am able to insert new row at the end of table but cannot make visible existing value update. Tried many solutions but sadly without result. I would be very grateful for every suggestions.

在此处输入图片说明

Working code illustrating the problem:

import java.awt.EventQueue;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class ModelTest {

    private JFrame frame;
    private JTable tablePendingVisits;
    private PendingVisitModel pendingVisitModel;
    private JScrollPane scrollPanePendingVisits;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    ModelTest window = new ModelTest();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public ModelTest() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 407);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(null);

        JButton btnChangeValue = new JButton("Change value at 9:00");
        btnChangeValue.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                refreshTableModel();
            }
        });
        btnChangeValue.setBounds(63, 308, 305, 23);
        frame.getContentPane().add(btnChangeValue);

        tablePendingVisits = new JTable();
        scrollPanePendingVisits = new JScrollPane();
        pendingVisitModel = new PendingVisitModel();

        tablePendingVisits.setModel(pendingVisitModel);
        scrollPanePendingVisits.setBounds(63, 36, 305, 246);
        scrollPanePendingVisits.setViewportView(tablePendingVisits);

        frame.getContentPane().add(scrollPanePendingVisits);
    }

    public void refreshTableModel(){
        String[] sampleString = {"9:00", "Bobby", "Tables"};

        // search for row with 9:00 and replace values in given columns
        for (int i = 0; i < pendingVisitModel.getRowCount(); i++) {

            if( sampleString[0].equals(pendingVisitModel.getValueAt(i, 0)) ) {   // Change row values when both hours are equal 
                pendingVisitModel.setValueAt(sampleString[1], i, 1);             // Change at type column
                pendingVisitModel.setValueAt(sampleString[2], i, 2);             // Change at status column      
            }
        }
    }
}

// Custom TableModel
class PendingVisitModel extends AbstractTableModel {

    private static final long serialVersionUID = 1L;

    private String[] columnNames = {"HOUR", "TYPE", "STATE"};
    private Vector<String[]> data = new Vector<String[]>();

    public PendingVisitModel() {

        for(int i = 8; i<15; i++) {
            data.add(new String[]{i+":00", "-", "Free"} );
            data.add(new String[]{i+":15", "-", "Free"} );
            data.add(new String[]{i+":30", "-", "Free"} );
            data.add(new String[]{i+":45", "-", "Free"} );
        }
    }

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

    public int getRowCount() {
        return data.size();
    }

    public String getColumnName(int col) {
        return columnNames[col];
    }

    public String getValueAt(int row, int col) {
        String[] temp = data.get(row);
        if(temp.length > 0 && col < 3)
            return temp[col];
        else
            return null;
    }

    public void setValueAt(String[] value, int row, int col) {
        String[] temp = data.get(row);
        temp[col] = value[col];

        data.set(row, temp);
        fireTableRowsUpdated(row, row);
    }

    public void insertRow(String[] value) {
        data.add(value);
        fireTableRowsInserted(data.size(), data.size());
    }

    public void clearRows(){
        data.clear();
        fireTableDataChanged();
    }

    public void removeAllEntry(){
        data.clear();
        fireTableDataChanged();
    }

    public boolean isCellEditable(int row, int col) {
        return false;
    }

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

There is a difference between public void setValueAt(String[] value, int row, int col) { and public void setValueAt(Object value, int row, int col) {

You can test this by using the @Override annotation, for example...

@Override
public void setValueAt(String[] value, int row, int col) {

Generates a compiler error, where as

@Override
public void setValueAt(Object value, int row, int col) {

does not.

So changing the setValueAt method to...

@Override
public void setValueAt(Object value, int row, int col) {
    if (value instanceof String) {
        String[] temp = data.get(row);
        temp[col] = value.toString();

        data.set(row, temp);
        fireTableRowsUpdated(row, row);
    }
}

Worked just find for me

Also, you really should avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify

As noted in the comments, you could also use public void setValueAt(String value, int row, int col) { (no override annotation) and your code would work, but I wouldn't call the method setValueAt as that generates confusion (as you've discovered) and precludes the scenarios where all you have is an instance of TableModel

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM