Multiplication between two cells of a JTable in Java - java

When I enter values in the cells of the 1st row of columns "Units" and "Price" , column "Amount"; the columns cell value should be set to the product of unit and amount . Which listener should I use? Please help as I'm new to Java.
CODE:
JTable jTable1;
DefaultTableModel mod=new DefaultTableModel();
mod.addColumn("No");
mod.addColumn("Item ID");
mod.addColumn("Units");
mod.addColumn("Amount");
mod.addColumn("UOM");
mod.addColumn("Delivery Date");
mod.addColumn("Total Amount");
mod.addColumn("Notes");
mod.addColumn("Received");
mod.addRow(new Object [][] {
{1, null, null, null, null, null, null, null, null}
});
jTable1.setModel(mod);
jTable1.setColumnSelectionAllowed(true);

The idea is to implement a TableModel so that it automatically returns the result of the product of unit and amount. Here is a small demonstration of what I mean (to add a new row, simply double click below the table):
import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
public class TestTable3 {
protected void initUI() {
final DefaultTableModel model = new DefaultTableModel() {
#Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
return Integer.class;
case 1:
case 2:
return Double.class;
}
return super.getColumnClass(columnIndex);
}
#Override
public Object getValueAt(int row, int column) {
if (column == 2) {
Integer i = (Integer) getValueAt(row, 0);
Double d = (Double) getValueAt(row, 1);
if (i != null && d != null) {
return i * d;
} else {
return 0.0;
}
}
return super.getValueAt(row, column);
}
#Override
public boolean isCellEditable(int row, int column) {
return column == 0 || column == 1;
}
#Override
public void setValueAt(Object aValue, int row, int column) {
super.setValueAt(aValue, row, column);
fireTableCellUpdated(row, 2);
}
#Override
public String getColumnName(int column) {
switch (column) {
case 0:
return "Quantity";
case 1:
return "Price";
case 2:
return "Total";
}
return super.getColumnName(column);
}
#Override
public int getColumnCount() {
return 3;
}
};
final JTable table = new JTable(model);
table.setFillsViewportHeight(true);
table.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
if (table.rowAtPoint(e.getPoint()) < 0) {
model.addRow(new Vector());
}
}
}
});
model.addRow(new Vector());
JFrame frame = new JFrame(TestTable3.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollpane = new JScrollPane(table);
frame.add(scrollpane, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTable3().initUI();
}
});
}
}
And the result:

When I enter values in the cells of the 1st row of columns "Units" and
"Price" , column "Amount"; the columns cell value should be set to the
product of unit and amount
have look at TableModelListener
have to read the Oracle JTable tutorial

Related

Swing - sort a JTable's rows but keep them grouped by one column and only show the value once on top of each group

I want to sort a JTable like this:
That is: (EDIT)
If I click the "Name" column to sort, "Tom", "Polazzo" and "Anna" must be sorted alphabetically, and rows with the same names must stay together("grouped" by the name), and each name should be shown only once, the rest cells must be blank.
If I click the "Duration" or "Book #" column, I want all rows sorted ascending/descending by values of duration/book number, but same as in point 1), rows with the same "Name" must stay together, that is, stay grouped, and only the first row in every group is shown, and the rest "Name" stay blank.
The data in the table model's vector are collected from parsing a XML file. The rows with same "Name" are under the same node in the hierarchy tree.
I think there're two ways to do this:
a) When collecting the data and construct the rows, under the same "Name" node, give the cell at column 0 the "Name" value, and leave the rest of rows "" in the same column. But, I don't know how to construct the comparator of column "Name", to ensure the first row always being the top in sorting. (It cannot be the biggest and the smallest when we override compare() method, can it?)
b) Every time we click the table header to sort, make the renderer repaint the table the way we want: comparing the value in the first line of each group, and if it's the same as the last line's column 0's value, don't paint this cell, until we reach another different value. In that way, we don't mess with comparators/sorters, and it turns into a renderer problem. That's what I kind of achieved in the SSCCE below, but I am half way there and I need some tips.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.List;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Comparator;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.event.RowSorterEvent;
import javax.swing.event.RowSorterListener;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import com.WindThunderStudio.JHeaderToolTip.JHeaderToolTip;
import net.miginfocom.swing.MigLayout;
public class RowGroupInTable extends JFrame {
public RowGroupInTable() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel();
Object[][] data = new Object[][] {{"Tom", "17", "Book1"},
{"Tom", 23, "Book2"},
{"Tom", 25, "Book3"},
{"Polazzo", 41, "Book1"},
{"Polazzo", 45, "Book2"},
{"Polazzo", 12, "Book3"},
{"Anna", 1, "Book3"},
{"Anna", 33, "Book5"}};
String[] titles = new String[] {"Name", "Last job duration", "Book #"};
JTable table = new JTable(data, titles);
table.setFillsViewportHeight(true);
table.setAutoCreateRowSorter(false);
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
ArrayList<SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
sortKeys.add(new SortKey(2, SortOrder.ASCENDING));
sortKeys.add(new SortKey(1, SortOrder.ASCENDING));
// sorter.setSortKeys(sortKeys);
sorter.setSortable(0, true);
sorter.setSortable(1, false);
sorter.setSortable(2, true);
table.setRowSorter(sorter);
table.setDefaultRenderer(Object.class, new MyRenderer(table.getDefaultRenderer(Object.class)));
JTableHeader header = table.getTableHeader();
header.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent e) {
int col = ((JTableHeader)(e.getComponent())).getColumnModel().getColumnIndexAtX(e.getX());
}
});
JScrollPane sp = new JScrollPane(table);
sp.setBounds(0, 0, 200, 200);
add(sp, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
RowGroupInTable frame = new RowGroupInTable();
}
});
}
private class MyRenderer implements TableCellRenderer {
TableCellRenderer def;
public MyRenderer() {
// TODO Auto-generated constructor stub
}
public MyRenderer(TableCellRenderer defaultRend) {
this();
this.def = defaultRend;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
int rowCount = table.getModel().getRowCount();
Component orig = (def).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (column == 0) {
if (row == 0) {
return orig;
} else if (row > 0 && row < rowCount) {
if (table.getModel().getValueAt(row-1, column).equals(value)) {
return new JLabel("");
} else {
return orig;
}
}
}
return orig;
}
}
}
each name should be shown only once, the rest cells must be blank.
If I understand your requirement you might be able to use table.getValueAt(...) instead of table.getModel().getValueAt(...):
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;
public class RowGroupInTableTest {
private JComponent makeUI() {
String[] titles = new String[] {"Name", "Last job duration", "Book #"};
DefaultTableModel model = new DefaultTableModel(null, titles) {
#Override public Class<?> getColumnClass(int column) {
return MyData.class;
}
};
addMyData(model, new MyData("Tom", 17, "Book1"));
addMyData(model, new MyData("Tom", 23, "Book2"));
addMyData(model, new MyData("Tom", 25, "Book3"));
addMyData(model, new MyData("Polazzo", 41, "Book1"));
addMyData(model, new MyData("Polazzo", 45, "Book2"));
addMyData(model, new MyData("Polazzo", 12, "Book3"));
addMyData(model, new MyData("Anna", 1, "Book3"));
addMyData(model, new MyData("Anna", 33, "Book5"));
JTable table = new JTable(model);
table.setFillsViewportHeight(true);
table.setDefaultRenderer(MyData.class, new MyRenderer());
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
Comparator<MyData> c = Comparator.comparing(MyData::getName);
sorter.setComparator(0, c);
sorter.setComparator(1, c.thenComparing(Comparator.comparingInt(MyData::getDuration)));
sorter.setComparator(2, c.thenComparing(Comparator.comparing(MyData::getBook)));
table.setRowSorter(sorter);
return new JScrollPane(table);
}
private static void addMyData(DefaultTableModel model, MyData data) {
//Omission work...
model.addRow(Collections.nCopies(3, data).toArray());
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new RowGroupInTableTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class MyData {
private final String name;
private final int duration;
private final String book;
protected MyData(String name, int duration, String book) {
this.name = name;
this.duration = duration;
this.book = book;
}
public String getName() {
return name;
}
public int getDuration() {
return duration;
}
public String getBook() {
return book;
}
}
class MyRenderer implements TableCellRenderer {
TableCellRenderer def = new DefaultTableCellRenderer();
#Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
JLabel orig = (JLabel) def.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
orig.setHorizontalAlignment(SwingConstants.LEFT);
MyData data = (MyData) value;
switch (table.convertColumnIndexToModel(column)) {
case 0:
String str = data.getName();
if (row > 0) {
//if (table.getModel().getValueAt(row-1, column).equals(value)) {
//Since it compares with the value of the previous line on the display,
//table.getModel() is not needed
MyData prev = (MyData) table.getValueAt(row - 1, column);
if (Objects.equals(prev.getName(), str)) {
str = " ";
}
}
orig.setText(str);
break;
case 1:
orig.setHorizontalAlignment(SwingConstants.RIGHT);
orig.setText("" + data.getDuration());
break;
case 2:
orig.setText(data.getBook());
break;
default:
break;
}
return orig;
}
}
edit
Now if I only use Java 7, is there some "old" way to do this? Just setting the comparators in the Java 7 way?
You would need to impliment Comparator:
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
//Comparator<MyData> c = Comparator.comparing(MyData::getName);
//sorter.setComparator(0, c);
//sorter.setComparator(1, c.thenComparing(Comparator.comparingInt(MyData::getDuration)));
//sorter.setComparator(2, c.thenComparing(Comparator.comparing(MyData::getBook)));
sorter.setComparator(0, new MyDataGroupComparator(0));
sorter.setComparator(1, new MyDataGroupComparator(1));
sorter.setComparator(2, new MyDataGroupComparator(2));
table.setRowSorter(sorter);
class MyDataGroupComparator implements Comparator<MyData> {
private final int column;
protected MyDataGroupComparator(int column) {
this.column = column;
}
#Override public int compare(MyData a, MyData b) {
if (a == null && b == null) {
return 0;
} else if (a != null && b == null) {
return -1;
} else if (a == null && b != null) {
return 1;
} else {
int v = a.getName().compareTo(b.getName());
if (v == 0) {
switch (column) {
case 2:
return a.getBook().compareTo(b.getBook());
case 1:
return a.getDuration() - b.getDuration();
case 0:
default:
return v;
}
}
return v;
}
}
}
when I change table.getModel().getValueAt() to table.getValueAt() I cannot get my original example to work. Why?
Works fine for me(only the cell under Anna is blank):

JTable valuechanged then change cell color

I have here a JTable with two(2) columns. The right column is an editable one while the other is not.
So, what my problem is that whenever the user changed the value of a cell, that specific cell will changed its cell color.
I wanna do this because I want to let the user know that he/she made some changes in the table.
I found this somewhere and it somehow solved my problem but 1 thing that didn't come up with my expectation is that after changing the value and clicked another cell, the color changes back to its original color. I want to let it stay until it is saved.
#Override
public Component prepareEditor(TableCellEditor editor, int data, int columns) {
Component c = super.prepareEditor(editor, data, columns);
c.setBackground(Color.RED);
return c;
}
Is it possible? If yes, please show some example.
UPDATE:
String[] columnname = {"Student Name", "Grade"};
Object[][] data = {};
gradetable = new JTable(data, columnname){
private Object[][] rowData;
public boolean isCellEditable(int data, int columns){
return columns == 1;
}
public Component prepareRenderer(TableCellRenderer r, int data, int columns){
final Component c = super.prepareRenderer(r, data, columns);
if (data % 2 == 0){
c.setBackground(Color.LIGHT_GRAY);
}
else{
c.setBackground(Color.WHITE);
}
if (isCellSelected(data, columns)){
c.setBackground(Color.ORANGE);
}
return c;
}
#Override
public Component prepareEditor(TableCellEditor editor, int data, int columns) {
Component c = super.prepareEditor(editor, data, columns);
c.setBackground(Color.RED);
return c;
}
};
gradetable.setModel(new DefaultTableModel(data, columnname));
gradetable.setPreferredScrollableViewportSize(new Dimension (350, 130));
gradetable.setFillsViewportHeight(true);
gradetable.getTableHeader().setReorderingAllowed(false);
gradetable.setGridColor(new Color(128,128,128,128));
JScrollPane jsp = new JScrollPane(gradetable);
panel3.add(jsp);
Tables use a TableCellRenderer to paint values on the screen. The editors and renderers don't actually have anything to do with each other (from a painting point of view).
So once the editor has been dismissed (accepted or cancelled), the cell is repainted using the assigned TableCellRenderer
You need to supply, in your table model, some way to determine which rows have been updated and change the state of the renderer to match.
FYI- The DefaultTableCellRenderer uses a JLabel as it's base renderer, so it is transparent by default; you will need to make it opaque to make it render properly.
Check out Using custom renderers for more details
Update with example
This is nothing more then a proof of concept. It will not meet your absolute requirements and you should take a serious look at the tutorial linked above.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
public class TableEdit {
public static void main(String[] args) {
new TableEdit();
}
public TableEdit() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JTable table = new JTable(new MyTableModel());
table.setSurrendersFocusOnKeystroke(true);
TableColumnModel model = table.getColumnModel();
model.getColumn(1).setCellRenderer(new MyTableCellRenderer());
add(new JScrollPane(table));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class MyData {
private String key;
private String value;
private boolean changed;
public MyData(String key, String value) {
this.key = key;
this.value = value;
this.changed = false;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
public void setValue(String newValue) {
if (value == null ? newValue != null : !value.equals(newValue)) {
value = newValue;
changed = true;
}
}
public boolean hasChanged() {
return changed;
}
}
public class MyTableModel extends AbstractTableModel {
private List<MyData> data;
public MyTableModel() {
data = new ArrayList<>(25);
for (int index = 0; index < 5; index++) {
data.add(new MyData("A" + (index + 1), "B" + (index + 1)));
}
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
MyData myData = data.get(rowIndex);
Object value = null;
switch (columnIndex) {
case 0:
value = myData.getKey();
break;
case 1:
value = myData.getValue();
break;
}
return value;
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 1;
}
public boolean hasChanged(int rowIndex) {
MyData myData = data.get(rowIndex);
return myData.hasChanged();
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
MyData myData = data.get(rowIndex);
myData.setValue(aValue == null ? null : aValue.toString());
}
}
public class MyTableCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
setOpaque(isSelected);
TableModel model = table.getModel();
if (model instanceof MyTableModel) {
MyTableModel myModel = (MyTableModel) model;
if (myModel.hasChanged(row)) {
if (!isSelected) {
setBackground(Color.RED);
setOpaque(true);
}
}
}
return this;
}
}
}

JTable cell renderer skips Boolean column

Below is a picture of my JTable. My problem is that the blue background every other row isn't present in the 4th column. My table model and cell renderer are also below.
Table model:
public class MyTableModel extends DefaultTableModel {
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int column) {
return columnNames[column];
}
public int getRowCount() {
return data.length;
}
public Object getValueAt(int row, int column) {
return data[row][column];
}
public Class getColumnClass(int column) {
return (getValueAt(0, column).getClass());
}
public void setValueAt(Object value, int row, int column) {
data[row][column] = value;
}
public boolean isCellEditable(int row, int column) {
return (column == 3);
}
}
Cell Renderer:
private class CalendarRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) {
super.getTableCellRendererComponent(table, value, selected, focused, row, column);
setBackground(new Color(0xFFFFFF));
if (row % 2 == 1) {
setBackground(new Color(0xE8F2FE)); //light blue
}
setBorder(null);
setForeground(Color.black);
return this;
}
}
The "default" cell renderer for Boolean is a check box. If you want to change the way that this cell gets rendered, you're going to have to supply your own TableCellRenderer that implements your own logic.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
public class TestTableCellRenderer {
public static void main(String[] args) {
new TestTableCellRenderer();
}
public TestTableCellRenderer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTable table = new JTable();
table.setModel(new TableModel());
table.setDefaultRenderer(Boolean.class, new BooleanCellRenderer());
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);
}
});
}
public class TableModel extends AbstractTableModel {
private List<Boolean> values = new ArrayList<>(25);
public TableModel() {
for (int index = 0; index < 25; index++) {
values.add((((int) Math.round(Math.random() * 1)) == 0 ? false : true));
}
}
#Override
public int getRowCount() {
return values.size();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return values.get(rowIndex);
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return Boolean.class;
}
}
public static class BooleanCellRenderer extends JCheckBox implements TableCellRenderer {
private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
public BooleanCellRenderer() {
setLayout(new GridBagLayout());
setMargin(new Insets(0, 0, 0, 0));
setHorizontalAlignment(JLabel.CENTER);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof Boolean) {
setSelected((Boolean) value);
}
if (!isSelected) {
setBackground(new Color(0xFFFFFF));
if (row % 2 == 1) {
setBackground(new Color(0xE8F2FE)); //light blue
}
setForeground(Color.black);
} else {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
}
if (hasFocus) {
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
} else {
setBorder(noFocusBorder);
}
return this;
}
}
}
See Table Row Rendering for a solution that doesn't require you to keep creating custom renderers every time you have columns with different data.
JTable table = new JTable( model )
{
public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
{
Component c = super.prepareRenderer(renderer, row, column);
// Alternate row color
if (!isRowSelected(row))
c.setBackground(row % 2 == 0 ? getBackground() : Color.LIGHT_GRAY);
return c;
}
};

Java, change a cell content as a function of another cell in the same row

I need some help for my problem. I have a table with e.g. a double column and a string column. If the value in the double column is negativ, the string should be "negativ". And the other way if the value is positiv, the string should be "positiv".
The problem is now if I edit the double value in the jTable, the string should also be updated.
Update to my question, the actual code look like this:
But it doesn't work, because the string in the second column wont be updated after I edit the first column value. It only works when I start the program the first time.
import java.util.Vector;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.*;
public class ExampleRemoveAddRows extends JFrame {
private Object[] columnNames = {"Double", "positiv / negativ"};
private Object[][] data = {
{new Double(10.0), "positiv"},
{new Double(-10.0), "negativ"},
{new Double(20.0), "positiv"},
{new Double(-30.0), "negativ"}
};
private JTable table;
private DefaultTableModel model;
public ExampleRemoveAddRows() {
model = new DefaultTableModel(data, columnNames) {
#Override
public Class getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
#Override
public Object getValueAt(int row, int column) {
if (column == 1) {
double number = Double.parseDouble(this.getValueAt(row, 0).toString());
System.out.println(number);
System.out.println("good");
System.out.println((number < 0) ? "negativ" : "positiv");
return "C: "+ this.getValueAt(row, 0);//((number < 0) ? "negativ" : "positiv");
} else {
return super.getValueAt(row, column);
}
}
};
table = new JTable(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ExampleRemoveAddRows frame = new ExampleRemoveAddRows();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Thanks for your help.
Sam
I've revised your sscce to show the alternate approach suggested here. Note the alternate ways to get a Double constant. I've also re-factored the String constrants.
Addendum: In helpful comments, #kleopatra observes that querying the model directly will always produce the correct result, but a TableModelListener will only see changes to column 0, not column 1. The simple expedient is to make column 1 non-editable, as its value depends completely on column 0.
#Override
public boolean isCellEditable(int row, int col) {
return col == 0;
}
The first example below uses DefaultTableModel:
import javax.swing.*;
import javax.swing.table.*;
/** #see https://stackoverflow.com/a/13628183/230513 */
public class ExampleRemoveAddRows extends JFrame {
public static final String NEGATIVE = "negativ";
public static final String POSITIVE = "positiv";
private Object[] columnNames = {"Double", POSITIVE + " / " + NEGATIVE};
private Object[][] data = {
{10d, null},
{-10.0, null},
{Double.valueOf(30), null},
{Double.valueOf("-30"), null}
};
private JTable table;
private DefaultTableModel model;
public ExampleRemoveAddRows() {
model = new DefaultTableModel(data, columnNames) {
#Override
public Class getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
return col == 0;
}
#Override
public Object getValueAt(int row, int col) {
if (col == 1) {
double number = (Double) this.getValueAt(row, 0);
return (number < 0) ? NEGATIVE : POSITIVE;
} else {
return super.getValueAt(row, col);
}
}
#Override
public void setValueAt(Object aValue, int row, int col) {
super.setValueAt(aValue, row, col);
fireTableCellUpdated(row, 1); // may have changed
}
};
table = new JTable(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ExampleRemoveAddRows frame = new ExampleRemoveAddRows();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
This variation extends AbstractTableModel:
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;
/**
* #see https://stackoverflow.com/a/13628183/230513
*/
public class ExampleRemoveAddRows extends JFrame {
public static final String NEGATIVE = "negativ";
public static final String POSITIVE = "positiv";
public ExampleRemoveAddRows() {
DoubleModel model = new DoubleModel();
model.add(10.1);
model.add(-10.2);
model.add(Double.valueOf(30.1));
model.add(Double.valueOf("-30.2"));
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ExampleRemoveAddRows frame = new ExampleRemoveAddRows();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private class DoubleModel extends AbstractTableModel {
List<Double> data = new ArrayList<Double>();
public void add(Double d) {
data.add(d);
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public String getColumnName(int col) {
if (col == 0) {
return "Double";
} else {
return POSITIVE + " / " + NEGATIVE;
}
}
#Override
public Class<?> getColumnClass(int col) {
if (col == 0) {
return Double.class;
} else {
return String.class;
}
}
#Override
public boolean isCellEditable(int row, int col) {
return col == 0;
}
#Override
public Object getValueAt(int row, int col) {
if (col == 0) {
return data.get(row);
} else {
double number = (Double) this.getValueAt(row, 0);
return (number < 0) ? NEGATIVE : POSITIVE;
}
}
#Override
public void setValueAt(Object aValue, int row, int col) {
if (col == 0) {
data.set(row, (Double) aValue);
fireTableRowsUpdated(row, row);
}
}
}
}
You do indeed have access to the TableModel, if I'm not mistaken, through TableModelEvent.getSource().
Just to provide a basic example (mostly because the one sentance answer hardly seems like much of an answer):
TableModel model = (TableModel)te.getSource();
Double number = model.getValueAt(te.firstRow, 0);
model.setValueAt(((number < 0) ? "negativ":"positiv"), te.firstRow, 1);

Disable JComboBox inside JTable on click of JCheckBox

I have JTable and it has a JCheckBox and a JComoboBox in two different columns. When I select a JCheckBox that corresponds to that row, the JComboBox should be disable. Kindly help me.
Simply disable editing of the cell based on your model. In your TableModel, override/implement the isCellEditable() method to return the "value" of the checkbox.
Although the following example is not based on JComboBox, it illustrates how to disable edition of a cell based on the value of a checkbox at the beginning of the row:
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
public class TestTable {
public JFrame f;
private JTable table;
public class TestTableModel extends DefaultTableModel {
public TestTableModel() {
super(new String[] { "Editable", "DATA" }, 3);
for (int i = 0; i < 3; i++) {
setValueAt(Boolean.TRUE, i, 0);
setValueAt(Double.valueOf(i), i, 1);
}
}
#Override
public boolean isCellEditable(int row, int column) {
if (column == 1) {
return (Boolean) getValueAt(row, 0);
}
return super.isCellEditable(row, column);
}
#Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return Boolean.class;
} else if (columnIndex == 1) {
return Double.class;
}
return super.getColumnClass(columnIndex);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTable().initUI();
}
});
}
protected void initUI() {
table = new JTable(new TestTableModel());
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.add(new JScrollPane(table));
f.setVisible(true);
}
}

Categories