【问题标题】:setValueAt in TableModel always gets a stringTableModel 中的 setValueAt 总是得到一个字符串
【发布时间】:2013-04-05 19:32:05
【问题描述】:

我有一个 JTable,它的一列中包含不同的对象类型。每种类型都有自己的渲染器和编辑器(getDefaultRenderer(Number.class)、我自己的 Date 渲染器/编辑器等)。

但是,我的表模型(我覆盖 DefaultTableModel)中的方法 setValueAt(Object value, int row, int col) 始终将字符串作为 value。因此,尽管有不同的编辑器,但我无法在不解析字符串的情况下更新行,这不是一个好主意,因为我的表将来应该可以轻松处理其他对象类型。

getValueAt(int row, int col) 不会将对象转换为字符串。我检查了一下。这种行为的原因是什么?

编辑:下面的代码。只有布尔单元格编辑器似乎可以正常工作。

import java.awt.Component;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

public class Tabela {

public static void main(String[] args){
    JFrame fr = new JFrame("tabela");
    fr.setSize(600, 400);
    fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // custom renderer and editor for Date
    final DateCellEditor dateEditor = new DateCellEditor();
    final DateCellRenderer dateRenderer = new DateCellRenderer();

    final TableModel model = new TableModel();
    JTable table = new JTable(model){
        public TableCellEditor getCellEditor(int row, int col){
            if (model.getValueAt(row, col) instanceof Boolean)
                return getDefaultEditor(Boolean.class);
            else if (model.getValueAt(row, col) instanceof Number)
                return getDefaultEditor(model.getValueAt(row, col).getClass());
            else if (model.getValueAt(row, col) instanceof Date)
                return dateEditor;
            else return getDefaultEditor(Object.class);
        }

        public TableCellRenderer getCellRenderer(int row, int col){
            if (model.getValueAt(row, col) instanceof Boolean)
                return getDefaultRenderer(Boolean.class);
            else if (model.getValueAt(row, col) instanceof Number)
                return getDefaultRenderer(model.getValueAt(row, col).getClass());
            else if (model.getValueAt(row, col) instanceof Date)
                return dateRenderer;
            else 
                return getDefaultRenderer(Object.class);
        }
    };
    JScrollPane sc = new JScrollPane(table);
    fr.getContentPane().add(sc);
    fr.setVisible(true);    
}

public static class TableModel extends DefaultTableModel{
    // data in first column
    private ArrayList<String> names = new ArrayList<String>();
    // data in second column - can by any object
    private ArrayList<Object> values = new ArrayList<Object>();

    public TableModel(){
      // insert example data
        names.add("string value");
        values.add("some string");
        names.add("number value");
        values.add(new Integer(12345));
        names.add("Boolean value");
        values.add(new Boolean(false));
        names.add("Double value");
        values.add(new Double(10.5));
        names.add("Date object");
        values.add(new Date(System.currentTimeMillis()));
    }

    public void setValueAt(Object value, int row, int col){
        values.set(row, value);
        fireTableCellUpdated(row, col);
    }

    public Object getValueAt(int row, int col){
        if (col==0)
            return names.get(row) + " ["+values.get(row).getClass().getSimpleName()+"]";
        else return values.get(row); 
    }

    public int getRowCount(){
        if (values==null) return 0;
        else return values.size();
    }

    public int getColumnCount(){
        return 2;
    }

    public boolean isCellEditable(int row, int col){
        return col==1; // only column 2 is editable
    }

    public Class<?> getColumnClass(int col){
        switch(col){
        case 0:
            return String.class;
        default:
            return Object.class;
        }
    }

}

// My own renderer and editor for Date type
protected static class DateCellRenderer extends DefaultTableCellRenderer{
    private static DateFormat format = new SimpleDateFormat("dd-MM-y HH:mm:ss");

    public static void setDateFormat(String f){
        format = new SimpleDateFormat(f);
    }

    public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)    
    {       
            if (value instanceof Date){
                value = format.format(value);
            }
            else if(value instanceof Calendar)    
            {    
                    Calendar dateValue = (Calendar) value;    
                    value = format.format(dateValue.getTime());  
            } 
            Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 
            if (c instanceof JComponent){
                JComponent jc = (JComponent) c;
                jc.setToolTipText("dd-MM-y HH:mm:ss");
            }
            return c;
    }    
}

protected static class DateCellEditor extends DefaultCellEditor{
    private static DateFormat format = new SimpleDateFormat("dd-MM-y HH:mm:ss");
    private JFormattedTextField textField = new JFormattedTextField(format);

    public DateCellEditor(){
        super(new JFormattedTextField(format));
    }

    public void setDateFormat(String f){
        format = new SimpleDateFormat(f);
        textField = new JFormattedTextField(format);
    }

    public Component getTableCellEditorComponent (JTable table, Object value, boolean isSelected, int row, int column)    
    {       System.out.println("editor: "+value.getClass());
            if (value instanceof Date){
                value = format.format(value);
            }
            else if(value instanceof Calendar)    
            {    
                    Calendar dateValue = (Calendar) value;    
                    value = format.format(dateValue.getTime());  
            } 
            return super.getTableCellEditorComponent(table, value, isSelected, row, column);    
    }    
  }
}

【问题讨论】:

  • 我能想到几个可能的原因,但它们只不过是猜测而已,您能否提供一个可运行的示例来证明您的问题
  • 您未显示的代码(很可能是编辑器)有问题...
  • 有几个错误:a) 不要'干扰编辑器查找,而是实现模型正确返回列类 b) 向 formattedTextField 注册的适当侦听器是其 value 属性的 propertyChangeListener(vs.由 super) 注册的 ActionListener c) 不相关:您在 getCellEditor(row, column) 中混合坐标参数索引在视图坐标中,而访问模型必须在模型坐标中完成
  • ahh .. 忽略了你的第一句话,所以我在评论中的 a) 不适用并且 c 变得相关 ;-) 并且不要忘记检查 null 的值,因为 @Mad 已经提到。
  • 看一种直观的方式Complex Table Cell Rendering Made Simple in Java Net,不确定是否有(EachRow)TableCellEditor,但Renderer描述得很好

标签: java swing jtable


【解决方案1】:

从您的代码示例中,(立即)有许多值得关注的事情。

首先,不需要重写JTablegetCellEditorgetCellRenderer 方法。

如果模型的值为null,我会遇到的最大问题是什么?

您可以改用setDefaultEditor(Class, TableCellEditor)setDefaultRenderer(Class, TableCellRenderer) 方法来修改单元格编辑器并为给定的Class 渲染。

从您的表模型中,您应该返回所需列的所有适当的Class 类型(来自getColumnClass)。这里的假设是给定列具有相同类型的Class 值。

但是,您的问题的原因在于编辑器。

如果可以使用示例编辑器,则您是从DefaultCellEditor 扩展而来的,它只是使用JTextField 作为它的编辑器。这意味着getCellEditorValue 将返回到String,因此您的setValueAt 方法被传递了String 值。

有很多选择。

  1. 您可以设计一个更好的TableCellEditor,它使用更合适的编辑器(例如JSpinnerJCheckBox
  2. 您可以覆盖 getCellEditorValue 以将默认编辑器中的 String 值转换为更合适的 Object 类型。

我更喜欢选项 1,因为它以适合的表单和编辑器表示基础数据,并且可以消除处理错误解析的大量麻烦。

我会仔细查看How to use tables 并密切关注自定义编辑器的部分

【讨论】:

  • 如何从 getColumnClass 返回所有适当的类类型?它们依赖于给定的行(同一列中的多个类类型)。
  • 如我所说,JTable假设一列数据是同一个类,它的JTable,而不是JSpreadSheet
【解决方案2】:

清理我可能在我的 cmets 中传播的困惑:如果你在一列中有不同类的值

  • model.getColumnClass 必须返回所有这些值的公共父级,通常是 Object.class
  • 实现每行渲染器/编辑器的方式确实是覆盖 table.getCellRenderer(row, column)

一些sn-ps

// override in JTable
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
    // use the per-column renderer if available
    TableColumn tableColumn = getColumnModel().getColumn(column);
    TableCellRenderer renderer = tableColumn.getCellRenderer();
    // if the column didn't provide a renderer and has type Object.class
    if (renderer == null && Object.class.equals(getColumnClass(column))) {
        Object value = getValueAt(row, column);
        if (value != null) {
            renderer = getDefaultRenderer(value.getClass());
        }
    }
    if (renderer == null) {
        // shouldn't happen, but fixing super bug
        renderer = getDefaultRenderer(Object.class);
    }
    return renderer;
}

【讨论】:

  • 请在此处查看我已删除的帖子
【解决方案3】:

由于多种原因,返回的对象不能是字符串:

  • 格式化(12345 ==> 1 234、日期、浮点数...)
  • 排序

一个对象到字符串的转换器很容易做到,但是一个字符串到对象将很难以通用的方式编写。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多