jcombobox中的复选框:从未选中

时间:2017-12-29 21:08:35

标签: java swing jcombobox

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

这是代码:

主类构造函数的一部分:

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个关键字:“aventure”,“jeunesse”,“maths”,“philo”。 *然后,我测试模型的每个关键字,看它是否在书的关键词列表中(变量“关键词”)(“aventure”和“jeunesse”)。 因此,在组合框列表中,我应该有4个项目,其中2个被检查:“aventure”和“jeunesse”)。如果我在JTable中选择另一本书,则应在JCombobox中检查另外两个关键字。 *此方法已经过测试并返回了一个有效的模型:其中2个为“已选中”的4个项目

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

 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),但渲染器仍然没有得到未经检查的值:

请看看这两张图片,它们总结了我的问题:

here你可以看到填充模型的方法正确完成,检查了4个项目中的2个。

here您可以看到渲染器获得了错误的值。

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

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 个答案:

答案 0 :(得分:1)

Swing基于“模型 - 视图 - 控制器”范例。这决定了数据和视图之间的分离。

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

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

Swing还使用“委托”范例,允许您自定义有多少UI组件呈现不同的元素。首先来看看Concepts: Editors and Renderers - 这是一个非常重要的概念,因为几乎每个地方都使用它。

您还应该查看How to use lists, writing a custom cell renderer以获取更具体的详细信息。

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

A Checkbox ListCellRenderer

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;
        }

    }

}
  

但如果更改了列表中的项目,该如何更新呢?

通常情况下,我会提供自定义ListModel来处理此问题,但是,您可以使用DefaultListModel的{​​{1}}来触发列表以重新呈现指定的项目。

这个例子只是添加一个按钮,当触发时,它将改变所选项目的选定状态(如果没有选择一个,则改变第一个项目)

setElementAt
  

但我如何允许用户从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通常不是可编辑的。对我来说,I'd prefer a JTable based solution instead,但是......

JList

此示例只是向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; } } } 添加了MouseListener,并更改了所点击项目的选定状态。

这有很多直接的缺点:

  • 如果用户只想选择项目怎么办?现在必须双击项目以重置状态
  • 没有键盘交互。您可以使用键绑定API来添加支持,但它并不直观,现在您无需使用基于JList的解决方案即可实现免费获得的功能
  • 一般来说,它并不直观,因为JTable并不是可编辑的,因此大多数用户不会有这种期望/心态,并且可能会导致用户感到沮丧,因为他们的预期结果不符合你的程序现在在做什么。

此时,我说您已经超出了JList的预期功能,使用JList可以更好地管理这些功能