1

在下面的示例中,我有一个JTableaJList和两个JButtons(添加和删除)。当单击添加按钮时,列表中有 6 个项目(字符串),选定的值将添加到表中。
表中的字符串使用自定义渲染器(JPanel带有按钮和标签)显示。按钮的文本和标签的文本被更改为 String 的值。
一切顺利,直到编辑进入。编辑器使单击按钮成为可能,因此这是必要的。
当第一次将字符串添加到表中时,它会正确显示,行的高度会调整为面板的首选高度,并为按钮和标签设置文本。
当通过单击行然后单击删除按钮从表中删除条目时,一切都按预期进行。
现在问题来了:如果向表中添加一个(不同的)字符串,则行高是并且标签和按钮的文本没有设置(因为渲染器和编辑器都没有被调用,我已经检查过使用断点)。
当然,我确实希望使用自定义渲染器显示新行,但我该怎么做呢?

package test;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.EventObject;
import javax.swing.AbstractAction;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.event.CellEditorListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class MainForm
        extends javax.swing.JFrame
{
    private JTable table;
    private JScrollPane tableScrollPane;
    private JList list;
    private JScrollPane listScrollPane;
    private JButton add;
    private JButton remove;

    public MainForm()
    {
        tableScrollPane = new JScrollPane();
        table = new JTable();
        listScrollPane = new JScrollPane();
        list = new JList();
        add = new JButton(new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                add();
            }
        });
        add.setText("add");
        remove = new JButton(new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                remove();
            }
        });
        remove.setText("remove");

        setLayout(new javax.swing.BoxLayout(getContentPane(), javax.swing.BoxLayout.LINE_AXIS));

        tableScrollPane.setViewportView(table);
        listScrollPane.setViewportView(list);

        add(tableScrollPane);

        DefaultTableModel model = new DefaultTableModel();
        model.addColumn("test");
        table.setModel(model);

        TableColumn col = table.getColumn("test");
        col.setCellRenderer(new CustomTableCellRenderer());
        col.setCellEditor(new CustomTableCellEditor());

        DefaultListModel listModel = new DefaultListModel();
        listModel.addElement("test1");
        listModel.addElement("test2");
        listModel.addElement("test3");
        listModel.addElement("test4");
        listModel.addElement("test5");
        listModel.addElement("test6");
        list.setModel(listModel);

        add(listScrollPane);
        add(add);
        add(remove);
    }

    private void add()
    {
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        model.addRow(new Object[]
                {
                    list.getSelectedValue()
                });
    }

    private void remove()
    {
        int selectedRow = table.getSelectedRow();
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        model.removeRow(selectedRow);
    }

    public static void main(String[] args)
    {
        new MainForm().setVisible(true);
    }

    public class CustomTableCellRenderer
            extends customPanel
            implements TableCellRenderer
    {
        public Component getTableCellRendererComponent(JTable table,
                                                       Object value,
                                                       boolean isSelected,
                                                       boolean hasFocus, int row,
                                                       int column)
        {
            setText((String) value);
            if (isSelected || hasFocus)
            {
                setBackground(UIManager.getColor("List.selectionBackground"));
                setForeground(UIManager.getColor("List.selectionForeground"));
            }
            else
            {
                setBackground(UIManager.getColor("Panel.background"));
                setForeground(UIManager.getColor("Panel.foreground"));
            }
            table.setRowHeight(row, (int)getPreferredSize().height);
            return this;
        }
    }

    public class CustomTableCellEditor
            extends customPanel
            implements TableCellEditor
    {
        Object value;

        public Component getTableCellEditorComponent(JTable table, Object value,
                                                     boolean isSelected, int row,
                                                     int column)
        {
            this.value = value;
            setText((String) value);
            setBackground(UIManager.getColor("List.selectionBackground"));
            setForeground(UIManager.getColor("List.selectionForeground"));
            table.setRowHeight(row, (int)getPreferredSize().height);
            return this;
        }

        public Object getCellEditorValue()
        {
            return value;
        }

        public boolean isCellEditable(EventObject anEvent)
        {
            return true;
        }

        public boolean shouldSelectCell(EventObject anEvent)
        {
            return true;
        }

        public boolean stopCellEditing()
        {
            setBackground(UIManager.getColor("Panel.background"));
            setForeground(UIManager.getColor("Panel.foreground"));
            return true;
        }

        public void cancelCellEditing()
        {
        }

        public void addCellEditorListener(CellEditorListener l)
        {
        }

        public void removeCellEditorListener(CellEditorListener l)
        {
        }
    }

    public class customPanel
            extends JPanel
    {
        private JLabel label;
        private JButton button;

        public customPanel()
        {
            label = new JLabel();
            button = new JButton();
            add(label);
            add(button);
        }

        public void setText(String text)
        {
            label.setText(text);
            button.setText(text);
        }
    }
}
4

2 回答 2

2

You should never use:

table.setRowHeight(row, (int)getPreferredSize().height);

in a renderer. This will cause an infinite loop since whenever you change the row height the table will repaint() the row and when the renderer is invoked you will again change the row height....

Get rid of that line of code. Instead you can calculate the row height when you add the row to the table. Add this code to the bottom of your add() method:

int row = table.getRowCount() - 1;
Component comp = table.prepareRenderer(table.getCellRenderer(row, 0), row, 0);
int rowHeight = comp.getPreferredSize().height;
table.setRowHeight(row, rowHeight);

Update:

The code works fine without the custom editor. So the problem is the editor. Look at the source code of the AbstractCellEditor to see what happens when editing is stopped. It fires an event to notify the table so the editor can be removed. You don't have this code. So I suggest you extend the AbstractCellEditor instead of extending customPanel so you can easilty fire the appropriate event.

Also, I believe that clicking on a row will invoke the editor, so you need to remove the editor before you remove the row from the model. See Table Stop Editing for a couple of ways to do this.

于 2011-12-05T20:57:37.760 回答
0

实现 addCellEditorListener()、removeCellEditorListener()、stopCellEditing() 和 cancelCellEditing() 方法就像一个魅力。在删除所选对象之前,需要“断开”编辑器。
在我的一个表格中,我有一个上下文菜单来修改表格条目,在删除之前我必须调用 cancelCellEditing() 或 stopCellEditing() 否则表格不会释放条目,或者它会像以前一样重新出现。

于 2011-12-06T12:19:17.300 回答