如何在Java Swing中制作带有复选框的列表?

35
什么是在Java Swing中拥有带有复选框的项目列表的最佳方法?即,具有一些文本和每个复选框的项目的JList?

您可以使用Japura API。其中有一个带有复选框的列表组件http://www.japura.org/checklist.html。 - user760034
那个 Japura API 的链接现在已经失效了。 - Mike Stoddart
11个回答

26

一个很棒的答案是这个CheckBoxList。它实现了Telcontar的答案(尽管早了三年 :) ...我在Java 1.6中使用它没有问题。我还添加了一个addCheckbox方法,像这样(肯定可以更短,好久没用Java了):

public void addCheckbox(JCheckBox checkBox) {
    ListModel currentList = this.getModel();
    JCheckBox[] newList = new JCheckBox[currentList.getSize() + 1];
    for (int i = 0; i < currentList.getSize(); i++) {
        newList[i] = (JCheckBox) currentList.getElementAt(i);
    }
    newList[newList.length - 1] = checkBox;
    setListData(newList);
}

我试用了Jidesoft的演示,玩弄CheckBoxList时遇到了一些问题(一些不起作用的行为)。如果我发现我链接的CheckBoxList也存在问题,我会修改这个答案。


我需要添加checkbox.setOpaque(true);以便背景可以被绘制(使用GTK+ L&F)。 - Sampo
1
如何为每个复选框添加监听器? - NumenorForLife
我正在使用这个,没有任何问题。我提到的另一件事有问题。 - Dan Rosenstark
你如何获取所有选定的行? - Hirad Roshandel
1
@HiradRoshandel 制作一个方法,遍历所有行并返回已选择的行。请参阅 http://docs.oracle.com/javase/7/docs/api/javax/swing/AbstractButton.html#isSelected()。 - Dan Rosenstark

17

创建一个自定义的ListCellRenderer并将其分配给JList

getListCellRendererComponent(...)方法的实现中,这个自定义的ListCellRenderer必须返回一个JCheckbox

但是这个JCheckbox是不可编辑的,在屏幕上只是简单的绘制。你可以选择何时将此JCheckbox打勾或取消勾选。

例如,当行被选中时(参数isSelected),显示它被勾选,但这样做会导致检查状态在选择更改时不会保持不变。最好根据ListModel下面的数据来显示已选择的项,然后由您来实现更改数据的选中状态的方法,并通知JList重新绘制以更新状态。

如果需要,我稍后会发布示例代码。

ListCellRenderer


15

只需实现一个ListCellRenderer即可

public class CheckboxListCellRenderer extends JCheckBox implements ListCellRenderer {

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

        setComponentOrientation(list.getComponentOrientation());
        setFont(list.getFont());
        setBackground(list.getBackground());
        setForeground(list.getForeground());
        setSelected(isSelected);
        setEnabled(list.isEnabled());

        setText(value == null ? "" : value.toString());  

        return this;
    }
}

并设置渲染器

JList list = new JList();
list.setCellRenderer(new CheckboxListCellRenderer());

这将导致:

CheckboxListCellRenderer example

详见自定义Swing组件渲染器

附:如果您想要单选框,请将extends JCheckbox替换为extends JRadioButton


2
我只能使用ctrl键来选择多个复选框。这是预期的吗?我想像普通复选框一样自由地选择任意数量。谢谢。 - arxakoulini

11

我可能会使用 JTable 而不是 JList,由于复选框的默认渲染方式相当丑陋,我可能会想要添加一个自定义的 TableModelCellRendererCellEditor 来表示布尔值。当然,我想象中这已经做了无数次了。Sun 公司提供了很好的示例


7

Java 7及以上的更好解决方案

我看到了这个问题并意识到一些答案已经过时了。现在,JList是通用的,因此有更好的解决方案。

我的通用JCheckBoxList解决方案:

import java.awt.Component;

import javax.swing.*;
import javax.swing.border.*;

import java.awt.event.*;

@SuppressWarnings("serial")
public class JCheckBoxList extends JList<JCheckBox> {
  protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

  public JCheckBoxList() {
    setCellRenderer(new CellRenderer());
    addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        int index = locationToIndex(e.getPoint());
        if (index != -1) {
          JCheckBox checkbox = (JCheckBox) getModel().getElementAt(index);
          checkbox.setSelected(!checkbox.isSelected());
          repaint();
        }
      }
    });
    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  }

  public JCheckBoxList(ListModel<JCheckBox> model){
    this();
    setModel(model);
  }

  protected class CellRenderer implements ListCellRenderer<JCheckBox> {
    public Component getListCellRendererComponent(
        JList<? extends JCheckBox> list, JCheckBox value, int index,
        boolean isSelected, boolean cellHasFocus) {
      JCheckBox checkbox = value;

      //Drawing checkbox, change the appearance here
      checkbox.setBackground(isSelected ? getSelectionBackground()
          : getBackground());
      checkbox.setForeground(isSelected ? getSelectionForeground()
          : getForeground());
      checkbox.setEnabled(isEnabled());
      checkbox.setFont(getFont());
      checkbox.setFocusPainted(false);
      checkbox.setBorderPainted(true);
      checkbox.setBorder(isSelected ? UIManager
          .getBorder("List.focusCellHighlightBorder") : noFocusBorder);
      return checkbox;
    }
  }
}

为了动态添加JCheckBox列表,您需要创建自己的ListModel或添加DefaultListModel。
DefaultListModel<JCheckBox> model = new DefaultListModel<JCheckBox>();
JCheckBoxList checkBoxList = new JCheckBoxList(model);

DefaultListModel是通用的,因此您可以使用JAVA 7 API这里指定的方法,例如:

model.addElement(new JCheckBox("Checkbox1"));
model.addElement(new JCheckBox("Checkbox2"));
model.addElement(new JCheckBox("Checkbox3"));

请问如何获取我的Jcheckboxlist中已选元素的列表?PS:(我将ListSelectionModel.SINGLE_SELECTION更改为ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) - Mehrez
我一直在尝试在NetBeans中让它工作,但是当我运行它时无法在屏幕上显示出来。有人能发布一个代码片段,展示如何将CheckBoxList添加到JPanel中吗? - Ed S
搞定了。我没有设置 CheckBoxList 的大小:'CheckBoxListYetAnother checkBoxListYetAnother = new CheckBoxListYetAnother(model); checkBoxListYetAnother.setSize(new Dimension(500, 200)); rtPanel.add(checkBoxListYetAnother);' - Ed S
我认为,如果您不需要管理大量复选框,则拥有一个充满复选框的模型是可以接受的。因此,正确的方法是仅使用渲染器并保留没有它们的模型。 - Jan Pešta
1
确实,模型应该只包含数据,而不是 UI 组件(基本上是回应 @JanPešta 的观点)。 - TT.

3

我建议你使用一个一列的GridLayout JPanel。将复选框添加到JPanel中,并将JPanel设置为JScrollPane的数据源。要获取所选的CheckBoxes,只需调用getComponents()方法来获取JPanel中的CheckBoxes。


2
我不喜欢将复选框放入模型的解决方案。模型应该只包含数据,而不是显示元素。
我找到了这个http://www.java2s.com/Tutorials/Java/Swing_How_to/JList/Create_JList_of_CheckBox.htm,并对其进行了优化。ACTIVE标志表示复选框,SELECTED标志显示光标所在的条目。
我的版本需要一个渲染器。
import java.awt.Component;

import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.ListCellRenderer;

class CheckListRenderer extends JCheckBox implements ListCellRenderer<Entity> {

    @Override
    public Component getListCellRendererComponent(JList<? extends Entity> list, 
        Entity value, int index, boolean isSelected, boolean cellHasFocus) {

            setEnabled(list.isEnabled());
            setSelected(value.isActive()); // sets the checkbox
            setFont(list.getFont());
        if (isSelected) { // highlights the currently selected entry
            setBackground(list.getSelectionBackground());
            setForeground(list.getSelectionForeground());
        } else {
            setBackground(list.getBackground());
            setForeground(list.getForeground());
        }
        setText(value.toString()+" - A" + value.isActive()+" - F"+cellHasFocus+" - S"+isSelected );
        return this;
    }

}

一个获得了活跃领域的实体:
public class Entity {

    private boolean active = true;

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean isActive) {
        this.active = isActive;
    }

}

现在你只需要将这个添加到你的JList中:
list = new JList<Entity>();
list.setModel(new DefaultListModel<Entity>());
list.setCellRenderer(new CheckListRenderer());
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent event) {
        if (event.getX() < 20) { 
            // Quick and dirty: only change the tick if clicked into the leftmost pixels
            @SuppressWarnings("unchecked")
            JList<Entity> list = ((JList<Entity>) event.getSource());
            int index = list.locationToIndex(event.getPoint());// Get index of item clicked
            if (index >= 0) {
                Entity item = (Entity) list.getModel().getElementAt(index);
                item.setActive(!item.isActive()); // Toggle selected state
                list.repaint(list.getCellBounds(index, index));// Repaint cell
            }
        }
    }
});

2
使用Java的好处之一是,由于有庞大的开源社区,很可能已经有人实现了您需要的小部件或工具。除非您真的想自己重新发明轮子,否则没有必要这样做。在这种情况下,这将是一个学习CellRenderers和Editors的好机会。
我的项目在JIDE方面取得了巨大成功。您想要的组件——复选框列表,位于JIDE Common Layer中(它是OSS并托管在java.net上)。商业软件也不错,但您不需要它。 http://www.jidesoft.com/products/oss.htm https://jide-oss.dev.java.net/

1
Swing中的所有聚合组件(即由其他组件组成的组件,例如JTable、JTree或JComboBox)都可以高度定制。例如,JTable组件通常显示JLabel组件的网格,但它也可以显示JButton、JTextField或其他JTable。但是,让这些聚合组件显示非默认对象只是其中易于部分。让它们对键盘和鼠标事件做出适当响应则更加困难,因为Swing将组件分为"渲染器"和"编辑器"两个部分。在我看来,这种分离是一个糟糕的设计选择,只会在尝试扩展Swing组件时使事情变得更加复杂。
为了解释我的意思,请尝试增强Swing的JList组件,使其显示复选框而不是标签。根据Swing的理念,此任务需要实现两个接口:ListCellRenderer(用于绘制复选框)和CellEditor(用于处理复选框上的键盘和鼠标事件)。实现ListCellRenderer接口相对容易,但CellEditor接口可能相当笨拙和难以理解。在这种特殊情况下,我建议完全忘记CellEditor,并直接处理输入事件,如以下代码所示。
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;

public class CheckBoxList extends JList
{
   protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

   public CheckBoxList()
   {
      setCellRenderer(new CellRenderer());

      addMouseListener(new MouseAdapter()
         {
            public void mousePressed(MouseEvent e)
            {
               int index = locationToIndex(e.getPoint());

               if (index != -1) {
                  JCheckBox checkbox = (JCheckBox)
                              getModel().getElementAt(index);
                  checkbox.setSelected(
                                     !checkbox.isSelected());
                  repaint();
               }
            }
         }
      );

      setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
   }

   protected class CellRenderer implements ListCellRenderer
   {
      public Component getListCellRendererComponent(
                    JList list, Object value, int index,
                    boolean isSelected, boolean cellHasFocus)
      {
         JCheckBox checkbox = (JCheckBox) value;
         checkbox.setBackground(isSelected ?
                 getSelectionBackground() : getBackground());
         checkbox.setForeground(isSelected ?
                 getSelectionForeground() : getForeground());
         checkbox.setEnabled(isEnabled());
         checkbox.setFont(getFont());
         checkbox.setFocusPainted(false);
         checkbox.setBorderPainted(true);
         checkbox.setBorder(isSelected ?
          UIManager.getBorder(
           "List.focusCellHighlightBorder") : noFocusBorder);
         return checkbox;
      }
   }
}

在这里,我截取了列表框的鼠标点击事件,并模拟了适当复选框的单击。结果是一个“CheckBoxList”组件,比使用CellEditor接口的等效组件更简单、更小。要使用该类,只需实例化它,然后通过调用setListData方法将JCheckBox对象数组(或JCheckBox对象的子类)传递给它。请注意,此组件中的复选框不会响应按键(即空格键),但如果需要,您可以始终添加自己的按键监听器。
来源:DevX.com

0

这是使用复选框制作列表的又一个示例。

class JCheckList<T> extends JList<T> {

    protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

    public void setSelected(int index) {
        if (index != -1) {
            JCheckBox checkbox = (JCheckBox) getModel().getElementAt(index);
            checkbox.setSelected(
                    !checkbox.isSelected());
            repaint();
        }
    }

    protected static class CellListener
            extends DefaultListModel
            implements ListDataListener {

        ListModel ls;

        public CellListener(ListModel ls) {
            ls.addListDataListener(this);
            int i = ls.getSize();
            for (int v = 0; v < i; v++) {
                var r = new JCheckBox();
                r.setText(ls.getElementAt(v).toString());
                this.addElement(r);
            }
            this.ls = ls;
        }

        @Override
        public void intervalAdded(ListDataEvent e) {
            int begin = e.getIndex0();
            int end = e.getIndex1();
            for (; begin <= end; begin++) {
                var r = new JCheckBox();
                r.setText(ls.getElementAt(begin).toString());
                this.add(begin, r);
            }
        }

        @Override
        public void intervalRemoved(ListDataEvent e) {
            int begin = e.getIndex0();
            int end = e.getIndex1();
            for (; begin <= end; end--) {
                this.remove(begin);
            }
        }

        @Override
        public void contentsChanged(ListDataEvent e) {

        }
    }

    public JCheckList() {
        setCellRenderer(new CellRenderer());

        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                int index = locationToIndex(e.getPoint());

                setSelected(index);
            }
        }
        );


    addKeyListener(new KeyListener(){
        @Override
        public void keyTyped(KeyEvent e) {
            
        }

        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_SPACE){
                
            int index = JCheckList.this.getSelectedIndex();

            setSelected(index);
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
            
        }
            
    });

        setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    }

    @Override
    public void setModel(ListModel<T> d) {
        var r = new CellListener(d);
        d.addListDataListener(r);
        super.setModel(r);
    }

    protected class CellRenderer implements ListCellRenderer {

        public Component getListCellRendererComponent(
                JList list, Object value, int index,
                boolean isSelected, boolean cellHasFocus) {
            JCheckBox checkbox = (JCheckBox) value;
            checkbox.setBackground(isSelected
                    ? getSelectionBackground() : getBackground());
            checkbox.setForeground(isSelected
                    ? getSelectionForeground() : getForeground());
            checkbox.setEnabled(isEnabled());
            checkbox.setFont(getFont());
            checkbox.setFocusPainted(false);
            checkbox.setBorderPainted(true);
            checkbox.setBorder(isSelected
                    ? UIManager.getBorder(
                            "List.focusCellHighlightBorder") : noFocusBorder);
            return checkbox;
        }
    }
}

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