I have a JTable that has four columns. In the second column I have assigned a JComboBox to be the cell editor of my column.The problem that I encountered is that I cannot handle the combobox the way I want.

What I want is that after a user select an item in the combobox, the program should add a new row to the table model and shift the focus to this new row in the first cell. It should then not worry about the remaining columns, I will not allow them to be editable.

Please assist, I will post all the classes below. Thanks for help

Classs 1:

 public class Product {
Integer quantity;
String decsription;
Double price;
Double tprice;

public Integer getQuantity() {
    return quantity;
}

public void setQuantity(Integer quantity) {
    this.quantity = quantity;
}

public String getDecsription() {
    return decsription;
}

public void setDecsription(String decsription) {
    this.decsription = decsription;
}

public Double getPrice() {
    return price;
}

public void setPrice(Double price) {
    this.price = price;
}

public Double getTprice() {
    return tprice;
}

public void setTprice(Double tprice) {
    this.tprice = tprice;
}

 public Product() {
    this.quantity = 0;
    this.decsription = "";
    this.price = 0.0;
    this.tprice = 0.0;
}}

class 2:

   public class Model extends AbstractTableModel {

static final int qi = 0;
static final int di = 1;
static final int ui = 2;
static final int ti = 3;
static final int hi=4;

protected String[] colNames;
protected Vector data;

Model(String[] col) {
    this.colNames = col;
    this.data = new Vector();
}

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

@Override
public String getColumnName(int col) {
    return this.colNames[col];
}

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

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
    Product record = (Product) data.get(rowIndex);
    switch (columnIndex) {
        case qi:
            return record.getQuantity();
        case di:
            return record.getDecsription();
        case ui:
            return record.getPrice();
        case ti:
            return record.getTprice();
        default:
            return new Object();
    }

}

@Override
public boolean isCellEditable(int row, int col) {
   return col<=3;

}

@Override
public void setValueAt(Object value, int row, int column) {
    Product record = (Product) data.get(row);
    switch (column) {
        case qi:
            record.setQuantity((Integer) value);
            break;
        case di:
            record.setDecsription((String) value);
            break;
        case ui:
            record.setPrice((Double) value);
            break;
        case ti:
            record.setTprice((Double) value);
            break;
        default:
            System.out.println("invalid index");
    }
    fireTableCellUpdated(row, column);//this is for updating the cell
}

@Override
public Class getColumnClass(int column) {// returns a class representing the datatype of the data stored in that column
    switch (column) {
        case qi:
            return Integer.class;
        case di:
            return String.class;
        case ui:
        case ti:
            return Double.class;
        default:
            return Object.class;
    }
}

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

}

public boolean hasEmptyRow() {
    if (data.isEmpty()) {
        return false;
    }
    Product productRecord = (Product) data.get(data.size() - 1);
    if (productRecord.getDecsription().trim().equals("")
            && productRecord.getPrice() == 0.0
            && productRecord.getQuantity() == 0
            && productRecord.getTprice() == 0) {
        return true;
    } else {
        return false;
    }}

class 3:

public final class Table {

JTable table;
JScrollPane sp;
static final String[] columNames = {"Quantity", "Description", "Unity Price", "Total Price", ""};
Model model = new Model(columNames);
String[] values = {"1", "2", "3"};

public Table() {

    createTable();
}

public void createTable() {

    JFrame f = new JFrame();
    f.setSize(300, 300);
    f.setVisible(true);

    //information regarding the table here
    table = new JTable();
    table.setModel(model);
    table.setSurrendersFocusOnKeystroke(true);
    table.setPreferredScrollableViewportSize(new java.awt.Dimension(300, 300));
    TableColumn col = table.getColumnModel().getColumn(1);
    col.setCellEditor(new myComboBoxEditor(values));
    col.setCellRenderer(new MyComboBoxRenderer(values));

    sp = new javax.swing.JScrollPane(table);

    //information on table model
    model.addTableModelListener(new Table.InteractiveTableModelListener());
    if (!model.hasEmptyRow()) {
        model.addEmptyRow();
    }

    //
    f.add(sp);
    TableColumn hidden = table.getColumnModel().getColumn(Model.hi);
    hidden.setMinWidth(2);
    hidden.setPreferredWidth(2);
    hidden.setMaxWidth(2);
    hidden.setCellRenderer(new InteractiveRenderer(Model.hi));
}

public void highlightLastRow(int row) {
    int lastrow = model.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 ((Table.this.model.getRowCount() - 1) == row
                    && !Table.this.model.hasEmptyRow()) {
                Table.this.model.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);
        }
    }
}

private class myComboBoxEditor extends DefaultCellEditor {

    myComboBoxEditor(String[] items) {
        super(new JComboBox(items));

    }
}

private class MyComboBoxRenderer extends JComboBox implements TableCellRenderer {

    public MyComboBoxRenderer(String[] items) {
        super(items);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column) {
        if (isSelected) {
            setForeground(table.getSelectionForeground());
            super.setBackground(table.getSelectionBackground());
        } else {
            setForeground(table.getForeground());
            setBackground(table.getBackground());
        }
        setSelectedItem(value);
        return this;
    }

}

public static void main(String[] args) {

    Table tab = new Table();
}}

Before reading all that code...
How much of the requirement is working already? How much is implemented, but not working properly (detail incorrect behaviour vs correct). How much isn't implemented at all?

TableCellRenderer is called from all (could be very intensive, short period and repeatly)

  • Mouse Events inside JTable

  • Key Events

  • events implemented in rellated APIs

Hi James

The situation is as follows:

The table is being shown with the combobox as I want it.
The combobox is shown correctly and in the correect column.

I have tried to put this code inside mymyComboboxRenderer{} as follows

 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
if (isSelected) {
model.addEmptyRow();//this method is for adding the row
Table.this.highLightLastRow();this method is for changing focus

} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
setSelectedItem(value);
return this;
}
}

However, it is not working.
I would want it to work as follows,this is what it is not doing:

  1. When a user select an item in the combobox(this combobox is in the second column),the program should automatically add/append a new empty row to the table-model.addEmptyRow().
  2. After adding the row, it should then move focus to the newly created row -the focus should be in the last row(which would be the newly created one)
    and in the fisrt colum- Table.this.highlisghtLastRow().

We can't help you if you don't help us by answering our questions.

"is not working" tells us nothing. Do you mean nothing happens at all? Or something happens but its wrong (in which case exactly what does it do that's wrong) = give DETAILS.

Hie James,
The problem is if you select an item, the program will keep on adding/appending a new row as if it is in some kind of a loop. I would want it to add one row every time the user select an item in the combobox.

OK - that makes it a lot clearer. You should have said that right at the start to save time.

My guess is that when the cell is rendered it adds the row, which causes the table to be re-drawn - which renders the cell again - ... loop

The cell renderer isn't a good place to do this. It's just part of the painting, and could get called any time. I would try adding a listener to the combo box(es) and using that to add the row etc when the user selection changes in the combo box.

aaaach :-)

My guess is that when the cell is rendered it adds the row, which causes the table to be re-drawn - which renders the cell again - ... loop

The cell renderer isn't a good place to do this. It's just part of the painting, and could get called any time.

yes, exactly

I would try adding a listener to the combo box(es) and using that to add the row etc when the user selection changes in the combo box.

use events implemented in JTables API, e.g. setValueAt is very good start point, easier, direct, without something moreover, nor code, why lots of expensive code lines

please could you elaborate with sample codes; that would be very much helpful.

  1. Why is there AbstractTableModel (works only for methods that are override and if is the correct way), to use DefaultTableModel

  2. Why is there used POJO (public class Product {), two models for the same thing (sure if Product is major model then it make me the sence)

  3. read Oracle tutorial - How to use Tables, part ComboBox as editor, for working code example

  4. why custom editor and renderer for basics stuff, is there some advantage to see painted JComboBox in the JTables cell, I'm really doubt in..

  5. why are there two renderers

  6. (as where mentioned in this thread) don't use renderer for another job as cells decorating, painting is (your renderer can to add 1K rows per one minute from simple mouse events over the JTables view)

  7. is very confusing to add a new row from selection made by (JComboBox as) TableCellEditor,

  8. etc :-)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.