将JComboBox添加到单元格后,JTable列无法使用

时间:2015-06-01 00:51:14

标签: java swing jtable

我有一个带有2列的JTable组件,对于第二列我只想为一个单元添加一个JComboBox,遵循oracle文档我创建了自己的单元格编辑器,并添加了JComboBox但是之后所有的来自该列的其他细胞变得无法编辑。 这是一个例子:

Before adding the JComboBox:

添加JComboBox后,我无法编辑其他单元格

After adding the JComboBox:

我的代码:

DefaultTableModel model = new DefaultTableModel(textosTabela, stubColumnNames);
tabela.setModel(model);
tabela.setBorder(new LineBorder(Color.black));
tabela.setGridColor(Color.black);
tabela.setShowGrid(true);
tabela.setPreferredSize(new Dimension(290, 132));
tabela.setRowHeight(22);
tabela.getColumnModel().getColumn(0).setPreferredWidth(160);
tabela.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
TableColumn tm = tabela.getColumnModel().getColumn(0);
tm.setCellRenderer(new ColorColumnRenderer(Color.lightGray));
TableColumn comboCol1 = tabela.getColumnModel().getColumn(1);
JComboBox comboBox = new JComboBox(valoresComboBox);
comboBox.setSelectedIndex(0);
comboCol1.setCellEditor(new ComboBoxEditor(1,3,comboBox));

CellEditor代码:

public class ComboBoxEditor extends DefaultCellEditor {

    private String[] values;
    private String selectedValue;
    private int column = -1;
    private int row = -1;

    public ComboBoxEditor(JComboBox values) {
        super(values);
        // TODO Auto-generated constructor stub
    }

    public ComboBoxEditor(int column, int row, JComboBox values) {
        super(values);
        this.column = column;
        this.row = row;

    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int row, int column) {
        // TODO Auto-generated method stub
        Component c = table.getEditorComponent();
        if(column == this.column && row == this.row) {
            return super.getTableCellEditorComponent(table, value, isSelected, row, column);
        }

        return null;
    }
}

3 个答案:

答案 0 :(得分:2)

这是一个逐行返回不同组合框的示例。如果该行没有组合框,则使用默认编辑器:

private String processAction(String actionJson) {     
    String[] data = actionJson.split(":");
    int limit = data[1].length() - 3;
    String result = data[1].substring(1, limit);
    return result;
}

密钥是覆盖import java.awt.*; import java.util.List; import java.util.ArrayList; import javax.swing.*; import javax.swing.border.*; import javax.swing.table.*; public class TableComboBoxByRow extends JPanel { List<String[]> editorData = new ArrayList<String[]>(3); public TableComboBoxByRow() { setLayout( new BorderLayout() ); // Create the editorData to be used for each row editorData.add( new String[]{ "Red", "Blue", "Green" } ); editorData.add( new String[]{ "Circle", "Square", "Triangle" } ); editorData.add( new String[]{ "Apple", "Orange", "Banana" } ); // Create the table with default data Object[][] data = { {"Color", "Red"}, {"Shape", "Square"}, {"Fruit", "Banana"}, {"Plain", "Text"} }; String[] columnNames = {"Type","Value"}; DefaultTableModel model = new DefaultTableModel(data, columnNames); JTable table = new JTable(model) { // Determine editor to be used by row public TableCellEditor getCellEditor(int row, int column) { int modelColumn = convertColumnIndexToModel( column ); if (modelColumn == 1 && row < 3) { JComboBox<String> comboBox1 = new JComboBox<String>( editorData.get(row)); return new DefaultCellEditor( comboBox1 ); } else return super.getCellEditor(row, column); } }; JScrollPane scrollPane = new JScrollPane( table ); add( scrollPane ); // table.getColumnModel().getColumn(1).setCellRenderer(new ComboBoxRenderer2() ); } private static void createAndShowUI() { JFrame frame = new JFrame("Table Combo Box by Row"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add( new TableComboBoxByRow() ); frame.setSize(200, 200); frame.setLocationByPlatform( true ); frame.setVisible( true ); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { createAndShowUI(); } }); } } 方法,而不是创建自定义渲染器。

答案 1 :(得分:2)

TableModel应该指定哪些单元格可以编辑,默认情况下,DefaultTableModel会使所有单元格都可编辑。

TableCellEditor,你有......

if (column == this.column && row == this.row) {
    return super.getTableCellEditorComponent(table, value, isSelected, row, column);
}

这似乎是为编辑做出决定,例如模型应该是

DefaultTableModel model = new DefaultTableModel(textosTabela, stubColumnNames) {
    @Override
    public boolean isCellEditable(int row, int column) {
        return row == 3 && column == 1;
    }
};

只需使用DefaltCellEditor打包JComboBox

即可
JComboBox comboBox = new JComboBox(valoresComboBox);
DefaultCellEditor editor = new DefaultCellEditor(comboBox);

TableColumn comboCol1 = tabela.getColumnModel().getColumn(1);

我可能会指出,一般来说,JTable作为属性表编辑器并不是很好,其中每行的单元格值不同

PropertySheetEditor

可重用性和可配置性是我对任何类型的实现的两个主要关注点。现在,桌子有责任决定如何渲染或编辑事物,这就是渲染器和编辑器的原因。我们需要的是一些在API中提供可配置元素的方法,它可以做出我们需要的决策,并让表格完成它目前正在做的工作。

由于JTable并非真正设计用于处理特定列的多种类型的数据,以及不同的渲染器和编辑器,因此我们需要提供该功能。

属性

让我们从关键元素属性开始。属性有三个基本元素,名称,值和类型。

public interface Property<T> {
    public String getName();
    public T getValue();
    public Class<T> getType();
    public void setValue(T value);
}

我确实考虑为可变属性制作另一个interface,但如果需要,您可以轻松地向此界面添加readOnly属性。

因为我懒惰......

public class DefaultProperty<T> implements Property<T> {

    private final String name;
    private T value;
    private final Class<T> type;

    public DefaultProperty(String name, T value, Class<T> type) {
        this.name = name;
        this.value = value;
        this.type = type;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public T getValue() {
        return value;
    }

    @Override
    public Class<T> getType() {
        return type;
    }

    @Override
    public void setValue(T value) {
        this.value = value;
    }

}

模型

现在,我们需要的模型可以管理我们的Property以及与JTable的合同...

public interface PropertySheetModel extends TableModel {        
    public Property getPropertyAt(int row);
}

因为我懒惰......

public class DefaultPropertySheeModel extends AbstractTableModel implements PropertySheetModel {

    private List<Property> properties;

    public DefaultPropertySheeModel(List<Property> properties) {
        this.properties = new ArrayList<>(properties);
    }

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

    @Override
    public int getColumnCount() {
        return 2; // Key/value
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Property p = getPropertyAt(rowIndex);
        Object value = null;
        switch (columnIndex) {
            case 0:
                value = p.getName();
                break;
            case 1:
                value = p.getValue();
                break;
        }
        return value;
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        Property p = getPropertyAt(rowIndex);
        p.setValue(aValue);
        fireTableCellUpdated(rowIndex, columnIndex);
    }

    @Override
    public Property getPropertyAt(int row) {
        return getProperties().get(row);
    }

    protected List<Property> getProperties() {
        return properties;
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnIndex != 0;
    }

}

创建动态模型并不会花费太多时间,您可以添加/删除Property,这超出了此答案的范围;)

代表

好的,现在我们需要开始为我们制定更复杂的决策。这是代表进来的地方,我们可以用它来回答我们不想回答的一些问题

public interface PropertySheetDelegate {

    public TableCellEditor getCellEditorFor(Property property);

    public void setCellEditorFor(Property property, TableCellEditor editor);
    public void setCellEditorFor(Class type, TableCellEditor editor);

    public TableCellRenderer getCellRendererFor(Property property);
    public void setCellRendererFor(Property property, TableCellRenderer editor);
    public void setCellRendererFor(Class type, TableCellRenderer editor);

}

因为我懒惰......

public class DefaultPropertySheetDelegate implements PropertySheetDelegate {

    private Map<Property, TableCellEditor> propertyEditors;
    private Map<Class, TableCellEditor> typeEditors;

    private Map<Property, TableCellRenderer> propertyRenderers;
    private Map<Class, TableCellRenderer> typeRenderers;

    public DefaultPropertySheetDelegate() {
        propertyEditors = new HashMap<>(25);
        typeEditors = new HashMap<>(25);
        propertyRenderers = new HashMap<>(25);
        typeRenderers = new HashMap<>(25);

        JTextField field = new JTextField();
        field.setBorder(null);

        DefaultCellEditor editor = new DefaultCellEditor(field);
        editor.setClickCountToStart(1);
        setCellEditorFor(String.class, editor);
    }

    @Override
    public TableCellEditor getCellEditorFor(Property property) {
        TableCellEditor editor = propertyEditors.get(property);
        if (editor == null) {
            editor = typeEditors.get(property.getType());
        }
        return editor;
    }

    @Override
    public void setCellEditorFor(Property property, TableCellEditor editor) {
        propertyEditors.put(property, editor);
    }

    @Override
    public void setCellEditorFor(Class type, TableCellEditor editor) {
        typeEditors.put(type, editor);
    }

    @Override
    public void setCellRendererFor(Class type, TableCellRenderer renderer) {
        typeRenderers.put(type, renderer);
    }

    @Override
    public void setCellRendererFor(Property property, TableCellRenderer renderer) {
        propertyRenderers.put(property, renderer);
    }

    @Override
    public TableCellRenderer getCellRendererFor(Property property) {
        TableCellRenderer renderer = propertyRenderers.get(property);
        if (renderer == null) {
            renderer = typeRenderers.get(property.getType());
        }
        return renderer;
    }

}

现在,这个实现只是处理TableCellEditor,实际上,你应该也可以处理渲染器。

视图......

最后,我们需要一些方法来展示它,这是一个自定义的JTable(显然),它使用PropertySheetDelegatePropertySheetModel来呈现管理的数据...

public class PropertySheet extends JTable {

    private PropertySheetDelegate propertySheetDelegate;

    public PropertySheet(PropertySheetDelegate controller, PropertySheetModel model) {
        super(model);
        setDelegate(controller);
    }

    public PropertySheet() {
        super();
    }

    @Override
    public void setModel(TableModel dataModel) {
        if (dataModel instanceof PropertySheetModel || dataModel == null) {
            super.setModel(dataModel);
        } else {
            throw new UnsupportedOperationException("Unsupported PropertySheetModel " + dataModel.getClass().getName());
        }
    }

    public PropertySheetDelegate getPropertySheetDelegate() {
        return propertySheetDelegate;
    }

    public void setDelegate(PropertySheetDelegate value) {
        if (propertySheetDelegate != value) {
            PropertySheetDelegate old = propertySheetDelegate;
            this.propertySheetDelegate = value;
            firePropertyChange("propertySheetController", old, value);
        }
    }

    @Override
    public TableCellEditor getCellEditor(int row, int column) {
        TableCellEditor editor = null;
        // Assumes that only the values can be modified
        if (column == 1) {
            PropertySheetDelegate delegate = getPropertySheetDelegate();
            if (delegate != null) {
                PropertySheetModel model = (PropertySheetModel) getModel();
                Property property = model.getPropertyAt(row);
                editor = delegate.getCellEditorFor(property);

                System.out.println("Editor for " + property + " = " + editor);

                if (editor == null) {
                    editor = getDefaultEditor(property.getType());
                    System.out.println("Default Editor for " + property.getType() + " = " + editor);
                }
            } else {
                editor = super.getCellEditor(row, column);
            }
        }
        return editor;
    }

    @Override
    public TableCellRenderer getCellRenderer(int row, int column) {
        TableCellRenderer renderer = null;
        if (column == 1) {
            PropertySheetDelegate delegate = getPropertySheetDelegate();
            if (delegate != null) {
                PropertySheetModel model = (PropertySheetModel) getModel();
                Property property = model.getPropertyAt(row);
                renderer = delegate.getCellRendererFor(property);
                if (renderer == null) {
                    renderer = getDefaultRenderer(property.getType());
                }
            } else {
                renderer = super.getCellRenderer(row, column);
            }
        } else {
            renderer = super.getCellRenderer(row, column);
        }
        return renderer;
    }

}

管道

最后,还有一些让它有用的东西......

List<Property> properties = new ArrayList<>(25);
properties.add(new DefaultProperty<>("Name", null, String.class));
properties.add(new DefaultProperty<>("Description", null, String.class));
properties.add(new DefaultProperty<>("Quanity", null, Integer.class));
properties.add(new DefaultProperty<>("Warrenty", null, Integer.class));
properties.add(new DefaultProperty<>("Returns", null, Boolean.class));

// This is our custom editor
DefaultCellEditor editor = new DefaultCellEditor(new JComboBox(new Integer[]{1, 2, 3, 4, 5}));
PropertySheetDelegate controller = new DefaultPropertySheetDelegate();
controller.setCellEditorFor(properties.get(2), editor);

PropertySheetModel model = new DefaultPropertySheeModel(properties);
PropertySheet propertySheet = new PropertySheet(controller, model);

setLayout(new BorderLayout());
add(new JScrollPane(propertySheet));

答案 2 :(得分:0)

使用新的更新可以更轻松地使用默认的渲染器和编辑器。

但是,在JTable中,GenericEditor使用getColumnClass()方法将输入到文本字段的字符串转换为正确的类,以便将值保存到TableModel。 / p>

您的TableModel正在使用默认的getColumnClass()实现,因此所有值都被视为对象,并且只是存储为字符串。

您可以在编辑Integer或Double值之前和之后单击“所选行类”来验证这一点。 (ComboBoxEditor和BooleanEditor没有问题,因为它们实现了自己的getCellEditorValue()方法以返回正确的值。)

import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;

public class PropertyTest extends JPanel
{
    public PropertyTest()
    {
        List<Property> properties = new ArrayList<>(25);
        properties.add(new DefaultProperty<>("String", "String", String.class));
        properties.add(new DefaultProperty<>("Integer Combo", new Integer(2), Integer.class));
        properties.add(new DefaultProperty<>("Boolean", Boolean.TRUE, Boolean.class));
        properties.add(new DefaultProperty<>("Integer", new Integer(1), Integer.class));
        properties.add(new DefaultProperty<>("Double", new Double(1.25), Double.class));

        // This is our custom editor
        DefaultCellEditor editor = new DefaultCellEditor(new JComboBox(new Integer[]{1, 2, 3, 4, 5}));
        PropertySheetDelegate delegate = new DefaultPropertySheetDelegate();
        delegate.setCellEditorFor(properties.get(1), editor);


        PropertySheetModel model = new DefaultPropertySheetModel(properties);
        final PropertySheet propertySheet = new PropertySheet(delegate, model);

        setLayout(new BorderLayout());
        add(new JScrollPane(propertySheet));

        JButton button = new JButton("Class of Selected Row");
        add(button, BorderLayout.SOUTH);
        button.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                int row = propertySheet.getSelectedRow();
                System.out.println(propertySheet.getValueAt(row, 1).getClass() );
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("PropertyTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new PropertyTest());
        frame.setLocationByPlatform( true );
        frame.setSize(400, 400);
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}

我认为您需要覆盖getColumnClass()的{​​{1}}方法,因为它可以访问正在编辑的行,而JTable则没有。

以下是我添加到PropertySheet类的代码:

TableModel

虽然我相信你会找到更好的方法来实现它:)