Как мне сделать список с флажками в Java Swing?

Что было бы лучшим способом иметь список элементов с флажком каждый в Java Swing?

Т.е. JList с элементами, которые имеют некоторый текст и флажок каждый?

11 ответов

Решение

Создать кастом ListCellRenderer и назначить его JList,

Этот обычай ListCellRenderer должен вернуть JCheckbox в осуществлении getListCellRendererComponent(...) метод.

Но это JCheckbox не будет редактируемым, это простая краска на экране, вы можете выбрать, когда это JCheckbox должен быть отмечен или нет,

Например, показывать галочку при выборе строки (параметр isSelected), но в этом случае статус проверки не будет поддерживаться, если выбор будет изменен. Лучше показать это проверено, сверяясь с данными ниже ListModel, но затем вам нужно реализовать метод, который изменяет статус проверки данных, и уведомить об изменении JList быть перекрашенным.

Я опубликую пример кода позже, если вам это нужно

ListCellRenderer

Прекрасный ответ это CheckBoxList, Он реализует ответ Telcontar (хотя за 3 года до этого:))... Я использую его в 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 Я связался с.

Просто внедрите 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

Подробности в пользовательских компонентах рендеринга свинга.

PS: если хотите радиоэлементы просто замените extends JCheckbox с extends JRadioButton,

Я, вероятно, хотел бы использовать JTable, а не JList, и поскольку рендеринг флажка по умолчанию довольно уродлив, я бы, вероятно, хотел бы добавить пользовательские TableModel, CellRenderer и CellEditor для представления логического значения. Конечно, я бы предположил, что это было сделано уже несколько раз. У Солнца есть хорошие примеры.

Лучшее решение для 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"));

Я рекомендую вам использовать JPanel с GridLayout 1 столбца. Добавьте флажки в JPanel и установите JPanel в качестве источника данных JScrollPane. А чтобы получить выбранные CheckBoxes, просто вызовите getComponents() JPanel, чтобы получить CheckBoxes.

Хорошие шансы с Java, что кто-то уже реализовал нужный вам виджет или утилиту. Часть преимуществ большого сообщества OSS. Не нужно изобретать велосипед, если вы действительно не хотите делать это самостоятельно. В этом случае это будет хорошим обучающим упражнением в CellRenderers и Editors.

Мой проект имел большой успех с JIDE. Требуемый компонент, список флажков, находится на общем уровне JIDE (который является OSS и размещен на java.net). Коммерческий материал тоже хорош, но он вам не нужен.

http://www.jidesoft.com/products/oss.htm https://jide-oss.dev.java.net/

Мне не нравятся решения, которые помещают флажок в модель. Модель должна содержать только данные, а не элементы отображения.

Я нашел это 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
            }
        }
    }
});

это еще один пример создания списка с флажками

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

Все агрегатные компоненты в Swing, то есть компоненты, состоящие из других компонентов, таких как JTable, JTree или JComboBox, могут быть настроены индивидуально. Например, компонент JTable обычно отображает сетку компонентов JLabel, но он также может отображать JButtons, JTextFields или даже другие JTables. Однако получить эти агрегатные компоненты для отображения объектов не по умолчанию. Заставить их правильно реагировать на события клавиатуры и мыши - гораздо более сложная задача из-за разделения компонентов 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. Чтобы использовать класс, просто создайте его экземпляр, а затем передайте ему массив объектов JCheckBox (или подклассов объектов JCheckBox), вызвав setListData. Обратите внимание, что флажки в этом компоненте не будут реагировать на нажатия клавиш (например, пробел), но вы всегда можете добавить свой собственный слушатель клавиш, если это необходимо.

Источник: DevX.com

Вот лишь небольшое дополнение к JCheckBoxList от Rawa. Это добавит возможность выбора с помощью пробела. Если выбрано несколько элементов, для всех будет установлено инвертированное значение первого элемента.

        addKeyListener(new KeyAdapter() {
        @Override
        public void keyPressed(KeyEvent e) {
            int index = getSelectedIndex();
            if (index != -1 && e.getKeyCode() == KeyEvent.VK_SPACE) {
                boolean newVal = !((JCheckBox) (getModel()
                        .getElementAt(index))).isSelected();
                for (int i : getSelectedIndices()) {
                    JCheckBox checkbox = (JCheckBox) getModel()
                            .getElementAt(i);
                    checkbox.setSelected(newVal);
                    repaint();
                }
            }
        }
        });
Другие вопросы по тегам