繁体   English   中英

将JComboBox添加到单元格后,JTable列不可编辑

[英]JTable column uneditable after adding JComboBox to a cell

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

在添加JComboBox之前:

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

添加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;
    }
}

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

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();
            }
        });
    }
}

关键是重写getCellEditor(...)方法,而不是创建自定义渲染器。

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));

使用新的更新使使用默认渲染器和编辑器更加容易。

但是,在JTableGenericEditor使用getColumnClass()方法将输入到文本字段中的String转换为正确的Class,以便可以将值保存到TableModel。

您的TableModel使用默认的getColumnClass()实现,因此所有值都被视为Objects,并简单地存储为String。

您可以通过在编辑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();
            }
        });
    }
}

我认为您需要重写JTablegetColumnClass()方法,因为它可以访问正在编辑的行,而TableModel没有访问权限。

这是我添加到PropertySheet类的代码:

@Override
public Class getColumnClass(int column)
{
    int row = getSelectedRow();

    if (row == -1) return super.getColumnClass( column );

    PropertySheetDelegate controller = getPropertySheetDelegate();

    if (controller == null || column == 0)
    {
        return super.getColumnClass( column );
    }
    else
    {
        PropertySheetModel model = (PropertySheetModel) getModel();
        Property property = model.getPropertyAt(row);
        return property.getType();
    }
}

虽然我确定您会找到一种更好的方法来实现它:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM