[英]JTable column uneditable after adding JComboBox to a cell
我有一个包含2列的JTable组件,对于第二列,我想仅为一个单元格添加一个JComboBox,在oracle文档之后,我创建了自己的单元格编辑器,并添加了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
不能真正用作属性表编辑器,因为每行的单元格值都不同
对于任何类型的实现,可重用性和可配置性都是我的两个主要问题。 现在实际上是表格的责任来决定应如何渲染或编辑事物,这就是拥有渲染器和编辑器的原因。 我们需要某种方式在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
(显然),它使用PropertySheetDelegate
和PropertySheetModel
来显示已管理的数据...
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));
使用新的更新使使用默认渲染器和编辑器更加容易。
但是,在JTable
, GenericEditor
使用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();
}
});
}
}
我认为您需要重写JTable
的getColumnClass()
方法,因为它可以访问正在编辑的行,而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.