Обновление
Подтверждено как ошибка на JTable не может отформатировать данный объект как число, когда columnClass имеет значение Double (идентификатор ошибки: 7051636) .Не стесняйтесь голосовать за него, или, если у вас есть альтернативный (лучший) обходной путь, опубликуйте его как комментарий к отчету.
Я создаю JTable с пользовательской моделью таблицывстроенный расширяющий AbstractTableModel.Моя модель должна поддерживать пустую строку для отображения и сортировки.Поэтому я реализовал этот пост , чтобы реализовать это, и теперь работает довольно хорошо.
У меня все еще есть проблема с отформатированным полем в JTable.Предположим, у меня есть следующая модель:
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();
}
}
Все отлично работает.Но если у меня есть Double вместо Integer:
public class MyModel extends AbstractTableModel{
public Object[] types= {new Double(0.0), ""};
.......
Я получу исключение Illegal Argument:
EDIT: вывод новой трассировки стека после предложения @Aaron Digulla
Исключение в потоке "AWT-EventQueue-0" java.lang.IllegalArgumentException: не удается отформатировать данный объект как число в java.text.DecimalFormat.format (DecimalFormat.java:487) в java.text.Format.format (Format.java:140) в javax.swing.JTable $ DoubleRenderer.setValue (JTable.java:5352) в javax.swing.table.DefaultTableCellRenderer.getTableCellRendererComponent (DefaultTableCellRenderesw.are.j.R.D.Rax.37.Rax.37.2)(JTable.java:5720) в javax.swing.plaf.basic.BasicTableUI.paintCell (BasicTableUI.java:2072) в javax.swing.plaf.basic.BasicTableUI.paintCells (BasicTableUI.java:1974) в javax.plaf.basic.BasicTableUI.paint (BasicTableUI.java:1770) в javax.swing.plaf.ComponentUI.update (ComponentUI.java:143) в javax.swing.JComponent.paintComponent (JComponent.java:752 at).JComponent.paint (JComponent.java:1029) в javax.swing.JComponent.paintChildren (JComponent.java:862) в javax.swing.JComponent.paint (JComponent.java:1038) в javax.swing.JViewport.paint (.java: 747) в javax.swing.JComponent.paintChildren (JComponent.java:862) в javax.swing.JComponent.paint (JComponent.java:1038) в javax.swing.JComponent.paintChildren (JCompon2).на javax.swing.JComponent.paint (JComponent.java:1038) на javax.swing.JComponent.paintChildren (JComponent.java:862) на javax.swing.JComponent.paint (JComponent.java:1038) на javax.jpg.JLayeredPane.paint (JLayeredPane.java:567) по адресу javax.swing.JComponent.paintChildren (JComponent.java:862) по адресу javax.swing.JComponent.paintToOffscreen (JComponent.java:5131) Пейджен.java: 278) на javax.swing.RepaintManager.paint (RepaintManager.java:1224) на javax.swing.JComponent.paint (JComponent.java:1015) на java.awt.GraphicsCallback $ PaintCallback.run (GraphicsCallback.jun)a: 21) в sun.awt.SunGraphicsCallback.runOneComponent (SunGraphicsCallback.java:60) в sun.awt.SunGraphicsCallback.runComponents (SunGraphicsCallback.java:97) в java.awt.Container.paint (Container.java:17)java.awt.Window.paint (Window.java:3375) в javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:796) в javax.swing.RepaintManager.paintDirtyRegions (RepaintManager.java:713) в javaxswing.seqPaintDirtyRegions (RepaintManager.java:693) в javax.swing.SystemEventQueueUtilities $ ComponentWorkRequest.run (SystemEventQueueUtilities.java:125) в java.awt.event.InvocationEvent.dispatch (InvavaueE.t).dispatchEvent (EventQueue.java:597) по адресу java.awt.EventDispatchThread.pumpOneEventForFilters (EventDispatchThread.java:269) по адресу java.awt.EventDispatchThread.pumpEventsForFilter (EventDispatchThread.javaisatchHatchDatchPextHt: 174) в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:169)в java.awt.EventDispatchThread.pumpEvents (EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run (EventDispatchThread.java:122)
Почему это?
getValueAt всегда возвращает одно и то же значение, чтобы заполнить все записи таблицы им.
Это только для отладки:
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return this.EMPTY_ROW;
}
Например, если я изменю на:
@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
}
все работает нормально, даже если некоторые поля таблицы имеют строковое значение. Это предлагает мне, потому что
Integer и Double могут быть преобразованы в String, это не вызовет проблем.
В любом случае я хотел бы понять, почему универсальный объект, такой как EMPTY_ROW, может быть принят в качестве значения объявленного поля Integer, в то время как это не работает с полями Double.
EDIT2:
Если я удалю метод getClass в моей табличной модели. Оно работает. В любом случае, я бы хотел решить эту проблему, не удаляя этот метод, даже если это заставит меня реализовать некоторые пользовательские методы рендеринга.
EDIT3:
вот SSCCE. Есть некоторые ошибки при добавлении новых значений в таблицу, но они не имеют ничего общего с проблемами рендеринга.
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();
}
}
Если вы попытаетесь изменить
Class[] types = {Integer.class, String.class};
с:
Class[] types = {Double.class, String.class};
Вы можете увидеть проблему.