CN1002
CN1002

Reputation: 1115

An Interactive JTable

I am developing a simple Java Application that uses a JTable. I am trying to implement what I have seen here. The table on the link given has behavior exactly the same as what I want in my table. I have done exactly as what is shown on the referenced link. However, when I run my application I am not able to edit the third and last column. The behavior that I want is that the application should add an empty row when the user press Enter upon filling the last column in the table.

Here is my first class, the entity class:

public class Product {
String productDescription;
String productCode;
double productPrice;
//dummy properties
int productQuantity;
double productTotalAmount;

 public Product() {
    this.productDescription = "";
    this.productCode = "";
    this.productPrice = 0;
    this.productTotalAmount = 0;
    this.productQuantity = 0;
}

public int getProductQuantity() {
    return productQuantity;
}

public void setProductQuantity(int productQuantity) {
    this.productQuantity = productQuantity;
}

public double getProductTotalAmount() {
    return productTotalAmount;
}

public void setProductTotalAmount(double productTotalAmount) {
    this.productTotalAmount = productTotalAmount;
}

public String getProductDescription() {
    return productDescription;
}

public void setProductDescription(String productDescription) {
    this.productDescription = productDescription;
}

public String getProductCode() {
    return productCode;
}

public void setProductCode(String productCode) {
    this.productCode = productCode;
}

public double getProductPrice() {
    return productPrice;
}

public void setProductPrice(double productPrice) {
    this.productPrice = productPrice;
}}

Here is my second class, the table model:

public class InteractiveTableModel extends AbstractTableModel{

// indexes
 public static final int PRODUCT_DESCRIPTION_INDEX = 0;
 public static final int PRODUCT_CODE_INDEX = 1;
 public static final int PRODUCT_PRICE_INDEX = 2;
 public static final int PRODUCT_QUANTITY_INDEX=3;
 public static final int PRODUCT_TOTAL_AMOUNT_INDEX=4;
 public static final int HIDDEN_INDEX = 5;

 protected String [] columnNames;
 protected Vector dataVector;

  //this constructor is responsible for initializing the columnmanes and the data of the table.
 public InteractiveTableModel(String[] columnNames) {
     this.columnNames = columnNames;
     dataVector = new Vector();
 }


@Override
 //this method returns the name of the column corresponding to the passed integer.
 public String getColumnName(int column) {
     return columnNames[column];
 }

  //this method is used to determine whether a cell is editable
 //this is for manuvouring to the next editable cell
 @Override

 public boolean isCellEditable(int row, int column) {
     if (column == HIDDEN_INDEX) return false;
     else return true;
 }

 @Override
      public Class getColumnClass(int column) {// returns a class representing the datatype of the data stored in that column
     switch (column) {
         case PRODUCT_DESCRIPTION_INDEX:
         case PRODUCT_CODE_INDEX:
            return String.class;
         case PRODUCT_QUANTITY_INDEX:
             return Integer.class;
         case PRODUCT_PRICE_INDEX:
         case PRODUCT_TOTAL_AMOUNT_INDEX:
             return double.class;
         default:
            return Object.class;
     }
 }

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

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

 @Override
 public Object getValueAt(int row, int column) {
     Product record = (Product)dataVector.get(row);//this will return a row in a vector
     //this is cast to AudioRecord so that we can extract the data that we want correspoinding to column number
     switch (column) {
         case PRODUCT_DESCRIPTION_INDEX:
            return record.getProductDescription();
         case PRODUCT_CODE_INDEX:
            return record.getProductCode();
         case PRODUCT_PRICE_INDEX:
            return record.getProductPrice();
         case PRODUCT_TOTAL_AMOUNT_INDEX:
            return record.getProductTotalAmount();
        case PRODUCT_QUANTITY_INDEX:
            return record.getProductQuantity();
         default:
            return new Object();
     }
 }

      //setting data in a cell
 @Override
 public void setValueAt(Object value, int row, int column) {
     Product record = (Product)dataVector.get(row);
     switch (column) {
         case PRODUCT_DESCRIPTION_INDEX:
            record.setProductDescription((String)value);
            break;
         case PRODUCT_CODE_INDEX:
            record.setProductCode((String)value);
            break;
         case PRODUCT_PRICE_INDEX:
            record.setProductPrice((Double)value);
            break;
         case PRODUCT_QUANTITY_INDEX:
            record.setProductQuantity((Integer)value);
             break;
           case PRODUCT_TOTAL_AMOUNT_INDEX:
            record.setProductTotalAmount((Double)value);
               break;
         default:
            System.out.println("invalid index");
     }
     fireTableCellUpdated(row, column);//this is for updating the cell
 }


 public boolean hasEmptyRow() {
     if (dataVector.isEmpty()) return false;
     Product productRecord = (Product)dataVector.get(dataVector.size() - 1);
     if (productRecord.getProductDescription().trim().equals("") &&
        productRecord.getProductCode().trim().equals("") &&
        (productRecord.getProductPrice()!=1 &&
         productRecord.getProductQuantity()!=1 &&
          productRecord.getProductTotalAmount()!=1))
     {
        return true;
     }
     else return false;
 }

 public void addEmptyRow() {
     dataVector.add(new Product());
     fireTableRowsInserted(
        dataVector.size() - 1,
        dataVector.size() - 1);
 }}

The last class is as follows:

 public final class InteractiveForm extends JPanel {

 public static final String[] columnNames = {
     "Quantity", "Product Code", "Product Description", "Product Price","Product Quantity",""
 };

 protected JTable table;
 protected JScrollPane scroller;
 protected InteractiveTableModel tableModel;

 public InteractiveForm() {
     initComponent();
 }

 public void initComponent() {
     tableModel = new InteractiveTableModel(columnNames);
     tableModel.addTableModelListener(new InteractiveForm.InteractiveTableModelListener());
     table = new JTable();
     table.setModel(tableModel);
     table.setSurrendersFocusOnKeystroke(true);
     if (!tableModel.hasEmptyRow()) {
         tableModel.addEmptyRow();
     }

     scroller = new javax.swing.JScrollPane(table);
     table.setPreferredScrollableViewportSize(new java.awt.Dimension(500, 300));
     TableColumn hidden = table.getColumnModel().getColumn(InteractiveTableModel.HIDDEN_INDEX);
     hidden.setMinWidth(2);
     hidden.setPreferredWidth(2);
     hidden.setMaxWidth(2);
     hidden.setCellRenderer(new InteractiveRenderer(InteractiveTableModel.HIDDEN_INDEX));

     setLayout(new BorderLayout());
     add(scroller, BorderLayout.CENTER);
 }


 public void highlightLastRow(int row) {
     int lastrow = tableModel.getRowCount();//return the number of rows - last row
     if (row == lastrow - 1) {
         table.setRowSelectionInterval(lastrow - 1, lastrow - 1);
     } else {
         table.setRowSelectionInterval(row + 1, row + 1);
     }

     table.setColumnSelectionInterval(0, 0);//setting it to the first column
 }

 class InteractiveRenderer extends DefaultTableCellRenderer {
     protected int interactiveColumn;

     public InteractiveRenderer(int interactiveColumn) {
         this.interactiveColumn = interactiveColumn;
     }

     @Override
     public Component getTableCellRendererComponent(JTable table,
        Object value, boolean isSelected, boolean hasFocus, int row,
        int column)
     {
         Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
         if (column == interactiveColumn && hasFocus) {
             if ((InteractiveForm.this.tableModel.getRowCount() - 1) == row &&
                !InteractiveForm.this.tableModel.hasEmptyRow())
             {
                 InteractiveForm.this.tableModel.addEmptyRow();//this is where we append a new row
             }

             highlightLastRow(row);//making it get focus and highlighted
         }

         return c;//returning the component
     }
 }

 public class InteractiveTableModelListener implements TableModelListener {
     @Override
     public void tableChanged(TableModelEvent evt) {
         if (evt.getType() == TableModelEvent.UPDATE) {
             int column = evt.getColumn();
             int row = evt.getFirstRow();
             System.out.println("row: " + row + " column: " + column);
             table.setColumnSelectionInterval(column + 1, column + 1);
             table.setRowSelectionInterval(row, row);
         }
     }
 }

 public static void main(String[] args) {
     try {
         UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
         JFrame frame = new JFrame("Interactive Form");
         frame.addWindowListener(new WindowAdapter() {
             @Override
             public void windowClosing(WindowEvent evt) {
                 System.exit(0);
             }
         });
         frame.getContentPane().add(new InteractiveForm());
         frame.pack();
         frame.setVisible(true);
     } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException | HeadlessException e) {
     }
 }}

The only differences that I have made from the table on the link above is to add my own variables of which that should not make it not work.The logic is escaping me here.

How can I make my table behave the same way as this one?

Upvotes: 1

Views: 815

Answers (1)

PbxMan
PbxMan

Reputation: 7623

You are setting null values. It works OK with Double.class not double.class.

    public class Product {
    String productDescription;
    String productCode;
    Double productPrice;
    //dummy properties
    Double productQuantity;
    Double productTotalAmount;
..
    @Override
        public Class getColumnClass(int column) {// returns a class representing the datatype of the data stored in that column
       switch (column) {
           case PRODUCT_DESCRIPTION_INDEX:
           case PRODUCT_CODE_INDEX:
              return String.class;
           case PRODUCT_QUANTITY_INDEX:
           case PRODUCT_PRICE_INDEX:
           case PRODUCT_TOTAL_AMOUNT_INDEX:
               return Double.class;
           default:
              return Object.class;
       }
    }
...

UPDATE Something related with the boxing/unboxing. I know who to fix it but I still don't know the real cause.

Upvotes: 2

Related Questions