problem formatting fields in a JTable - differences between Integer and Double - java

Update
Confirmed as a bug on JTable can't format given Object as Number when columnClass is Double (bug ID: 7051636). Feel free to vote for it, or if if you have an alternate (better) work-around, post it as a comment to the report.
I'm building a JTable with a custom table model built extending AbstractTableModel.
My model need to support empty row to be displayed and sorted.
So I follwed this post to implement that, and now works pretty fine.
I still have a problem whith formatted field in a JTable.
Suppose I have the following model:
public class MyModel extends AbstractTableModel{
public Object[] types= {new Integer(0), ""};
public static final Object EMPTY_ROW = "";
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return this.EMPTY_ROW;
}
public Class<? extends Object> getColumnClass(int c) {
if (c > this.types.length - 1)
return null;
else
return this.types[c].getClass();
}
}
Everything works fine.
But if I have a Double instead of an Integer:
public class MyModel extends AbstractTableModel{
public Object[] types= {new Double(0.0), ""};
.......
I'll get an Illegal Argument exception:
EDIT:
new stack trace output after #Aaron Digulla suggestion
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Cannot format given Object as a Number
at java.text.DecimalFormat.format(DecimalFormat.java:487)
at java.text.Format.format(Format.java:140)
at javax.swing.JTable$DoubleRenderer.setValue(JTable.java:5352)
at javax.swing.table.DefaultTableCellRenderer.getTableCellRendererComponent(DefaultTableCellRenderer.java:237)
at javax.swing.JTable.prepareRenderer(JTable.java:5720)
at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2072)
at javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:1974)
at javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1770)
at javax.swing.plaf.ComponentUI.update(ComponentUI.java:143)
at javax.swing.JComponent.paintComponent(JComponent.java:752)
at javax.swing.JComponent.paint(JComponent.java:1029)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JViewport.paint(JViewport.java:747)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5131)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
at javax.swing.JComponent.paint(JComponent.java:1015)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
at java.awt.Container.paint(Container.java:1780)
at java.awt.Window.paint(Window.java:3375)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Why this?
getValueAt returns always the same value to fill all tables entries with it.
This is only for debug :
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return this.EMPTY_ROW;
}
For example if i change to :
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return new Integer(3);
//or return new Double(3.3);
//return new String("foobar"); doesn't work
}
all works fine even if some field of the table are String. It suggest to me that because
an Integer and a Double can be transformed into String, this won't cause problem.
Anyway I would like to understand why a generic Object like my EMPTY_ROW can be accepted as value of a declared Integer field while this don't work with Double fields.
EDIT2:
If I remove getClass method in my table model. It works. Anyway I would like to solve this without having to remove that method, even if this will force me to implement some custom render methods.
EDIT3:
here's an SSCCE. There is some errors while adding new values to the table, but hasn't nothing to do with rendering problems.
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SortOrder;
import javax.swing.RowSorter.SortKey;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
public class TableExample extends JFrame{
public static final Object EMPTY_ROW = "";
public class EmptyRowComparator<COLUMN_TYPE extends Comparable<COLUMN_TYPE>> implements Comparator<Object>{//extends RuleBasedCollator{
private TableRowSorter<? extends AbstractTableMod> sorter;
private int column;
public EmptyRowComparator(TableRowSorter<? extends AbstractTableMod> sorter, int col) throws ParseException {
// super(arg0);
this.sorter = sorter;
this.column = col;
// TODO Auto-generated constructor stub
}
private int getSortOrder() {
SortOrder order = SortOrder.ASCENDING;
// List<? extends SortKey> keys = sorter.getSortKeys();
// sorter.getSortKeys();
//
for (SortKey sortKey : sorter.getSortKeys()) {
if (sortKey.getColumn() == this.column) {
order = sortKey.getSortOrder();
break;
}
}
return order == SortOrder.ASCENDING ? 1 : -1;
}
#Override
public int hashCode() {
// TODO Auto-generated method stub
return 0;
}
#Override
public int compare(Object arg0, Object arg1) {
// TODO Auto-generated method stub
//System.out.println("Comparing Integer arg0 " + arg0 + " arg1 " + arg1);
boolean empty1 = arg0 == EMPTY_ROW;
boolean empty2 = arg1 == EMPTY_ROW;
if (empty1 && empty2) {
return 0;
}
else if (empty1) {
return 1 * getSortOrder();
}
else if (empty2) {
return -1 * getSortOrder();
}
return ((Comparable<COLUMN_TYPE>) (COLUMN_TYPE)arg0).compareTo((COLUMN_TYPE)arg1);
// return 0;
}
}
public class ConcreteTable extends AbstractTableMod{
//
private static final long serialVersionUID = 4672561280810649603L;
private String[] columnNames = {"ID",
"description"};
Class[] types = {Integer.class, String.class};
//Object[] types = {Double.class, String.class};
private int minimumDisplayedRow;
public ConcreteTable(){
//System.out.println("DEBUG ARRAY length " + data.length);
this.minimumDisplayedRow = 10;
this.datas = new ArrayList<ArrayList<Object>>();
for (int i = 0 ; i < this.minimumDisplayedRow ; i++){
this.addEmptyRow();
}
for (int i = 0 ; i < 5 ; i++){
ArrayList<Object> row = new ArrayList<Object>();
row.add(new Integer(i));
row.add(new String("prova " + i));
this.addRow(row);
}
}
public String getColumnName(int col) {
System.out.println("getColumnName " + col + " = " + columnNames[col]);
return columnNames[col];
}
#Override
protected Class[] getTypeArray() {
// TODO Auto-generated method stub
return this.types;
}
#Override
protected ArrayList<Integer> getKeysColumnIndex() {
// TODO Auto-generated method stub
ArrayList<Integer> keys = new ArrayList<Integer>();
keys.add(0);
return keys;
}
public boolean isCellEditable(int row, int col) {
System.out.println("isCellEditable row " + row + " col " + col);
if (col == 1){
System.out.println("TRUE");
return true;
}
return false;
}
/*note: generated keys must be in the same order they appear in the table*/
#Override
protected Object getGeneratedKeys(int col) {
// TODO Auto-generated method stub
if (col != 0 )
return null;
return new Integer(this.rowNumber);
}
#Override
protected int getMinimumDisplayedRow() {
// TODO Auto-generated method stub
return this.minimumDisplayedRow;
}
}
public abstract class AbstractTableMod extends AbstractTableModel {
ArrayList<ArrayList<Object>> datas ;
protected int rowNumber = 0;
protected abstract Class[] getTypeArray();
protected abstract ArrayList<Integer> getKeysColumnIndex();
protected abstract Object getGeneratedKeys(int col);
protected abstract int getMinimumDisplayedRow();
public int getRowCount(){
return this.datas.size() ;
}
#Override
public int getColumnCount() {
return this.getTypeArray().length;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex >= this.rowNumber ){
return EMPTY_ROW;
}
try{
ArrayList<Object> row = this.datas.get(rowIndex);
if (row == null)
return null;
Object obj = row.get(columnIndex);
return obj;
}catch(IndexOutOfBoundsException e){
return null;
}
}
public void setValueAt(Object value, int row, int col) {
//System.out.println("setValueAt object : " + value.getClass().getName());
Class<? extends Object> targetColClass = this.getColumnClass(col);
if (!targetColClass.isInstance(value))
return;
if (value instanceof String){
String stringVal = (String)value;
if (stringVal.compareTo("") == 0)
return;
}
if (row >= this.rowNumber){
ArrayList<Object> newRow = new ArrayList<Object>();
ArrayList<Integer> keysIndexList = this.getKeysColumnIndex();
for (int i = 0 ; i < this.getColumnCount(); i++){
if (i == col){
newRow.add(value);
}else if (keysIndexList.contains(i)){
newRow.add(this.getGeneratedKeys(i));
}else{
newRow.add(EMPTY_ROW);
}
}
this.addRow(newRow);
}else{
this.datas.get(row).set(col, value);
}
this.fireTableCellUpdated(row, col);
}
public Class<? extends Object> getColumnClass(int c) {
System.out.println("AbstractTable: getColumnClass");
if (c > this.getTypeArray().length - 1)
return null;
else
return this.getTypeArray()[c];
}
public void addEmptyRow(){
ArrayList<Object> emptyRow = new ArrayList<Object>();
for (int i = 0 ; i < this.getTypeArray().length; i++){
emptyRow.add(EMPTY_ROW);
}
this.datas.add(emptyRow);
}
public void addRow(ArrayList<Object> row){
Object[] types = this.getTypeArray();
if (types.length != row.size())
return;
for (int i = 0 ; i < row.size() ; i++){
Class<? extends Object> targetColClass = this.getColumnClass(i);
Object rowItem = row.get(i);
}
this.datas.add(this.rowNumber, row);
this.rowNumber++;
if (this.rowNumber < this.getMinimumDisplayedRow())
this.datas.remove(this.datas.size() -1 );
this.fireTableRowsInserted(this.rowNumber , this.rowNumber );
}
}
public TableExample(){
super("JTable example");
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
ConcreteTable model = new ConcreteTable();
JTable tab = new JTable(model);
TableRowSorter<ConcreteTable> sorter = new TableRowSorter<ConcreteTable>(model);
try {
sorter.setComparator(0, new EmptyRowComparator<Integer>(sorter,0));
sorter.setComparator(1, new EmptyRowComparator<String>(sorter,1));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tab.setRowSorter(sorter);
JScrollPane table = new JScrollPane(tab);
this.getContentPane().add(table);
this.setSize(600, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new TableExample();
}
}
If you try to change
Class[] types = {Integer.class, String.class};
with :
Class[] types = {Double.class, String.class};
you can see the problem.

how did Walter Laan says in his thread
Never give up! Never surrender!
EDIT: I can't resist, but due to my poor English I dare not to commenting why, where and how is that possible, nor works correctly, for confirmations I added Rob's two (little bit) modified class for TableColumnRendering ...,
import java.awt.EventQueue;
import java.math.RoundingMode;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.RowSorter.SortKey;
import javax.swing.SwingConstants;
import javax.swing.table.*;
public class TableExample extends JFrame {
public static final Object EMPTY_ROW = "";
private static final long serialVersionUID = 1L;
private JTable tab;
private Calendar cal;
private Date dateWithOutTime = new java.util.Date();
private SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");//standard continental EU date format
public class EmptyRowComparator<COLUMN_TYPE extends Comparable<COLUMN_TYPE>> implements Comparator<Object> {//extends RuleBasedCollator{
private TableRowSorter<? extends AbstractTableMod> sorter;
private int column;
public EmptyRowComparator(TableRowSorter<? extends AbstractTableMod> sorter, int col) throws ParseException {
this.sorter = sorter;
this.column = col;
}
private int getSortOrder() {
SortOrder order = SortOrder.ASCENDING;
for (SortKey sortKey : sorter.getSortKeys()) {
if (sortKey.getColumn() == this.column) {
order = sortKey.getSortOrder();
break;
}
}
return order == SortOrder.ASCENDING ? 1 : -1;
}
#Override
public int hashCode() {
return 0;
}
#Override
#SuppressWarnings("unchecked")
public int compare(Object arg0, Object arg1) {
boolean empty1 = arg0 == EMPTY_ROW;
boolean empty2 = arg1 == EMPTY_ROW;
if (empty1 && empty2) {
return 0;
} else if (empty1) {
return 1 * getSortOrder();
} else if (empty2) {
return -1 * getSortOrder();
}
return ((Comparable<COLUMN_TYPE>) (COLUMN_TYPE) arg0).compareTo((COLUMN_TYPE) arg1);
}
}
public class ConcreteTable extends AbstractTableMod {
private static final long serialVersionUID = 4672561280810649603L;
private String[] columnNames = {"Integer", "String", "Integer", "Double", "Boolean", "Double", "String", "Boolean", "Date"};
private Class<?>[] types = {Integer.class, String.class, String.class, String.class, String.class,
String.class, String.class, String.class, String.class};
private int minimumDisplayedRow;
public ConcreteTable() {
this.minimumDisplayedRow = 10;
this.datas = new ArrayList<ArrayList<Object>>();
for (int i = 0; i < this.minimumDisplayedRow; i++) {
this.addEmptyRow();
}
Random rnd = new Random();
for (int i = 0; i < 7; i++) {
ArrayList<Object> row = new ArrayList<Object>();
row.add(i);
row.add(((rnd.nextInt(25)) + "prova"));
row.add(rnd.nextInt(25));
row.add(rnd.nextInt(25) + 3.14);
row.add((i % 2 == 0) ? true : false);
row.add(rnd.nextInt(25) + 3.14);
row.add(((rnd.nextInt(25)) + "prova"));
row.add((i % 2 == 0) ? false : true);
cal = Calendar.getInstance();
cal.add(Calendar.DATE, -rnd.nextInt(25));
dateWithOutTime = cal.getTime();
String nullTimeForDateString = sdf.format(dateWithOutTime);
try {
dateWithOutTime = sdf.parse(nullTimeForDateString);
} catch (ParseException ex) {
}
row.add(dateWithOutTime);
this.addRow(row);
}
}
#Override
public String getColumnName(int col) {
System.out.println("getColumnName " + col + " = " + columnNames[col]);
return columnNames[col];
}
#Override
protected Class<?>[] getTypeArray() {
return this.types;
}
#Override
protected ArrayList<Integer> getKeysColumnIndex() {
ArrayList<Integer> keys = new ArrayList<Integer>();
keys.add(0);
return keys;
}
#Override
public boolean isCellEditable(int row, int col) {
System.out.println("isCellEditable row " + row + " col " + col);
if (col == 1) {
System.out.println("TRUE");
return true;
}
return false;
}
#Override
protected Object getGeneratedKeys(int col) {
if (col != 0) {
return null;
}
return new Integer(this.rowNumber);
}
#Override
protected int getMinimumDisplayedRow() {
return this.minimumDisplayedRow;
}
}
public abstract class AbstractTableMod extends AbstractTableModel {
private static final long serialVersionUID = 1L;
protected ArrayList<ArrayList<Object>> datas;
protected int rowNumber = 0;
protected abstract Class<?>[] getTypeArray();
protected abstract ArrayList<Integer> getKeysColumnIndex();
protected abstract Object getGeneratedKeys(int col);
protected abstract int getMinimumDisplayedRow();
public int getRowCount() {
return this.datas.size();
}
#Override
public int getColumnCount() {
return this.getTypeArray().length;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex >= this.rowNumber) {
return EMPTY_ROW;
}
try {
ArrayList<Object> row = this.datas.get(rowIndex);
if (row == null) {
return null;
}
Object obj = row.get(columnIndex);
return obj;
} catch (IndexOutOfBoundsException e) {
return null;
}
}
#Override
public void setValueAt(Object value, int row, int col) {
Class<? extends Object> targetColClass = this.getColumnClass(col);
if (!targetColClass.isInstance(value)) {
return;
}
if (value instanceof String) {
String stringVal = (String) value;
if (stringVal.compareTo("") == 0) {
return;
}
}
if (row >= this.rowNumber) {
ArrayList<Object> newRow = new ArrayList<Object>();
ArrayList<Integer> keysIndexList = this.getKeysColumnIndex();
for (int i = 0; i < this.getColumnCount(); i++) {
if (i == col) {
newRow.add(value);
} else if (keysIndexList.contains(i)) {
newRow.add(this.getGeneratedKeys(i));
} else {
newRow.add(EMPTY_ROW);
}
}
this.addRow(newRow);
} else {
this.datas.get(row).set(col, value);
}
this.fireTableCellUpdated(row, col);
}
#Override
#SuppressWarnings("unchecked")
public Class<? extends Object> getColumnClass(int c) {
if (c > this.getTypeArray().length - 1) {
return null;
} else {
return this.getTypeArray()[c];
}
}
public void addEmptyRow() {
ArrayList<Object> emptyRow = new ArrayList<Object>();
for (int i = 0; i < this.getTypeArray().length; i++) {
emptyRow.add(EMPTY_ROW);
}
this.datas.add(emptyRow);
}
public void addRow(ArrayList<Object> row) {
Object[] types = this.getTypeArray();
if (types.length != row.size()) {
return;
}
for (int i = 0; i < row.size(); i++) {
Class<? extends Object> targetColClass = this.getColumnClass(i);
Object rowItem = row.get(i);
}
this.datas.add(this.rowNumber, row);
this.rowNumber++;
if (this.rowNumber < this.getMinimumDisplayedRow()) {
this.datas.remove(this.datas.size() - 1);
}
this.fireTableRowsInserted(this.rowNumber, this.rowNumber);
}
}
public TableExample() {
super("JTable example");
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
ConcreteTable model = new ConcreteTable();
tab = new JTable(model);
TableRowSorter<ConcreteTable> sorter = new TableRowSorter<ConcreteTable>(model);
try {
sorter.setComparator(0, new EmptyRowComparator<Integer>(sorter, 0));
sorter.setComparator(1, new EmptyRowComparator<String>(sorter, 1));
sorter.setComparator(2, new EmptyRowComparator<String>(sorter, 2));
sorter.setComparator(3, new EmptyRowComparator<String>(sorter, 3));
sorter.setComparator(4, new EmptyRowComparator<String>(sorter, 4));
sorter.setComparator(5, new EmptyRowComparator<String>(sorter, 5));
sorter.setComparator(6, new EmptyRowComparator<String>(sorter, 6));
sorter.setComparator(7, new EmptyRowComparator<String>(sorter, 7));
sorter.setComparator(8, new EmptyRowComparator<String>(sorter, 8));
} catch (ParseException e) {
e.printStackTrace();
}
tab.setRowSorter(sorter);
JScrollPane table = new JScrollPane(tab);
this.getContentPane().add(table);
this.setSize(800, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setRenderers();
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
setVisible(true);
}
});
//TableExample tableExample = new TableExample();
}
public void setRenderers() {
TableColumnModel m = tab.getColumnModel();
//"Integer", "String", "Interger", "Double", "Boolean", "Double", "String", "Boolean", "Date"
m.getColumn(0).setCellRenderer(NumberRenderer.getIntegerRenderer());
m.getColumn(2).setCellRenderer(NumberRenderer.getIntegerRenderer());
m.getColumn(3).setCellRenderer(NumberRenderer.getDoubleRenderer5());
m.getColumn(5).setCellRenderer(NumberRenderer.getDoubleRenderer3());
m.getColumn(8).setCellRenderer(FormatRenderer.getDateRenderer());
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TableExample tableExample = new TableExample();
}
});
TableExample tableExample = new TableExample();
}
}
class FormatRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
private Format formatter;
private static DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");//standard continental EU date format
FormatRenderer(Format formatter) {
this.formatter = formatter;
}
#Override
public void setValue(Object value) {
try {
if ((value != null)) {
if ((value instanceof Number) || (value instanceof Date)) {
setHorizontalAlignment(SwingConstants.RIGHT);
value = formatter.format(value);
}
}
} catch (IllegalArgumentException e) {
}
super.setValue(value);
}
public static FormatRenderer getDateRenderer() {
return new FormatRenderer(dateFormat);
}
}
class NumberRenderer extends FormatRenderer {
private static final long serialVersionUID = 1L;
private static Number numberValue;
private static NumberFormat nf;
NumberRenderer(NumberFormat formatter) {
super(formatter);
setHorizontalAlignment(SwingConstants.RIGHT);
}
public static NumberRenderer getIntegerRenderer() {
return new NumberRenderer(NumberFormat.getIntegerInstance());
}
public static NumberRenderer getDoubleRenderer3() {
nf = NumberFormat.getNumberInstance();
nf.setMinimumFractionDigits(3);
nf.setMaximumFractionDigits(3);
nf.setRoundingMode(RoundingMode.HALF_UP);
return new NumberRenderer(nf);
}
public static NumberRenderer getDoubleRenderer5() {
nf = NumberFormat.getNumberInstance();
nf.setMinimumFractionDigits(5);
nf.setMaximumFractionDigits(5);
nf.setRoundingMode(RoundingMode.HALF_UP);
return new NumberRenderer(nf);
}
}

reall problem is noting to do with previous debate, there is plain vanilla (compare my missing English skills), are you think that that's really Bug, or if I showed ViceVersaView, maybe then somone can be able to comment that chains for JTable + TableModel + Comparator (TableRowSorter for JTable API) in English language, eeeeeerghhh really my bad in this case
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
import javax.swing.table.*;
public class ViceVersaBugFromTableModelAndComparator {
private String[] columnNames = {"String", "Integer", "Boolean", "Double", "Date"};
private Object[][] data = {
{"aaa", 12, true, .15, new Date()},
{"bbb", 5, false, 100.01, new Date()},
{"CCC", 92, true, 15.2, new Date()},
{"DDD", 0, false, 10.80, new Date()},
{"abc", 5, true, 4.11, new Date()},
{"bae", 31, false, 100.01, new Date()},
{"CAX", 27, true, 2.3, new Date()},
{"AXD", 4, false, 50.00, new Date()},
{"abc", 3, true, 1.5, new Date()},
{"bae", 5, false, 1000, new Date()}, //java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
//{"bae", 5, false, 1000.0, new Date()}, //un-comment for correctness
{"CAX", 2, true, 21.7, new Date()},
{"AXD", 2, false, 5.30, new Date()}
};
private TableModel model = new DefaultTableModel(data, columnNames) {
private static final long serialVersionUID = 1L;
#Override
public Class<?> getColumnClass(int column) {
return String.class;// again java.lang.ClassCastException
//return getValueAt(0, column).getClass(); //un-comment for correctness
}
};
private JTable table = new JTable(model);
private JTableHeader header;
static class TestTableRowSorter extends TableRowSorter<TableModel> {
public TestTableRowSorter(TableModel m) {
super(m);
}
#Override
public void toggleSortOrder(int column) {
}
public void wrapToggleSortOrder(int column) {
super.toggleSortOrder(column);
}
}
private Timer timer = new Timer(400, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("single");
JTable table = header.getTable();
RowSorter sorter;
if (pt != null && table != null && (sorter = table.getRowSorter()) != null) {
int columnIndex = header.columnAtPoint(pt);
if (columnIndex != -1) {
columnIndex = table.convertColumnIndexToModel(columnIndex);
((TestTableRowSorter) sorter).wrapToggleSortOrder(columnIndex);
}
}
}
});
private Point pt;
public JComponent makeUI() {
timer.setRepeats(false);
table.setRowSorter(new TestTableRowSorter(model));
header = table.getTableHeader();
header.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(final MouseEvent e) {
if (timer.isRunning() && !e.isConsumed() && e.getClickCount() > 1) {
System.out.println("double");
pt = null;
timer.stop();
} else {
pt = e.getPoint();
timer.restart();
}
}
});
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ViceVersaBugFromTableModelAndComparator().makeUI());
f.setSize(820, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}

The problem is not the double but something else. As you can see in the stack trace, the table has special support for double values (javax.swing.JTable$DoubleRenderer).
The problem us that the value passed to setValue() is not a Double but something else.

The thing is: when you format the Double value, it becomes a string, you can't return it back cause the column only allows Double.class. So if you change the Double.class to String.class, it will work.

Related

setAutoCreateRowSorter doesn't sort table column correctly after update

While developing a small task manager, I have noticed that columns aren't sorted correctly. To discard problems with my program, I have created a minimal version but it still fails to order the unique column right.
import java.awt.BorderLayout;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
public class TableSortTest extends JFrame
{
private final JTable table;
private final ATableModel model;
public TableSortTest ()
{
setDefaultCloseOperation (EXIT_ON_CLOSE);
setSize (1366, 768);
setLocationRelativeTo (null);
model = new ATableModel ();
table = new JTable ();
table.setFillsViewportHeight (true);
table.setAutoCreateRowSorter (true);
table.setModel (model);
add (new JScrollPane (table), BorderLayout.CENTER);
setVisible (true);
Worker worker = new Worker ();
worker.execute ();
}
private class Pair
{
int index;
int value;
}
private class Worker extends SwingWorker <Void, Pair>
{
#Override
protected Void doInBackground ()
{
while (!isCancelled ())
{
Random r = new Random ();
for (int i = 0; i < 100; i++)
{
int indice = getIndexInRange (0, 99);
Pair p = new Pair ();
p.index = indice;
p.value = Math.abs (r.nextInt ());
publish (p);
}
try
{
Thread.sleep (1000);
}
catch (InterruptedException ie)
{
ie.printStackTrace ();
}
}
return null;
}
#Override
public void process (List <Pair> items)
{
for (Pair p : items)
{
model.setValueAt (p.value, p.index, 0);
}
}
}
public static int getIndexInRange (int min, int max)
{
return (min + (int) (Math.random () * ((max - min) + 1)));
}
private class ATableModel extends AbstractTableModel
{
private final Integer [] data;
public ATableModel ()
{
data = new Integer [100];
Random r = new Random ();
for (int i = 0; i < 100; i++)
{
data [i] = Math.abs (r.nextInt ());
}
}
#Override
public int getColumnCount ()
{
return 1;
}
#Override
public int getRowCount ()
{
return data.length;
}
#Override
public Object getValueAt (int rowIndex, int columnIndex)
{
return data [rowIndex];
}
#Override
public void setValueAt (Object value, int rowIndex, int columnIndex)
{
data [rowIndex] = (Integer) value;
fireTableRowUpdated (rowIndex, columnIndex);
}
#Override
public Class getColumnClass (int columnIndex)
{
return Integer.class;
}
#Override
public String getColumnName (int col)
{
return "Column";
}
}
public static final void main (String [] args)
{
SwingUtilities.invokeLater (() ->
{
try
{
new TableSortTest ();
}
catch (Exception e)
{
e.printStackTrace ();
}
});
}
}
I have tried with a ScheduledExecutorService + Runnable and a Timer + TimerTask just to test if it was a threading problem, but the behavior is the same. I have also read the Java Tutorial page about the subject. Given that my table only uses standard types I think that a simple table.setAutoCreateRowSorter (true); should do the job, shouldn't it?
Shouldn't the table be sorted after every modification/addition/removal even is fired?
Thanks for your quick answer trashgod. You're right, I meant fireTableRowsUpdated () but I made a mistake when I wrote the code, sorry. The point is that fireTableRowsUpdated (rowIndex, rowIndex) and fireTableCellUpdated (rowIndex, columnIndex) both fail to sort the column correctly. In the real program most of the table rows do change from one iteration to the next so calling fireTableDataChanged () makes perfect sense. But I didn't want to use it because if I select one or more rows to send a signal to the processes or whatever the selection is lost on every update. I have explored this way and found two forms of preserving the selection but it's a bit annoying and one of them breaks the selection with the keyboard. I show the necessary additions to the original code next.
The first form saves the selection before modifying the model and restores it after every update:
...
private class Worker extends SwingWorker <Void, Pair>
{
private int [] selectedRows;
#Override
protected Void doInBackground ()
{
while (!isCancelled ())
{
// Save the selection before modifying the model
int x = table.getSelectedRowCount ();
if (x > 0)
{
selectedRows = new int [x];
int [] tableSelection = table.getSelectedRows ();
for (int i = 0; i < x; i++)
{
selectedRows [i] = table.convertRowIndexToModel (tableSelection [i]);
}
}
Random r = new Random ();
for (int i = 0; i < table.getRowCount (); i++)
{
int indice = getIndexInRange (0, table.getRowCount () - 1);
Pair p = new Pair ();
p.index = indice;
p.value = Math.abs (r.nextInt ());
publish (p);
}
// If I put the code to restore the selection here, it doesn't work...
try
{
Thread.sleep (1000);
}
catch (InterruptedException ie)
{
ie.printStackTrace ();
}
}
return null;
}
#Override
public void process (List <Pair> items)
{
for (Pair p : items)
{
model.setValueAt (p.value, p.index, 1);
}
// Restore the selection on every update
if (selectedRows != null && selectedRows.length > 0)
{
for (int i = 0; i < selectedRows.length; i++)
{
table.addRowSelectionInterval (table.convertRowIndexToView (selectedRows [i]), table.convertRowIndexToView (selectedRows [i]));
}
}
}
}
...
The second form uses a ListSelectionListener, a KeyListener, and a flag. Selection with the keyboard doesn't work. To be honest, I don't know how did I come to get this solution. It probably was by chance:
public class TableSortTestSolucionConSelectionListener extends JFrame implements KeyListener
{
...
private boolean ctrlOrShiftDown = false;
private int [] selectedRows;
#Override
public void keyPressed (KeyEvent e)
{
ctrlOrShiftDown = e.isControlDown () || e.isShiftDown ();
}
#Override
public void keyReleased (KeyEvent e)
{
ctrlOrShiftDown = e.isControlDown () || e.isShiftDown ();
}
#Override
public void keyTyped (KeyEvent e)
{
ctrlOrShiftDown = e.isControlDown () || e.isShiftDown ();
}
public TableSortTestSolucionConSelectionListener ()
{
...
ListSelectionListener lsl = new ListSelectionListener ()
{
#Override
public void valueChanged (ListSelectionEvent e)
{
if (!e.getValueIsAdjusting ())
{
if (!ctrlOrShiftDown)
{
int x = table.getSelectedRowCount ();
if (x > 0)
{
selectedRows = new int [x];
int [] tableSelection = table.getSelectedRows ();
for (int i = 0; i < x; i++)
{
selectedRows [i] = table.convertRowIndexToModel (tableSelection [i]);
}
}
}
// Disable the listener to avoid infinite recursion
table.getSelectionModel ().removeListSelectionListener (this);
if (selectedRows != null && selectedRows.length > 0)
{
for (int i = 0; i < selectedRows.length; i++)
{
table.addRowSelectionInterval (table.convertRowIndexToView (selectedRows [i]), table.convertRowIndexToView (selectedRows [i]));
}
}
table.getSelectionModel ().addListSelectionListener (this);
}
}
};
table.getSelectionModel ().addListSelectionListener (lsl);
...
}
Fortunately today I have found a simple way to get the column sorted correctly and keep the current selection. You only have to add the following to your code:
TableRowSorter trs = (TableRowSorter) table.getRowSorter ();
trs.setSortsOnUpdates (true);
With this both fireTableCellUpdated () and fireTableRowsUpdated () work as I expected. To my understanding, setAutoCreateRowSorter () is only used to sort the rows when you click on the table header.
Greetings.
Using setSortsOnUpdates(), suggested here by #trcs, is the best general solution, but you may be able to optimize updates by the choice of TableModelEvent available to subclasses of AbstractTableModel.
The critical issue is the implementation of setValueAt(). If you meant fireTableRowsUpdated(), instead of fireTableRowUpdated(), note that the parameters represent a range of rows, not a row & column. In this case, because "all cell values in the table's rows may have changed," the revised example below invokes fireTableDataChanged(). I've also changed the model to manage a List<Integer> and normalized the size, N.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
/** #see https://stackoverflow.com/a/36522182/230513 */
public class TableSortTest extends JFrame {
private final JTable table;
private final ATableModel model;
public TableSortTest() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
model = new ATableModel();
table = new JTable(model){
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 500);
}
};
table.setFillsViewportHeight(true);
table.setAutoCreateRowSorter(true);
add(new JScrollPane(table), BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
Worker worker = new Worker();
worker.execute();
}
private class Pair {
int index;
int value;
}
private class Worker extends SwingWorker<Void, Pair> {
private static final int N = 100;
private final Random r = new Random();
#Override
protected Void doInBackground() {
while (!isCancelled()) {
for (int i = 0; i < N; i++) {
int index = r.nextInt(N);
Pair p = new Pair();
p.index = index;
p.value = Math.abs(r.nextInt());
publish(p);
}
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
return null;
}
#Override
public void process(List<Pair> items) {
for (Pair p : items) {
model.setValueAt(p.value, p.index, 0);
}
}
}
private class ATableModel extends AbstractTableModel {
private static final int N = 100;
private final List<Integer> data = new ArrayList<>(N);
public ATableModel() {
final Random r = new Random();
for (int i = 0; i < N; i++) {
data.add(Math.abs(r.nextInt()));
}
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex);
}
#Override
public void setValueAt(Object value, int rowIndex, int columnIndex) {
data.set(rowIndex, (Integer) value);
fireTableDataChanged();
}
#Override
public Class getColumnClass(int columnIndex) {
return Integer.class;
}
#Override
public String getColumnName(int col) {
return "Column";
}
}
public static final void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new TableSortTest();
});
}
}
Recognizing that this is just an example, the variation below optimizes updates by publishing a List<Integer>, which is passed en bloc to the TableModel via process().
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
/**
* # see https://stackoverflow.com/a/36522182/230513
*/
public class TableSortTest extends JFrame {
private final JTable table;
private final ATableModel model;
public TableSortTest() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
model = new ATableModel();
table = new JTable(model) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 500);
}
};
table.setFillsViewportHeight(true);
table.setAutoCreateRowSorter(true);
add(new JScrollPane(table), BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
Worker worker = new Worker();
worker.execute();
}
private class Worker extends SwingWorker<List<Integer>, List<Integer>> {
private static final int N = 100;
private final Random r = new Random();
private final List<Integer> data = new ArrayList<>(N);
#Override
protected List<Integer> doInBackground() throws Exception {
while (!isCancelled()) {
data.clear();
for (int i = 0; i < N; i++) {
data.add(Math.abs(r.nextInt()));
}
publish(data);
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
ie.printStackTrace(System.err);
}
}
return data;
}
#Override
protected void process(List<List<Integer>> chunks) {
for (List<Integer> chunk : chunks) {
model.update(chunk);
}
}
}
private class ATableModel extends AbstractTableModel {
private List<Integer> data = new ArrayList<>();
public void update(List<Integer> data) {
this.data = data;
fireTableDataChanged();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex);
}
#Override
public Class getColumnClass(int columnIndex) {
return Integer.class;
}
#Override
public String getColumnName(int col) {
return "Column";
}
}
public static final void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new TableSortTest();
});
}
}

How to update jtable model correctly?

I have been thinking a lot to solve this mistake. But unfortunately I came to final cnclusion that I need help of professionals.
Please, copy,paste this code to see issue:
public class DateFormatDemo extends JFrame
{
private JTable dataSearchResultTable;
public DateFormatDemo()
{
JButton updateTable = new JButton("Update table");
updateTable.setMaximumSize(updateTable.getPreferredSize());
updateTable.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
updateMyTableModel();
}
});
JPanel panel = new JPanel(new GridLayout(2, 1, 5, 10));
panel.setPreferredSize(new Dimension(500, 300));
panel.add(new JScrollPane(initDataSearchResultTable()));
panel.add(updateTable);
super.getContentPane().add(panel);
super.pack();
super.setDefaultCloseOperation(EXIT_ON_CLOSE);
super.setVisible(true);
}
private JTable initDataSearchResultTable()
{
dataSearchResultTable = new JTable();
// dataSearchResultTable.setAutoCreateColumnsFromModel(false);
dataSearchResultTable.setSelectionBackground(new Color(0xaaaaff));
dataSearchResultTable.setFillsViewportHeight(true);
dataSearchResultTable.setRowSelectionAllowed(true);
dataSearchResultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
dataSearchResultTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
return dataSearchResultTable;
}
void updateMyTableModel()
{
TableModel tableModel = dataSearchResultTable.getModel();
TableColumnModel columnModel = dataSearchResultTable.getColumnModel();
if (tableModel instanceof MyTableModel) {
((MyTableModel) tableModel).updateModel();
this.initColumnWidths(tableModel, columnModel);
} else {
tableModel = new MyTableModel();
dataSearchResultTable.setModel(tableModel);
this.makeColumnsNotResizable(columnModel);
this.initColumnWidths(tableModel, columnModel);
}
}
private void makeColumnsNotResizable(TableColumnModel columnModel)
{
for (int i = 0; i < columnModel.getColumnCount(); i++) {
if (i == 0 || i == 1) {
columnModel.getColumn(i).setResizable(false);
}
}
}
private void initColumnWidths(TableModel tableModel, TableColumnModel columnModel)
{
TableColumn column = null;
Component comp = null;
int cellWidth = 0;
int headerWidth = 0;
TableCellRenderer headerRenderer = dataSearchResultTable.getTableHeader().getDefaultRenderer();
for (int i = 0; i < columnModel.getColumnCount(); i++) {
column = columnModel.getColumn(i);
comp = headerRenderer.getTableCellRendererComponent(null, column.getHeaderValue(), false, false, -1, 0);
headerWidth = comp.getPreferredSize().width;
Class<?> columnClass = tableModel.getColumnClass(i);
for (int j = 0; j < tableModel.getRowCount(); j++) {
comp = dataSearchResultTable.getDefaultRenderer(columnClass).getTableCellRendererComponent(
dataSearchResultTable, tableModel.getValueAt(j, i), false, false, j, i);
int width = comp.getPreferredSize().width;
// we cache width of first row. And compare widths of next
// rows with width of first.
// If some row has greater width it becomes width of whole
// row(unless header has greater width)
if (cellWidth < width || j == 0) {
cellWidth = width;
}
}
System.out
.println("columnClass=" + columnClass + ",headerWidth=" + headerWidth + ",cellWidth=" + cellWidth);
if (headerWidth > cellWidth) {
TableCellRenderer centeredRenderer = dataSearchResultTable.getDefaultRenderer(columnClass);
if (centeredRenderer instanceof DefaultTableCellRenderer) {
((DefaultTableCellRenderer) centeredRenderer).setHorizontalAlignment(SwingConstants.CENTER);
column.setCellRenderer(centeredRenderer);
column.setPreferredWidth(headerWidth);
}
} else {
column.setPreferredWidth(cellWidth + 5);
}
}
}
class MyTableModel extends AbstractTableModel
{
private String[] columnNames = { "First Name", "Last Name", "Timestamp", "Number", "Vegetarian" };
private Object[][] data = new Object[5][];
void updateModel()
{
data = new Object[][] {
{ "Vova", "KipokKipokKipokKipok", "2013-04-12 11:20:41", new Integer(5), new Boolean(true) },
{ "Olia", "Duo", "2010-01-11 11:11:41", new Integer(3), new Boolean(false) },
{ "Oksana", "Stack", "2012-04-12 11:20:41", new Integer(2), new Boolean(false) },
{ "Petro", "White", "2010-04-12 11:20:21", new Integer(20), new Boolean(true) },
{ "Ivan", "Brown", "2011-04-11 11:20:41", new Integer(10), new Boolean(true) } };
fireTableDataChanged();
}
public int getColumnCount()
{
return columnNames.length;
}
public int getRowCount()
{
return data.length;
}
public String getColumnName(int col)
{
return columnNames[col];
}
public Object getValueAt(int row, int col)
{
if (data.length > 0 && data[0] != null) {
return data[row][col];
}
return null;
}
/*
* JTable uses this method to determine the default renderer/ editor for
* each cell. If we didn't implement this method, then the last column
* would contain text ("true"/"false"), rather than a check box.
*/
public Class getColumnClass(int c)
{
Object valueAt = getValueAt(0, c);
return valueAt == null ? Object.class : valueAt.getClass();
}
/*
* Don't need to implement this method unless your table's editable.
*/
public boolean isCellEditable(int row, int col)
{
// Note that the data/cell address is constant,
// no matter where the cell appears onscreen.
if (col < 2) {
return false;
} else {
return true;
}
}
/*
* Don't need to implement this method unless your table's data can
* change.
*/
public void setValueAt(Object value, int row, int col)
{
if (data.length > 0 && data[0] != null) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
}
public static void main(String[] args) throws ParseException
{
new DateFormatDemo();
}
}
Now please click twice on that big button called 'Update Table'. As you see column that should display checkbox as it holds boolean values do not do that but instead displays String true or false.
This code emulates my real workflow.
So, how to update tablemodel to have boolean columns with checkbozes.
Thanks!
take this as simple start point
import java.awt.*;
import java.util.Random;
import java.util.Vector;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
public class Forum implements ListSelectionListener {
private JFrame frame = new JFrame("Frame");
private JPanel fatherCenter = new JPanel();
private JScrollPane tableScroll = new JScrollPane();
private MyTableModel tableModel;
private JTable dialogTable;
private ListSelectionModel lsDialog;
private Color clr;
private Color clr1;
private void addData() {
Runnable doRun1 = new Runnable() {
#Override
public void run() {
tableModel.resetTable();
Vector<String> tbl = new Vector<String>();
Vector<Object> tbl1 = new Vector<Object>();
Random rnd = new Random();
tbl.add("Integer");
tbl.add("Double");
tbl.add("Boolean");
tbl.add("Boolean");
tbl.add("String");
tableModel.setColumnNames(tbl);
for (int row = 0; row < 30; row++) {
tbl1 = null;
tbl1 = new Vector<Object>();
tbl1.addElement(row + 1);
tbl1.addElement(rnd.nextInt(25) + 3.14);
tbl1.addElement((row % 3 == 0) ? false : true);
tbl1.addElement((row % 5 == 0) ? false : true);
if (row % 7 == 0) {
tbl1.add(("Canc"));
} else if (row % 6 == 0) {
tbl1.add(("Del"));
} else {
tbl1.add(("New"));
}
tableModel.addRow(tbl1);
}
addTableListener();
}
};
SwingUtilities.invokeLater(doRun1);
}
private void addTableListener() {
tableModel.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent tme) {
if (tme.getType() == TableModelEvent.UPDATE) {
System.out.println("");
System.out.println("Cell " + tme.getFirstRow() + ", "
+ tme.getColumn() + " changed. The new value: "
+ tableModel.getValueAt(tme.getFirstRow(),
tme.getColumn()));
}
}
});
}
#Override
public void valueChanged(ListSelectionEvent le) {
int row = dialogTable.getSelectedRow();
int col = dialogTable.getSelectedColumn();
String str = "Selected Row(s): ";
int[] rows = dialogTable.getSelectedRows();
for (int i = 0; i < rows.length; i++) {
str += rows[i] + " ";
}
str += "Selected Column(s): ";
int[] cols = dialogTable.getSelectedColumns();
for (int i = 0; i < cols.length; i++) {
str += cols[i] + " ";
}
str += "Selected Cell: " + dialogTable.getSelectedRow() + ", " + dialogTable.getSelectedColumn();
System.out.println(str);
Object value = dialogTable.getValueAt(row, col);
System.out.println(String.valueOf(value));
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Forum osFrame = new Forum();
}
});
}
public Forum() {
tableModel = new MyTableModel();
dialogTable = new JTable(tableModel) {
private static final long serialVersionUID = 1L;
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component comp = super.prepareRenderer(renderer, row, column);
JComponent jc = (JComponent) comp;//for Custom JComponent
if (!isRowSelected(row)) {
int modelRow = convertRowIndexToModel(row);
boolean type = (Boolean) getModel().getValueAt(modelRow, 2);
boolean type1 = (Boolean) getModel().getValueAt(modelRow, 3);
comp.setForeground(Color.black);
if ((type) && (!type1)) {
comp.setBackground(clr1);
} else if ((!type) && (type1)) {
comp.setBackground(Color.orange);
} else if ((!type) || (!type1)) {
comp.setBackground(Color.red);
} else {
comp.setBackground(row % 2 == 0 ? getBackground() : getBackground().darker());
}
dialogTable.convertRowIndexToView(0);
} else {
comp.setForeground(Color.blue);
}
if (!isCellEditable(row, column)) {
comp.setForeground(Color.red);
comp.setBackground(Color.magenta);
}
return comp;
}
};
tableScroll = new JScrollPane(dialogTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
tableScroll.setBorder(null);
dialogTable.getTableHeader().setReorderingAllowed(false);
dialogTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
lsDialog = dialogTable.getSelectionModel();
dialogTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
dialogTable.setRowHeight(20);
dialogTable.setRowMargin(2);
dialogTable.setPreferredScrollableViewportSize(dialogTable.getPreferredSize());
ListSelectionModel rowSelMod = dialogTable.getSelectionModel();
//ListSelectionModel colSelMod = dialogTable.getColumnModel().getSelectionModel();
rowSelMod.addListSelectionListener(this);
//colSelMod.addListSelectionListener(this);
fatherCenter = new JPanel();
fatherCenter.setLayout(new BorderLayout(10, 10));
fatherCenter.add(tableScroll, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout(10, 10));
frame.add(fatherCenter);
frame.setPreferredSize(new Dimension(400, 660));
frame.pack();
frame.setLocation(150, 150);
frame.setVisible(true);
addData();
}
private class MyTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private Vector<Vector<Object>> _data;
private Vector<String> _colNames;
private boolean[] _columnsVisible = {true, true, true, true, true};
public MyTableModel() {
_colNames = new Vector<String>();
_data = new Vector<Vector<Object>>();
}
public MyTableModel(Vector<String> colnames) {
_colNames = colnames;
_data = new Vector<Vector<Object>>();
}
public void resetTable() {
_colNames.removeAllElements();
_data.removeAllElements();
}
public void setColumnNames(Vector<String> colNames) {
_colNames = colNames;
fireTableStructureChanged();
}
public void addRow(Vector<Object> data) {
_data.add(data);
fireTableRowsInserted(_data.size() - 1, _data.size() - 1);
}
public void removeRowAt(int row) {
_data.removeElementAt(row);
fireTableRowsDeleted(row - 1, _data.size() - 1);
}
#Override
public int getColumnCount() {
return _colNames.size();
}
#Override
public Class<?> getColumnClass(int colNum) {
switch (colNum) {
case 0:
return Integer.class;
case 1:
return Double.class;
case 2:
return Boolean.class;
case 3:
return Boolean.class;
default:
return String.class;
}
}
#Override
public boolean isCellEditable(int row, int colNum) {
switch (colNum) {
case 2:
return false;
default:
return true;
}
}
#Override
public String getColumnName(int colNum) {
return _colNames.get(colNum);
}
#Override
public int getRowCount() {
return _data.size();
}
#Override
public Object getValueAt(int row, int col) {
Vector<Object> value = _data.get(row);
return value.get(col);
}
#Override
public void setValueAt(Object newVal, int row, int col) {
Vector<Object> aRow = _data.elementAt(row);
aRow.remove(col);
aRow.insertElementAt(newVal, col);
fireTableCellUpdated(row, col);
}
public void setColumnVisible(int index, boolean visible) {
_columnsVisible[index] = visible;
fireTableStructureChanged();
}
}
}
Your problem in getColumnClass method as you see you also return Object because of that you also get String columns.
You can determine that method in next way:
#Override
public Class<?> getColumnClass(int arg0) {
return longValues[arg0].getClass();
}
Or return Classes for your columns in another way. But you must to determine this Classes while cunstruct your model.

How to change rows with columns in JTable?

I have created my JTable like this:
String[] columns = {"Country", "Continent", "Latitude", "Longitude"};
String[][] data = {{"A","B","C","D"}};
table = new JTable(data,columns);
JScrollPane spTable = new JScrollPane(table);
panel2.add(spTable);
Now I want to change the look of the table in such a way that rows will be exchanged with columns, meaning that the table will have 4 rows and 1 column. Could not find such function between member methods, is it possible? Thanks.
To do that, I would consider a custom TableModel which can handle a "toggled" state.
From your example, the result may be surprizing but this code shows you the general idea and allows you to swap rows and columns with a button.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
public class TestTable {
private static class TogglingTableModel extends AbstractTableModel {
private boolean toggled = false;
private final Object[][] data;
private final Object[] columnNames;
public TogglingTableModel(Object[][] data, Object[] columnNames) {
this.columnNames = columnNames;
this.data = data;
normalizeData();
}
private void normalizeData() {
for (int i = 0; i < data.length; i++) {
Object[] o = data[i];
if (o.length < columnNames.length) {
data[i] = Arrays.copyOf(o, columnNames.length);
}
}
}
#Override
public String getColumnName(int column) {
if (toggled) {
Object valueAt;
if (column == 0) {
valueAt = columnNames[0];
} else {
valueAt = data[column - 1][0];
}
return valueAt != null ? valueAt.toString() : null;
} else {
Object object = columnNames[column];
return object != null ? object.toString() : null;
}
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
#Override
public int getColumnCount() {
if (toggled) {
return data.length + 1;
} else {
return columnNames.length;
}
}
#Override
public int getRowCount() {
if (toggled) {
return columnNames.length - 1;
} else {
return data.length;
}
}
#Override
public Object getValueAt(int row, int column) {
if (toggled) {
if (column == 0) {
return columnNames[row + 1];
} else {
return data[column - 1][row + 1];
}
} else {
return data[row][column];
}
}
#Override
public void setValueAt(Object aValue, int row, int column) {
if (toggled) {
if (column == 0) {
columnNames[row + 1] = aValue;
} else {
data[column - 1][row + 1] = aValue;
}
} else {
data[row][column] = aValue;
}
}
public boolean isToggled() {
return toggled;
}
public void toggle() {
toggled = !toggled;
fireTableStructureChanged();
}
}
private TogglingTableModel tableModel;
public TestTable() {
JFrame frame = new JFrame(TestTable.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] columns = { "Country", "Continent", "Latitude", "Longitude" };
String[][] data = { { "A" }, { "B" }, { "C" }, { "D" }, { "E" } };
tableModel = new TogglingTableModel(data, columns);
JTable table = new JTable(tableModel);
JPanel buttonPanel = new JPanel();
JButton togglingButton = new JButton("Toggle table");
togglingButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
tableModel.toggle();
}
});
buttonPanel.add(togglingButton);
frame.add(new JScrollPane(table));
frame.add(buttonPanel, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestTable fs = new TestTable();
}
});
}
}

Jtable update selected row

I have a jtable that has a edit button, when i select a row and clicked to edit button and edit fields, and click to save button, that row doesn't update, And i have to refresh my table to Change that row!
My code:
if (e.getSource() == editButton) {
selectedRow = uTable.getSelectedRow();
if (selectedRow >= 0) {
editUser(selectedRow);
} else {
JOptionPane.showMessageDialog(null, "Select a row");
}
}
public void editUser(int row) {
UserInformation userInf = userModel.getSelectedMember(row);
NewUserFrame_Edit edit = new NewUserFrame_Edit(userInf, row);
}
...
My NewUserFrame_Edit Class :
public class NewUserFrame_Edit extends javax.swing.JFrame {
private AllUser userModel;
private int selectedrow;
private String gender;
public NewUserFrame_Edit(AllUser userModel,UserInformation userinf, int row) {
...
this.userModel = userModel;
jTextField1.setText(userinf.getFname().toString().trim());
jTextField2.setText(userinf.getLname().toString().trim());
if (userinf.getGender().equals("Male")) {
jRadioButton1.setSelected(true);
} else {
jRadioButton2.setSelected(true);
}
jTextField3.setText(userinf.getDate());
selectedrow = row;
setVisible(true);
}
private void updateButtonActionPerformed(java.awt.event.ActionEvent evt) {
userinf = new UserInformation();
userinf.setFname(jTextField1.getText());
userinf.setLname(jTextField2.getText());
userinf.setGender(gender);
userinf.setDate(jTextField3.getText());
userModel.setValueAt(userinf.getFname() , selectedrow, 1);
userModel.setValueAt(userinf.getLname() , selectedrow, 2);
userModel.setValueAt(userinf.getGender(), selectedrow , 3);
userModel.setValueAt(userinf.getDate() , selectedrow, 4);
userModel.updateFile(userModel.Udata);
NewUserFrame_Edit.this.dispose();
}
...
}
My setValueAt() and updateFile() methods of my model Class:
public class AllUser extends AbstractTableModel {
...
#Override
public void setValueAt(Object value, int rowIndex, int columnIndex) {
UserInformation userInfo = Udata.get(rowIndex);
switch (columnIndex) {
case 0:
userInfo.setID((String) value);
break;
case 1:
userInfo.setFname((String) value);
break;
case 2:
userInfo.setLname((String) value);
break;
case 3:
userInfo.setGender((String) value);
break;
case 4:
userInfo.setDate((String) value);
break;
}
fireTableCellUpdated(rowIndex, columnIndex);
}
public void updateFile(ArrayList<UserInformation> data) {
PrintWriter pw;
try {
pw = new PrintWriter(new FileWriter("AllUserRecords.txt"));
for (UserInformation userinf : data) {
String line = userinf.getID()
+ " " + userinf.getFname()
+ " " + userinf.getLname()
+ " " + userinf.getGender()
+ " " + userinf.getDate();
pw.println(line);
}
pw.close();
} catch (IOException ioe) {
}
}
...
}
When i select a row and click to edit button, a new jframe is open that its text field is fill with older data, and i update data and click to save button.
Thus, my column is not certain.
I select a entire row, Not a cell!
Thanks.
For changing JTable entries, use TableModel#setValueAt. Calling fireTableDataChanged is unnecessary. This is for use internally within TableModel itself.
if (selectedRow >= 0) {
...
userModel.setValueAt(newValue, selectedRow, 0);
// ... more values for columns 1, 2, 3, etc.
} ...
You can create a method called updateRow(int index,String[] values) within your AbstractModel extending class and within it set new Value for each cell for that row using setValueAt(newValue,row,index). And within overridden setValue method of TableModel write fireTableCellUpdated(row, col).
Consider the Code Given below. Look at the updateRow and setValueAt method in MyModel class. And watch ((MyModel)myModel).updateRow(row,values);//update row written in MyMouseAdapter class.
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.table.*;
class TableRowEdit extends JFrame
{
private JTable table;
private JScrollPane jsPane;
private TableModel myModel;
private JPanel dialogPanel;
private JTextField tf[];
private JLabel lbl[];
public void prepareAndShowGUI()
{
myModel = new MyModel();
table = new JTable(myModel);
jsPane = new JScrollPane(table);
table.getColumnModel().getColumn(2).setCellRenderer(new LabelCellRenderer());
table.addMouseListener(new MyMouseAdapter());
getContentPane().add(jsPane);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
prepareDialogPanel();
pack();
setVisible(true);
}
private void prepareDialogPanel()
{
dialogPanel = new JPanel();
int col = table.getColumnCount() - 1;
dialogPanel.setLayout(new GridLayout(col,2));
tf = new JTextField[col];
lbl = new JLabel[col];
for (int i = 0; i < col; i++)
{
lbl[i] = new JLabel(table.getColumnName(i));
tf[i] = new JTextField(10);
dialogPanel.add(lbl[i]);
dialogPanel.add(tf[i]);
}
}
private void populateTextField(String[] s)
{
for (int i = 0 ; i < s.length ; i++ )
{
tf[i].setText(s[i]);
}
}
public class LabelCellRenderer extends DefaultTableCellRenderer
{
public Component getTableCellRendererComponent(JTable table,Object oValue, boolean isSelected, boolean hasFocus, int row, int column)
{
Component c = super.getTableCellRendererComponent(table, oValue,isSelected, hasFocus,row, column);
String value = (String)oValue;
JLabel label =(JLabel)c;
label.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
label.setBackground(Color.lightGray);
label.setHorizontalTextPosition(SwingUtilities.CENTER);
label.setHorizontalAlignment(SwingUtilities.CENTER);
label.setText(value);
return label;
}
}
private class MyMouseAdapter extends MouseAdapter
{
#Override
public void mousePressed(MouseEvent evt)
{
int x = evt.getX();
int y = evt.getY();
int row = table.rowAtPoint(new Point(x,y));
int col = table.columnAtPoint(new Point(x,y));
if (col == 2)
{
String arr[] = new String[table.getColumnCount() - 1];
for (int i = 0 ; i < arr.length ; i++)
{
arr[i] = (String)table.getValueAt(row,i);
}
populateTextField(arr);
JOptionPane.showMessageDialog(TableRowEdit.this,dialogPanel,"Information",JOptionPane.INFORMATION_MESSAGE);
String[] values = new String[tf.length];
for (int i = 0 ; i < tf.length ; i++)
{
values[i] = tf[i].getText();
}
((MyModel)myModel).updateRow(row,values);//update row
}
}
}
private class MyModel extends AbstractTableModel
{
String[] columns = {
"Roll No.",
"Name",
"Action"
};
String[][] inData = {
{"1","Anthony Hopkins","Edit"},
{"2","James William","Edit"},
{"3","Mc. Donald","Edit"}
};
#Override
public void setValueAt(Object value, int row, int col)
{
inData[row][col] = (String)value;
fireTableCellUpdated(row,col);
}
#Override
public Object getValueAt(int row, int col)
{
return inData[row][col];
}
#Override
public int getColumnCount()
{
return columns.length;
}
#Override
public int getRowCount()
{
return inData.length;
}
#Override
public String getColumnName(int col)
{
return columns[col];
}
#Override
public boolean isCellEditable(int row ,int col)
{
return true;
}
//This method updates the Row of table
public void updateRow(int index,String[] values)
{
for (int i = 0 ; i < values.length ; i++)
{
setValueAt(values[i],index,i);
}
}
}
public static void main(String st[])
{
SwingUtilities.invokeLater( new Runnable()
{
#Override
public void run()
{
TableRowEdit td = new TableRowEdit();
td.prepareAndShowGUI();
}
});
}
}
UPDATE
Everything was fine with your original code. Keep the code as it was initially. The only problem was line userModel = new AllUser(); in method updateButtonActionPerformed. It was creating new object of AllUser instead of using the current one. So you should remove this line from updateButtonActionPerformed . and change the NewUserFrame_Edit constructor as follows:
public NewUserFrame_Edit(AllUser userModel/*Add this parameter*/,UserInformation userinf, int row) {
...
this.userModel = userModel;
jTextField1.setText(userinf.getFname().toString().trim());
jTextField2.setText(userinf.getLname().toString().trim());
if (userinf.getGender().equals("Male")) {
jRadioButton1.setSelected(true);
} else {
jRadioButton2.setSelected(true);
}
jTextField3.setText(userinf.getDate());
selectedrow = row;
setVisible(true);
}
change your as updateButtonActionPerformed follows:
private void updateButtonActionPerformed(java.awt.event.ActionEvent evt) {
//userModel = new AllUser();//Comment it.
userinf = new UserInformation();
userinf.setFname(jTextField1.getText());
userinf.setLname(jTextField2.getText());
userinf.setGender(gender);
userinf.setDate(jTextField3.getText());
userModel.setValueAt(userinf.getFname() , selectedrow, 1);
userModel.setValueAt(userinf.getLname() , selectedrow, 2);
userModel.setValueAt(userinf.getGender(), selectedrow , 3);
userModel.setValueAt(userinf.getDate() , selectedrow, 4);
userModel.updateFile(userModel.Udata);
NewUserFrame_Edit.this.dispose();
}
And changeeditUser(int row) method of class UserPage as follows:
public void editUser(int row)
{
UserInformation userInf = userModel.getSelectedMember(row);
NewUserFrame_Edit edit = new NewUserFrame_Edit(userModel,userInf, row);
}
Here is your updateFile methd:
public void updateFile(ArrayList<UserInformation> data) {
PrintWriter pw;
try {
pw = new PrintWriter(new FileWriter("AllUserRecords.txt"));
for (UserInformation userinf : data) {
String line = userinf.getID()
+ " " + userinf.getFname()
+ " " + userinf.getLname()
+ " " + userinf.getGender()
+ " " + userinf.getDate();
pw.println(line);
}
pw.close();
} catch (IOException ioe) {
}
}
Another way to implement changes through JTable is to use the code template below:
table.getModel().addTableModelListener(new TableModelListener(){
/**
* Called when table has been changed.
*/
#Override
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
TableModel model = (TableModel) e.getSource();
String newdata = (String) model.getValueAt(row, column);
model.setValueAt(newdata, row, column);
// do your update with the newdata variable.
}
});
Here you can use the actual event to get the row and column of the cell that has been changed in the JTable variable.

How to gray-out non-editable cell in jtable?

I want to gray non-editable cell in JTable.
I'm using such TableCellRenderer:
TableColumn column = table.getColumnModel().getColumn(0);
column.setCellRenderer(new GrayableCheckboxCellRenderer());
public class GrayableCheckboxCellRenderer extends JCheckBox implements TableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int vRowIndex, int vColIndex) {
boolean editable = isEditable(vRowIndex, vColIndex);
setBackground(editable ? UIManager.getColor("Label.background") : Color.LIGHT_GRAY);
setSelected((Boolean) value);
if (isSelected) {
// TODO: cell (and perhaps other cells) are selected, need to highlight it
}
return this;
}
// perfomance
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {}
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
public void revalidate() {}
public void validate() {}
}
This works but with one annoying artefact:
Initially "checkbox" is "left-arranged", when I press left mouse button it moves to "center-arranged" and when I release mouse button it moves back to "left-arranged".
How to avoid such annoying artefact and probably there are better simpler solution for my problem?
Return an instance of GrayableCheckboxCellRenderer in a TableCellEditor.
Addendum: Aesthetically, you may want to condition the renderer's and editor's colors based on the defaults provided by the current Look & Feel, for example:
Color hilite = UIManager.getColor("Table.selectionBackground");
simpliest way by using preparedRenderer
import java.awt.*;
import java.util.Random;
import java.util.Vector;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
public class Forum {
private JFrame frame = new JFrame("Frame");
private JPanel fatherCenter = new JPanel();
private JScrollPane tableScroll = new JScrollPane();
private myTableModel tableModel;
private JTable dialogTable;
private ListSelectionModel lsDialog;
private void addComponentsToPane(Container pane) {
tableModel = new myTableModel();
dialogTable = new JTable(tableModel) {
private static final long serialVersionUID = 1L;
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component comp = super.prepareRenderer(renderer, row, column);
JComponent jc = (JComponent) comp;//for Custom JComponent
if (!isRowSelected(row)) {
int modelRow = convertRowIndexToModel(row);
boolean type = (Boolean) getModel().getValueAt(modelRow, 2);
boolean type1 = (Boolean) getModel().getValueAt(modelRow, 3);
boolean type2 = (Boolean) getModel().isCellEditable(row, column);
comp.setForeground(Color.black);
if ((type) && (!type1)) {
comp.setBackground(Color.yellow);
} else if ((!type) && (type1)) {
comp.setBackground(Color.orange);
} else if ((!type) || (!type1)) {
comp.setBackground(Color.red);
//} else if ((!type2)) {
//comp.setForeground(Color.red);
//comp.setBackground(Color.magenta);
} else {
comp.setBackground(row % 2 == 0 ? getBackground() : getBackground().darker());
}
dialogTable.convertRowIndexToView(0);
} else {
comp.setForeground(Color.blue);
comp.setBackground(Color.lightGray);
}
if (!isCellEditable(row, column)) {
comp.setForeground(Color.red);
comp.setBackground(Color.magenta);
}
return comp;
}
};
tableScroll = new JScrollPane(dialogTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
tableScroll.setBorder(null);
dialogTable.getTableHeader().setReorderingAllowed(false);
dialogTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
lsDialog = dialogTable.getSelectionModel();
dialogTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
dialogTable.setRowHeight(20);
dialogTable.setRowMargin(2);
fatherCenter = new JPanel();
fatherCenter.setLayout(new BorderLayout(10, 10));
fatherCenter.add(tableScroll, BorderLayout.CENTER);
pane.add(fatherCenter);
}
private void addData() {
Runnable doRun1 = new Runnable() {
#Override
public void run() {
tableModel.resetTable();
Vector<String> tbl = new Vector<String>();
Vector<Object> tbl1 = new Vector<Object>();
Random rnd = new Random();
tbl.add("Integer");
tbl.add("Double");
tbl.add("Boolean");
tbl.add("Boolean");
tbl.add("String");
tableModel.setColumnNames(tbl);
for (int row = 0; row < 30; row++) {
tbl1 = null;
tbl1 = new Vector<Object>();
tbl1.addElement(row + 1);
tbl1.addElement(rnd.nextInt(25) + 3.14);
tbl1.addElement((row % 3 == 0) ? false : true);
tbl1.addElement((row % 5 == 0) ? false : true);
if (row % 7 == 0) {
tbl1.add(("Canc"));
} else if (row % 6 == 0) {
tbl1.add(("Del"));
} else {
tbl1.add(("New"));
}
tableModel.addRow(tbl1);
}
addTableListener();
}
};
SwingUtilities.invokeLater(doRun1);
}
private void addTableListener() {
tableModel.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent tme) {
if (tme.getType() == TableModelEvent.UPDATE) {
System.out.println("Cell " + tme.getFirstRow() + ", "
+ tme.getColumn() + " changed. The new value: "
+ tableModel.getValueAt(tme.getFirstRow(),
tme.getColumn()));
}
}
});
}
private void createAndShowGUI() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout(10, 10));
addComponentsToPane(frame.getContentPane());
addData();
frame.setLocation(150, 150);
frame.setPreferredSize(new Dimension(400, 646));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
Forum osFrame = new Forum();
}
public Forum() {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private class myTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
private Vector<Vector<Object>> data;
private Vector<String> colNames;
private boolean[] _columnsVisible = {true, true, true, true, true};
myTableModel() {
this.colNames = new Vector<String>();
this.data = new Vector<Vector<Object>>();
}
myTableModel(Vector<String> colnames) {
this.colNames = colnames;
this.data = new Vector<Vector<Object>>();
}
public void resetTable() {
this.colNames.removeAllElements();
this.data.removeAllElements();
}
public void setColumnNames(Vector<String> colNames) {
this.colNames = colNames;
this.fireTableStructureChanged();
}
public void addRow(Vector<Object> data) {
this.data.add(data);
this.fireTableDataChanged();
this.fireTableStructureChanged();
}
public void removeRowAt(int row) {
this.data.removeElementAt(row);
this.fireTableDataChanged();
}
#Override
public int getColumnCount() {
return this.colNames.size();
}
#Override
public Class<?> getColumnClass(int colNum) {
switch (colNum) {
case 0:
return Integer.class;
case 1:
return Double.class;
case 2:
return Boolean.class;
case 3:
return Boolean.class;
default:
return String.class;
}
}
#Override
public boolean isCellEditable(int row, int colNum) {
switch (colNum) {
case 2:
return false;
default:
return true;
}
}
#Override
public String getColumnName(int colNum) {
return this.colNames.get(colNum);
}
#Override
public int getRowCount() {
return this.data.size();
}
#Override
public Object getValueAt(int row, int col) {
Vector<Object> value = this.data.get(row);
return value.get(col);
}
#Override
public void setValueAt(Object newVal, int row, int col) {
Vector<Object> aRow = data.elementAt(row);
aRow.remove(col);
aRow.insertElementAt(newVal, col);
fireTableCellUpdated(row, col);
}
public void setColumnVisible(int index, boolean visible) {
this._columnsVisible[index] = visible;
this.fireTableStructureChanged();
}
}
}
Just explicitly setting the alignment in the 'getTableCellRendererComponent()' method solves the problem. Add the below line before returning from the method.
setHorizontalAlignment(SwingConstants.CENTER);

Categories