【问题标题】:JComboBox as Jtable CellEditor with Overriden stopCellEditing modifies wrong table cellJComboBox 作为具有覆盖 stopCellEditing 的 Jtable CellEditor 修改错误的表格单元格
【发布时间】:2013-11-27 11:28:59
【问题描述】:

我有一个带有自定义 TableModel 的自定义 JTable,使用 JComboBox 作为单元格编辑器。 ComboBox 也有一个自定义的 ComboBoxModel ComboBox 模型包含多个字段,这些字段将用于更新 JTable 后面的数据,然后更新数据库。

下面是一个简单的例子来说明我遇到的问题。重现步骤:

  1. 点击一个单元格
  2. 从组合框下拉列表中选择一个值
  3. 点击不同的单元格
  4. 返回第一个选定的单元格

第二个单元格将从第一个单元格中获取值。

为什么会这样?为什么 ComboBox 模型会在 stopCellEditing 存在之前发生变化?

import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class TestComboCellEditor {

    public static void main(String[] args) {

        TestComboCellEditor test = new TestComboCellEditor();
        test.go();
    }

    public void go() {

        //create the frame
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // create and add a tabbed pane to the frame
        JTabbedPane tabbedPane = new JTabbedPane();
        frame.getContentPane().add(tabbedPane);
        //create a table and add it to a scroll pane in a new tab
        final JTable table = new JTable(new DefaultTableModel(new Object[]{"A", "B"}, 5));
        JScrollPane scrollPane = new JScrollPane(table);
        tabbedPane.addTab("test", scrollPane);

        // create a simple JComboBox and set is as table cell editor on column A
        Object[] comboElements = {"aaaaa1", "aaaaaa2", "b"};
        final JComboBox comboBox = new JComboBox(comboElements);
        comboBox.setEditable(true);
        table.getColumn("A").setCellEditor(new DefaultCellEditor(comboBox) {
            @Override
            public boolean stopCellEditing() {
                if (comboBox.isEditable()) {
                    DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
                    String selectedItem = (String) comboModel.getSelectedItem();
                    int selectedIndex = comboModel.getIndexOf(selectedItem);
                    if (!(selectedIndex == -1)) {
                        // the selected item exists as an Option inside the ComboBox
                        DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
                        int selectedRow = table.getSelectedRow();
                        int selectedColumn = table.getSelectedColumn();
                        tableModel.setValueAt(selectedItem, selectedRow, selectedColumn);
                    } else if (selectedItem != null) {
                        // missing code - adding new info to a custom JComboBox model and to alter info inside a custom table model
                    }
                }
                return super.stopCellEditing();
            }
        });

        // pack and show frame
        frame.pack();
        frame.setVisible(true);

    }
}

【问题讨论】:

  • 实现无效:编辑器不得更改调用它的视图/模型 - 它的唯一职责是在编辑完成时通知并保持编辑后的值 (供其客户访问)
  • 我的第一次尝试是这样的:stackoverflow.com/questions/19938204/… 但遇到了其他问题,所以我被推荐这样做。我一直在寻找最佳实践来实现我想要的东西,但找不到可以使用的东西。如果您可以指向一个带有示例的链接,那就太好了。

标签: java swing combobox jtable tablecelleditor


【解决方案1】:

这是一种将所有代码保留在编辑器中的方法:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class TestComboCellEditor {

    public static void main(String[] args) {

        TestComboCellEditor test = new TestComboCellEditor();
        test.go();
    }

    public void go() {

        //create the frame
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // create and add a tabbed pane to the frame
        JTabbedPane tabbedPane = new JTabbedPane();
        frame.getContentPane().add(tabbedPane);
        //create a table and add it to a scroll pane in a new tab
        final JTable table = new JTable(new DefaultTableModel(new Object[]{"A", "B"}, 5));
        JScrollPane scrollPane = new JScrollPane(table);
        tabbedPane.addTab("test", scrollPane);

        // create a simple JComboBox and set is as table cell editor on column A
        Object[] comboElements = {"aaaaa1", "aaaaaa2", "b"};
        final JComboBox comboBox = new JComboBox(comboElements);
        comboBox.setEditable(true);
        table.getColumn("A").setCellEditor(new DefaultCellEditor(comboBox)
        {
            private Object originalValue;

            @Override
            public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
            {
                originalValue = value;
                return super.getTableCellEditorComponent(table, value, isSelected, row, column);
            }

            @Override
            public boolean stopCellEditing()
            {
                JComboBox comboBox = (JComboBox)getComponent();
                DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
                Object editingValue = getCellEditorValue();

                //  Needed because your TableModel is empty

                if (editingValue == null)
                    return super.stopCellEditing();

                int selectedIndex = comboModel.getIndexOf(editingValue);

                //  Selecting item from model

                if (! (selectedIndex == -1))
                    return super.stopCellEditing();

                //  Confirm addition of new value

                int result = JOptionPane.showConfirmDialog(
                    comboBox.getParent(),
                    "Add (" + editingValue + ") to table?",
                    "Update Model",
                    JOptionPane.YES_NO_OPTION);

                if (result == JOptionPane.YES_OPTION)
                {
                    comboBox.addItem(editingValue);
                    return super.stopCellEditing();
                }
                else
                {
                    comboBox.removeItem(editingValue);
                     comboBox.setSelectedItem(originalValue);
                    return false;
                }
            }
        });

        // pack and show frame
        frame.pack();
        frame.setVisible(true);

    }
}

【讨论】:

  • 这很接近(到目前为止+1!)-除了a)(猜测一下)当需要继续编辑时重置原始值b)处理更复杂的对象。如果在 else 块中没有 setSelectedItem(original),则选项窗格会显示两次:罪魁祸首是有点奇怪的 ui/combo 行为,它会触发带有“comboBoxEdited”的人工 actionEvent ..
  • 我真的不想要编辑器中的所有代码。我只是这样尝试,因为我找不到更好的解决方案。我正在寻找的是一种使代码易于维护的良好做法。我目前正在尝试使用自定义渲染器的解决方案,以仅从自定义模型中提取我想要显示的信息,并在 cellEditor 中使用对话框(如您所建议的那样)。这样,当 ComboBox 将使用 JTableModel 所持有的相同数据结构时,将在您选择它时直接传递它。 JTable 将使用类似的渲染器。让我知道你对此的看法。
  • 我想我不明白你的要求。我回答了你原来的问题。
  • 你是对的,你的解决方案确实回答了我原来的问题。 :) 在与 kleopatra 讨论我在下面的答案时,我对我正在寻找的实现(在 cmets 中)提出了更广泛的看法。不过,您的解决方案就是我最初问题的答案。
  • kleopatra 关于选项窗格显示两次的事实是正确的。重现如下:运行程序;在第一次编辑单元格时添加一个未包含在列表中的值。无法弄清楚它为什么这样做。我添加了 setSelectedItem(originalValue),但现在它不保持单元格编辑,只是失去焦点。
【解决方案2】:

好的,我进行了一些更改,并且我认为我有一些工作。如果这不是最佳实践并且您获得了更好的实施,请发布答案。

编辑:不要按照下面的例子!在 kleopatra 的 cmets 之后,这是一个错误的实现。我把它留在这里,所以你知道如何做到这一点。

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class TestComboCellEditor {

    public static void main(String[] args) {

        TestComboCellEditor test = new TestComboCellEditor();
        test.go();
    }

    public void go() {

        //create the frame
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // create and add a tabbed pane to the frame
        JTabbedPane tabbedPane = new JTabbedPane();
        frame.getContentPane().add(tabbedPane);
        //create a table and add it to a scroll pane in a new tab
        final JTable table = new JTable(new DefaultTableModel(new Object[]{"A", "B"}, 5));
        JScrollPane scrollPane = new JScrollPane(table);
        tabbedPane.addTab("test", scrollPane);

        // create a simple JComboBox and set is as table cell editor on column A
        Object[] comboElements = {"aaaaa1", "aaaaaa2", "b"};
        final JComboBox comboBox = new JComboBox(comboElements);
        comboBox.setEditable(true);
        table.getColumn("A").setCellEditor(new DefaultCellEditor(comboBox) {
            @Override
            public boolean stopCellEditing() {
                if (comboBox.isEditable()) {
                    DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
                    String selectedItem = (String) comboModel.getSelectedItem();
                    int selectedIndex = comboModel.getIndexOf(selectedItem);
                    if (!(selectedIndex == -1)) {
                        comboBox.actionPerformed(new ActionEvent(this, selectedIndex, "blabla"));
                    } else if (selectedItem != null) {
                        // missing code - adding new info to a custom JComboBox model and to alter info inside a custom table model
                    }
                }
                return super.stopCellEditing();
            }
        });

        comboBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // the selected item exists as an Option inside the ComboBox
                if (e.getActionCommand().equals("blabla")) {
                    DefaultComboBoxModel comboModel = (DefaultComboBoxModel) comboBox.getModel();
                    String selectedItem = (String) comboModel.getSelectedItem();
                    DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
                    int selectedRow = table.getSelectedRow();
                    int selectedColumn = table.getSelectedColumn();
                    tableModel.setValueAt(selectedItem, selectedRow, selectedColumn);
                }
            }
        });

        // pack and show frame
        frame.pack();
        frame.setVisible(true);

    }
}

【讨论】:

  • 不,这是错误的 - 不要干扰 JTable 内部更新,无论是在编辑器中还是在侦听器中。
  • 感谢您的提示。编辑完成后我应该从哪里进行更新?
  • 我仍然不太明白为什么这会帮助您的 real 问题 - 您想静默添加新项目(到组合模型中)并将其用作表的模型或两者都不做,让用户再次选择,对吧?
  • 我希望当用户在组合框中填写一个不在列表中的值时,弹出一个对话框,询问他们是否要将此值添加到列表中(我删除了对话框从这个例子中,因为我只想专注于模型)。如果他们不这样做,我想返回编辑组合框。此外,在组合框中选择了一个值后,我想更新表格模型,而不仅仅是单元格值,因为组合框模型包含有关该值的其他信息(如数据库的 id 等)并且我想通过将其添加到表模型中,以便之后我可以使用它来更新数据库。
  • 关于不仅仅是单元格值 - 这闻起来像是一个次优的数据模型:让单元格包含完整的对象(显示值以及其他信息)并使用自定义渲染器/编辑。
猜你喜欢
  • 2015-03-07
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
  • 1970-01-01
  • 2021-06-20
  • 2020-01-02
  • 1970-01-01
相关资源
最近更新 更多