提问者:小点点

jcombobox 中的复选框:从不选中


我有一个关于 JCombobox 的问题,其中包含一些 JCheckBoxes,在 java swing 中。问题是复选框永远不会被选中,因为渲染器似乎只接收未选择的值;但该模型确实包含检查值,我用调试器对其进行了验证。我不知道问题出在哪里。

这是代码:

主类构造函数的一部分:

cbb_keywords = new JComboBox();
cbb_keywords.setName("cbb_keywords");
cbb_keywords.addActionListener(this);
cbb_keywords.setMaximumRowCount(5);
cbb_keywords.setRenderer(new CkbKeywordsRenderer(""));
cbbmodel = new DefaultComboBoxModel<CkbKeywordsRenderer>();
cbb_keywords.setModel(cbbmodel);
cbb_keywords.setEditable(true);

应该触发在 JCombobox 中显示某些 JCheckbox 的代码:

public void setKeywords(Keywords keywords) {
    txf_keywords.setText(keywords.toString());

    DefaultComboBoxModel model = extractComboboxModel();
    for (int i = 0; i < model.getSize(); i++) {
      CkbKeywordsRenderer ckbrenderer =
              new CkbKeywordsRenderer(
                      ((CkbKeywordsRenderer) model.getElementAt(i))
                              .getText());
      if (keywords.contains(ckbrenderer.getText()))
        ckbrenderer.setSelected(true);
      else
        ckbrenderer.setSelected(false);

    }
    cbb_keywords.setModel(model);
}

此方法可能需要一些解释:* 首先,“模型”填充了存储在 JTable 中的全部关键字;每行对应一本书,每本书包含一个关键字列表。“模型”的填充从收集所有关键字开始,然后继续删除双精度。使用我的调试器,我看到这个字段包含 4 个关键字:“冒险”、“婕斯”、“数学”、“philo”。* 然后,我测试模型的每个关键字,看看它是否在本书的关键字列表中(变量“关键字”)(分别是:“aventure”和“jeunesse”)。因此,在组合框的列表中,我LD有4个项目,其中2个被选中:“冒险”和“婕斯”)。如果我在JTable中选择另一本书,则应在JCombobox中选中其他2个关键字。* 此方法经过测试并返回一个有效的模型:4 个项目,其中 2 个被“选中”

现在这里是渲染器类,它永远不会收到检查值:

 public class CkbKeywordsRenderer extends JCheckBox implements ListCellRenderer,
        ActionListener {

  static CkbKeywordsRenderer[] elements;

  public CkbKeywordsRenderer(String text) {
    super(text);

  }

  @Override
  public Component getListCellRendererComponent(JList list, Object value, int index,
          boolean isSelected, boolean cellHasFocus) {

    CkbKeywordsRenderer selectedItem = (CkbKeywordsRenderer) value;

    if (isSelected) {
      setBackground(list.getSelectionBackground());
      setForeground(list.getSelectionForeground());
    } else {
      setBackground(list.getBackground());
      setForeground(list.getForeground());
    }
    if (selectedItem != null) {

      setText(selectedItem.getText());
      setSelected(selectedItem.isSelected());
    } else if (index == -1 && value == null) setText("abc");
    return this;
  }


  @Override
  public void actionPerformed(ActionEvent e) {
    JComboBox cb = (JComboBox) e.getSource();
    CkbKeywordsRenderer jcheckBox = (CkbKeywordsRenderer) cb.getSelectedItem();
    jcheckBox.setSelected(!jcheckBox.isSelected());
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        cb.showPopup();
      }
    });

  }

你看到问题出在哪里了吗?谢谢

编辑:没有答案...我试图调整我的程序(模型中没有 JCOmponent),但渲染器仍然得到未经检查的值:

请看一下这 2 张图片,它们很好地总结了我的问题:

顺便说一下,这是值的类(非常简单):

public class ItemCombobox {

  public String itemName;
  public boolean checked;

  public ItemCombobox(String itemName, boolean checked) {
    this.itemName = itemName;
    this.checked = checked;
  }


  @Override
  public String toString() {
    return itemName;
  }
}

渲染器的新代码在第一张图片中给出。

谢谢。


共1个答案

匿名用户

Swing 基于“模型-视图-控制器”范式。这要求数据与视图之间的分离。

这意味着需要呈现/显示的信息和数据片段与数据本身分离。这意味着数据可以用多种独立的方式表示,具体取决于您尝试使用的内容。

这意味着,模型应该只携带数据。它永远不应该携带任何类型的 UI 元素。

Swing 还使用“委派”范例来允许您自定义呈现不同元素的 UI 组件数量。首先看看概念:编辑器和渲染器 - 这是一个非常重要的概念,因为它几乎在任何地方都使用。

您还应该查看如何使用列表,编写自定义单元格呈现器以获取更具体的详细信息。

这是一个非常基本的示例,它使用ItemCombobox作为基本构建块来生成JList,该JList使用JCheckBox作为基本渲染器显示项目

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                DefaultListModel<Item> itemListModel = new DefaultListModel<>();
                itemListModel.addElement(new Item("A", false));
                itemListModel.addElement(new Item("B", true));
                itemListModel.addElement(new Item("C", false));
                itemListModel.addElement(new Item("D", false));
                itemListModel.addElement(new Item("E", true));
                itemListModel.addElement(new Item("F", true));
                itemListModel.addElement(new Item("G", false));
                itemListModel.addElement(new Item("H", true));

                JList list = new JList(itemListModel);
                list.setCellRenderer(new CheckBoxListCellRenderer());

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(list));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Item {

        public String itemName;
        public boolean checked;

        public Item(String itemName, boolean checked) {
            this.itemName = itemName;
            this.checked = checked;
        }

        public boolean isChecked() {
            return checked;
        }

        public String getItemName() {
            return itemName;
        }

        @Override
        public String toString() {
            return itemName;
        }
    }

    public static class CheckBoxListCellRenderer extends JCheckBox implements ListCellRenderer<Item> {

        private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);

        public CheckBoxListCellRenderer() {
            setOpaque(false);
            setBorder(DEFAULT_NO_FOCUS_BORDER);
        }

        @Override
        public Component getListCellRendererComponent(JList<? extends Item> list, Item value, int index, boolean isSelected, boolean cellHasFocus) {
            setSelected(value.isChecked());
            setText(value.getItemName());
            Color fg = list.getForeground();
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                fg = list.getSelectionForeground();
            } else {
                setBackground(list.getBackground());
            }
            setForeground(fg);
            setOpaque(isSelected);
            Border border = null;
            if (cellHasFocus) {
                if (isSelected) {
                    border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
                }
                if (border == null) {
                    border = UIManager.getBorder("List.focusCellHighlightBorder");
                }
            } else {
                border = DEFAULT_NO_FOCUS_BORDER;
            }
            setBorder(border);
            return this;
        }

    }

}

但是,如果列表中的项目发生更改,如何更新它?

通常,我会提供一个自定义列表模型来处理这个问题,但是,您可以使用 DefaultListModelsetElementAt 来触发列表以重新呈现指定的项。

此示例仅添加一个按钮,该按钮在触发时将更改所选项的选定状态(如果未选定项,则更改第一项)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                DefaultListModel<Item> itemListModel = new DefaultListModel<>();
                itemListModel.addElement(new Item("A", false));
                itemListModel.addElement(new Item("B", true));
                itemListModel.addElement(new Item("C", false));
                itemListModel.addElement(new Item("D", false));
                itemListModel.addElement(new Item("E", true));
                itemListModel.addElement(new Item("F", true));
                itemListModel.addElement(new Item("G", false));
                itemListModel.addElement(new Item("H", true));

                JList list = new JList(itemListModel);
                list.setCellRenderer(new CheckBoxListCellRenderer());

                JButton change = new JButton("Change");
                change.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        int index = list.getSelectedIndex();
                        if (index == -1) {
                            index = 0;
                        }
                        Item item = itemListModel.get(index);
                        item.setChecked(!item.isChecked());

                        // Force an update of the specified element
                        itemListModel.setElementAt(item, index);
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(list));
                frame.add(change, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Item {

        public String itemName;
        public boolean checked;

        public Item(String itemName, boolean checked) {
            this.itemName = itemName;
            this.checked = checked;
        }

        public void setChecked(boolean checked) {
            this.checked = checked;
        }

        public boolean isChecked() {
            return checked;
        }

        public String getItemName() {
            return itemName;
        }

        @Override
        public String toString() {
            return itemName;
        }
    }

    public static class CheckBoxListCellRenderer extends JCheckBox implements ListCellRenderer<Item> {

        private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);

        public CheckBoxListCellRenderer() {
            setOpaque(false);
            setBorder(DEFAULT_NO_FOCUS_BORDER);
        }

        @Override
        public Component getListCellRendererComponent(JList<? extends Item> list, Item value, int index, boolean isSelected, boolean cellHasFocus) {
            setSelected(value.isChecked());
            setText(value.getItemName());
            Color fg = list.getForeground();
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                fg = list.getSelectionForeground();
            } else {
                setBackground(list.getBackground());
            }
            setForeground(fg);
            setOpaque(isSelected);
            Border border = null;
            if (cellHasFocus) {
                if (isSelected) {
                    border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
                }
                if (border == null) {
                    border = UIManager.getBorder("List.focusCellHighlightBorder");
                }
            } else {
                border = DEFAULT_NO_FOCUS_BORDER;
            }
            setBorder(border);
            return this;
        }

    }

}

但是如何允许用户从 JList 中选择/取消选择项目?

对我来说,这就是使用JList进行此类任务的概念开始分崩离析的地方,因为JList通常不是可编辑的。对我来说,我更喜欢基于 JTable 的解决方案,但是......

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                DefaultListModel<Item> itemListModel = new DefaultListModel<>();
                itemListModel.addElement(new Item("A", false));
                itemListModel.addElement(new Item("B", true));
                itemListModel.addElement(new Item("C", false));
                itemListModel.addElement(new Item("D", false));
                itemListModel.addElement(new Item("E", true));
                itemListModel.addElement(new Item("F", true));
                itemListModel.addElement(new Item("G", false));
                itemListModel.addElement(new Item("H", true));

                JList list = new JList(itemListModel);
                list.setCellRenderer(new CheckBoxListCellRenderer());

                JButton change = new JButton("Change");
                change.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        int index = list.getSelectedIndex();
                        if (index == -1) {
                            index = 0;
                        }
                        Item item = itemListModel.get(index);
                        item.setChecked(!item.isChecked());

                        // Force an update of the specified element
                        itemListModel.setElementAt(item, index);
                    }
                });

                list.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        int index = list.locationToIndex(e.getPoint());
                        if (index < 0) {
                            return;
                        }
                        Item item = itemListModel.get(index);
                        item.setChecked(!item.isChecked());

                        // Force an update of the specified element
                        itemListModel.setElementAt(item, index);
                    }                   
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(list));
                frame.add(change, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Item {

        public String itemName;
        public boolean checked;

        public Item(String itemName, boolean checked) {
            this.itemName = itemName;
            this.checked = checked;
        }

        public void setChecked(boolean checked) {
            this.checked = checked;
        }

        public boolean isChecked() {
            return checked;
        }

        public String getItemName() {
            return itemName;
        }

        @Override
        public String toString() {
            return itemName;
        }
    }

    public static class CheckBoxListCellRenderer extends JCheckBox implements ListCellRenderer<Item> {

        private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);

        public CheckBoxListCellRenderer() {
            setOpaque(false);
            setBorder(DEFAULT_NO_FOCUS_BORDER);
        }

        @Override
        public Component getListCellRendererComponent(JList<? extends Item> list, Item value, int index, boolean isSelected, boolean cellHasFocus) {
            setSelected(value.isChecked());
            setText(value.getItemName());
            Color fg = list.getForeground();
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                fg = list.getSelectionForeground();
            } else {
                setBackground(list.getBackground());
            }
            setForeground(fg);
            setOpaque(isSelected);
            Border border = null;
            if (cellHasFocus) {
                if (isSelected) {
                    border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
                }
                if (border == null) {
                    border = UIManager.getBorder("List.focusCellHighlightBorder");
                }
            } else {
                border = DEFAULT_NO_FOCUS_BORDER;
            }
            setBorder(border);
            return this;
        }

    }

}

此示例只是将鼠标侦听器添加到 JList,并更改所单击项的选定状态。

这有许多直接的缺点:

  • 如果用户只想选择一个项目怎么办?现在必须双击该项目才能重置状态
  • 没有键盘交互。您可以使用键绑定 API 来添加支持,但它并不直观,现在您无论如何都可以使用基于 JTable 的解决方案免费实现功能
  • 一般来说,它并不直观,因为JList并不意味着是可编辑的,所以大多数用户不会有这种期望/心态,这可能会给用户带来挫败感,因为他们的预期结果不符合你的要求。拉姆现在正在做。

在这一点上,我想说你已经超出了JList的预期功能,通过使用JTable可以更好地管理这些功能。