问题
I've got a 3 column JTable. Column 2 is a checkbox which I want to enable/disable the JSpinner for that row.
I've got working how I want except for one thing -- The JSpinner doesn't actually look like its disabled (text and spinner buttons greyed out). I'm not quite sure how to attain this. I've tried forcibly calling setEnabled(false) on the JSpinner, but the table doesn't seem to redraw correctly.
Here is some code I've gotten working through other StackOverflow examples:
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.WindowConstants;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class SpinnerTable {
public JComponent makeUI() {
String[] columnNames = { "Name", "Spinner Enable", "Spinner" };
final Object[][] data = { { "aaa", true, 1 }, { "bbb", true, 10 },
{ "ccc", true, 10 } };
final DefaultTableModel model = new DefaultTableModel(data, columnNames) {
@Override
public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
};
JTable table = new JTable(model) {
@Override
public void setValueAt(Object aValue, int row, int column) {
super.setValueAt(aValue, row, column);
}
@Override
public boolean isCellEditable(int row, int column) {
if (column == 2)
return (Boolean) model.getValueAt(row, 1);
return super.isCellEditable(row, column);
}
};
table.setRowHeight(36);
table.setAutoCreateRowSorter(true);
TableColumn column = table.getColumnModel().getColumn(2);
column.setCellRenderer(new ComboBoxCellRenderer());
column.setCellEditor(new ComboBoxCellEditor());
return new JScrollPane(table);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new SpinnerTable().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class SpinnerPanel extends JPanel {
protected JSpinner spinner = new JSpinner() {
@Override
public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
return new Dimension(40, d.height);
}
};
public SpinnerPanel() {
super();
setOpaque(true);
add(spinner);
}
}
class ComboBoxCellRenderer extends SpinnerPanel implements TableCellRenderer {
public ComboBoxCellRenderer() {
super();
setName("Table.cellRenderer");
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
setBackground(isSelected ? table.getSelectionBackground() : table
.getBackground());
if (value != null) {
spinner.setValue(value);
}
return this;
}
}
class ComboBoxCellEditor extends SpinnerPanel implements TableCellEditor {
public ComboBoxCellEditor() {
super();
spinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
fireEditingStopped();
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
fireEditingStopped();
}
});
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
this.setBackground(table.getSelectionBackground());
spinner.setValue(value);
return this;
}
// Copid from DefaultCellEditor.EditorDelegate
@Override
public Object getCellEditorValue() {
return spinner.getValue();
}
@Override
public boolean shouldSelectCell(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
MouseEvent e = (MouseEvent) anEvent;
return e.getID() != MouseEvent.MOUSE_DRAGGED;
}
return true;
}
@Override
public boolean stopCellEditing() {
fireEditingStopped();
return true;
};
transient protected ChangeEvent changeEvent = null;
@Override
public boolean isCellEditable(EventObject e) {
return true;
}
@Override
public void cancelCellEditing() {
fireEditingCanceled();
}
@Override
public void addCellEditorListener(CellEditorListener l) {
listenerList.add(CellEditorListener.class, l);
}
@Override
public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(CellEditorListener.class, l);
}
public CellEditorListener[] getCellEditorListeners() {
return listenerList.getListeners(CellEditorListener.class);
}
protected void fireEditingStopped() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == CellEditorListener.class) {
// Lazily create the event:
if (changeEvent == null)
changeEvent = new ChangeEvent(this);
((CellEditorListener) listeners[i + 1])
.editingStopped(changeEvent);
}
}
}
protected void fireEditingCanceled() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == CellEditorListener.class) {
// Lazily create the event:
if (changeEvent == null)
changeEvent = new ChangeEvent(this);
((CellEditorListener) listeners[i + 1])
.editingCanceled(changeEvent);
}
}
}
}
回答1:
The table does not know it should repaint the cell in column 2 when column 1 was modified. You can notify the table by firing an update manually. For example, extend model's setValueAt()
:
@Override
public void setValueAt(Object aValue, int row, int column) {
super.setValueAt(aValue, row, column);
if (column == 1)
fireTableRowsUpdated(row, row);
}
This will disable the editor and spinner will become not editable. If you need to actually disable the spinner visually, then, inside the renderer you can enable/disable the spinner based on isCellEditable
, ie:
spinner.setEnabled(table.isCellEditable(row, column));
Note that in your current implementation you extend JTable
to implement isCellEditable
and setValueAt
. These should really be part of the model.
来源:https://stackoverflow.com/questions/22134138/jtable-with-jspinner-enable-disable