JTable与JSpinner启用/禁用

3
我有一个3列的JTable。第二列是复选框,我希望能够启用/禁用该行的JSpinner。
我已经实现了我想要的功能,但是有一个问题——JSpinner看起来并没有被禁用(文本和微调按钮没有变灰)。我不确定如何解决这个问题。我尝试强制在JSpinner上调用setEnabled(false)方法,但表格似乎没有正确地重新绘制。
这里是一些通过其他StackOverflow示例得到的代码:
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个回答

4
setValueAt()
不知道当第一列被修改时应该重新绘制第二列的单元格。您可以通过手动触发更新来通知表格。例如,扩展模型的:
@Override
public void setValueAt(Object aValue, int row, int column) {
    super.setValueAt(aValue, row, column);
    if (column == 1)
        fireTableRowsUpdated(row, row);
}

这将禁用编辑器,使旋转器不可编辑。如果您需要实际上在视觉上禁用旋转器,那么在渲染器内,您可以根据 isCellEditable 启用/禁用旋转器:

spinner.setEnabled(table.isCellEditable(row, column));

请注意,您目前的实现是通过扩展JTable来实现isCellEditablesetValueAt。这些应该真正成为模型的一部分。

不需要调用 fireTableRowsUpdated(row, row);,因为它与 DefaultTableModel 无关,这个通知器会被调用两次。 - mKorbel
将JTable中的所有内容、所有代码行移动到DefaultTableModel中。 - mKorbel
将简单的旋转器放到渲染器中,JPanel也是无用的,或者有另一种选项将所有JComponents放入JPanel中,然后将有一个具有一列的JTable。 - mKorbel
1
:-) 这是一个不太正规的解决方案,现在我明白了。难道不更好地调用setValueAt(getValueAt),以实现对呈现的JComponents进行真正的刷新吗? - mKorbel
1
@mKorbel 我同意,setValueAt(getValueAt) 更加简洁。 - tenorsax
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接