Question

I want to design this JTable.

enter image description here

The goal is the user to be able to edit the cells by double clicking on them. Any changes should be able to be saved and stored on a database later.

My code is the following:

public class BirthGUI extends JFrame implements TableModelListener {

//..//

//-------------------- BIRTH TABLE --------------------

                table = new JTable();
                table.setModel(myModel);
                myModel.reloadBirthJTable();

                table.getColumnModel().getColumn(0).setPreferredWidth(100);
                table.getColumnModel().getColumn(1).setPreferredWidth(80);
                table.getColumnModel().getColumn(2).setPreferredWidth(115);
                table.setRowHeight(20);

                JScrollPane scroller = new JScrollPane(table );
                table.setBounds(49, 85, 295, 374);

                            table.getModel().addTableModelListener(this);


                panel.add(scroller,BorderLayout.CENTER); 

        @Override
        public void tableChanged(TableModelEvent e) {
            System.out.println("TableModelEvent triggered!");
            int row = e.getFirstRow();
            int column = e.getColumn();
            Object test = myModel.getValueAt(row, column);      
            System.out.println("row: " + row + " column: " + column);   
            System.out.println(test.toString());
        }

and my TableModel:

public class BirthTableModel extends DefaultTableModel {

        private String[] columnNames = {"Date", "Children", "Complications/Comments"};      //column header labels
        private Object[][] data = new Object[20][3];
        private  List<Birth>  list;

        public void reloadBirthJTable() {
            System.out.println("loading birth table..");
            for(int i=0; i<20; i++){
                data[i][0] = "";
                data[i][1] = "";
                data[i][2] = "";
                this.addRow(data);
            }
        }

        public void clearJTable() {
            this.setRowCount(0);
        }

        @Override
        public void addRow(Object[] arg0) {
            // TODO Auto-generated method stub
            super.addRow(arg0);
        }

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

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }


        public Class getColumnClass(int c) {
            for(int rowIndex = 0; rowIndex < getRowCount(); rowIndex++) { 
                Object[] row = data[rowIndex];
                if (row[c] != null) {
                    return getValueAt(rowIndex, c).getClass();
                }
            }
            return String.class;
        } 

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

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

        }
    }

The problem is that while the cells are editable after I double click on them and the tableChanged() is triggered, any change I make to them is deleted as soon the cell loses focus...

My output while modifying cells is this:

loading birth table..
TableModelEvent triggered!
row: 1 column: 0

TableModelEvent triggered!
row: 0 column: 0

so test seems to be empty..

Was it helpful?

Solution

Recommendations:

  • Since you want your model to hold a list or an array of Birth objects, I would have BirthTableModel extend AbstractTableModel, not DefaultTableModel.
  • This will require that you override the abstract methods of the class.
  • And that you call the fire***(...) notification methods whenever you make changes to the data held by the model.
  • and as per Robin's comment, implement the setValueAt method.
  • I'd also implement an addRow(...) method.
  • Avoid trying to set the bounds of any component. This can make for a class that is very difficult to update, modify or improve. Let the layout managers do this heavy work for you.

An example of what I mean by AbstractTableModel:

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.*;

public class TestTableModel {
   public static void main(String[] args) {
      BirthTableModel2 dm2 = new BirthTableModel2();
      JTable table = new JTable(dm2);
      JOptionPane.showMessageDialog(null, new JScrollPane(table));

      dm2.reloadBirthJTable();
      JOptionPane.showMessageDialog(null, new JScrollPane(table));
   }

}


// I had to guess at this class
class Birth {
   private Date date;
   private String children;
   private String comments;

   public Birth(Date date, String children, String comments) {
      this.date = date;
      this.children = children;
      this.comments = comments;
   }

   public Date getDate() {
      return date;
   }

   public String getChildren() {
      return children;
   }

   public String getComments() {
      return comments;
   }

   public void setDate(Date date) {
      this.date = date;
   }

   public void setChildren(String children) {
      this.children = children;
   }

   public void setComments(String comments) {
      this.comments = comments;
   }

}

@SuppressWarnings("serial")
class BirthTableModel2 extends AbstractTableModel {
   public final static String[] COLUMN_NAMES = { "Date", "Children",
   "Complications/Comments" }; // column header labels
   private List<Birth> birthList = new ArrayList<>();

   @Override
   public Class<?> getColumnClass(int columnIndex) {
      if (columnIndex == 0) {
         return Date.class;
      }
      return super.getColumnClass(columnIndex);
   }

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

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

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

   public void reloadBirthJTable() {
      System.out.println("loading birth table..");
      clearModel();
      for (int i = 0; i < 20; i++) {
         addRow();
      }
   }

   public void clearModel() {
      birthList.clear();
      fireTableDataChanged();
   }

   public void addRow() {
      birthList.add(new Birth(null, "", ""));
      fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
   }

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

   @Override
   public Object getValueAt(int rowIndex, int colIndex) {
      if (rowIndex < 0 || rowIndex >= getRowCount()) {
         // throw exception
      }
      if (colIndex < 0 || colIndex >= getColumnCount()) {
         // throw exception
      }
      Birth birth = birthList.get(rowIndex);
      switch (colIndex) {
      case 0:
         return birth.getDate();

      case 1:
         return birth.getChildren();

      case 2:
         return birth.getComments();
      }
      return null;
   }

   @Override
   public void setValueAt(Object aValue, int rowIndex, int colIndex) {
      if (rowIndex < 0 || rowIndex >= getRowCount()) {
         // throw exception
      }
      if (colIndex < 0 || colIndex >= getColumnCount()) {
         // throw exception
      }

      Birth birth = birthList.get(rowIndex);
      switch (colIndex) {
      case 0:
         birth.setDate((Date)aValue);
         break;
      case 1:
         birth.setChildren(aValue.toString());
         break;
      case 2:
         birth.setComments(aValue.toString());
      }
      fireTableRowsUpdated(rowIndex, rowIndex);

   }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top