• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JListのセルにJCheckBoxを使用する
#navi(../)
RIGHT:Posted by &author(aterai); at 2011-03-28
*JListのセルにJCheckBoxを使用する [#z6df133e]
JListのセルにJCheckBoxを使用して、チェックボックスの一覧を作成します。
---
category: swing
folder: CheckBoxCellList
title: JListのセルにJCheckBoxを使用する
tags: [JList, JCheckBox, ListCellRenderer, MouseListener, JTree, Box]
author: aterai
pubdate: 2011-03-28T15:00:17+09:00
description: JListのセルにJCheckBoxを使用して、チェックボックスの一覧を作成します。
image: https://lh3.googleusercontent.com/-EfbwsqycTvg/UlyukvM4ivI/AAAAAAAAB3o/NJBvrfM4xPA/s800/CheckBoxCellList.png
hreflang:
    href: https://java-swing-tips.blogspot.com/2013/10/scrollable-list-of-jcheckboxes.html
    lang: en
---
* 概要 [#summary]
`JList`のセルに`JCheckBox`を使用して、チェックボックスの一覧を作成します。

-&jnlp;
-&jar;
-&zip;
#download(https://lh3.googleusercontent.com/-EfbwsqycTvg/UlyukvM4ivI/AAAAAAAAB3o/NJBvrfM4xPA/s800/CheckBoxCellList.png)

//#screenshot
#ref(https://lh6.googleusercontent.com/_9Z4BYR88imo/TZAiQ_MSc_I/AAAAAAAAA4k/FiVk_o38jyY/s800/CheckBoxCellList.png)

**サンプルコード [#hbcc9907]
* サンプルコード [#sourcecode]
#code(link){{
class CheckBoxCellRenderer extends JCheckBox
    implements ListCellRenderer, MouseListener, MouseMotionListener {
class CheckBoxCellRenderer<E extends CheckBoxNode> extends JCheckBox
    implements ListCellRenderer<E>, MouseListener, MouseMotionListener {
  private int rollOverRowIndex = -1;

  @Override public Component getListCellRendererComponent(
    JList list,
    Object value,
    int index,
    boolean isSelected,
    boolean cellHasFocus) {
        JList<? extends E> list, E value, int index,
        boolean isSelected, boolean cellHasFocus) {
    this.setOpaque(true);
    if(isSelected) {
    if (isSelected) {
      this.setBackground(list.getSelectionBackground());
      this.setForeground(list.getSelectionForeground());
    }else{
    } else {
      this.setBackground(list.getBackground());
      this.setForeground(list.getForeground());
    }
    if(value instanceof CheckBoxNode) {
      this.setSelected(((CheckBoxNode)value).selected);
      if(index==pressedRowIndex) {
        this.getModel().setArmed(true);
        this.getModel().setPressed(true);
      }else{
        this.getModel().setRollover(index==rollOverRowIndex);
        this.getModel().setArmed(false);
        this.getModel().setPressed(false);
      }
    }
    this.setText(value.toString());
    this.setSelected(value.selected);
    this.getModel().setRollover(index == rollOverRowIndex);
    this.setText(value.text);
    return this;
  }
  private int rollOverRowIndex = -1;
  private int pressedRowIndex = -3;

  @Override public void mouseExited(MouseEvent e) {
    rollOverRowIndex = -1;
    JList c = (JList)e.getSource();
    c.repaint();
    if (rollOverRowIndex >= 0) {
      JList<?> l = (JList<?>) e.getComponent();
      l.repaint(l.getCellBounds(rollOverRowIndex, rollOverRowIndex));
      rollOverRowIndex = -1;
    }
  }

  @Override public void mouseClicked(MouseEvent e) {
    if(e.getButton()==MouseEvent.BUTTON1) {
      JList t = (JList)e.getComponent();
      DefaultListModel m = (DefaultListModel)t.getModel();
    if (e.getButton() == MouseEvent.BUTTON1) {
      JList<?> l = (JList<?>) e.getComponent();
      Point p = e.getPoint();
      int index  = t.locationToIndex(p);
      CheckBoxNode n = (CheckBoxNode)m.get(index);
      Component c = t.getCellRenderer().getListCellRendererComponent(
          t, n, index, false, false);
      Dimension d = c.getPreferredSize();
      if(d.width>=p.x) {
      int index  = l.locationToIndex(p);
      if (index >= 0) {
        @SuppressWarnings("unchecked")
        DefaultListModel<CheckBoxNode> m =
            (DefaultListModel<CheckBoxNode>) l.getModel();
        CheckBoxNode n = m.get(index);
        m.set(index, new CheckBoxNode(n.text, !n.selected));
        t.repaint();
        t.repaint(t.getCellBounds(index, index));
        l.repaint(l.getCellBounds(index, index));
      }
    }
  }
  @Override public void mouseEntered(MouseEvent e) {}
  private int pressStartIndex = -1;
  @Override public void mousePressed(MouseEvent e) {
    if(e.getButton()==MouseEvent.BUTTON1) {
      JList c = (JList)e.getSource();
      int row = c.locationToIndex(e.getPoint());
      pressStartIndex = row;
      if(row != pressedRowIndex) {
        pressedRowIndex = row;
        c.repaint();
      }

  @Override public void mouseMoved(MouseEvent e) {
    JList<?> l = (JList<?>) e.getComponent();
    int index = l.locationToIndex(e.getPoint());
    if (index != rollOverRowIndex) {
      rollOverRowIndex = index;
      l.repaint();
    }
  }
  @Override public void mouseReleased(MouseEvent e) {
    pressedRowIndex = -1;
    pressStartIndex = -1;

  @Override public void mouseEntered(MouseEvent e)  {
    /* not needed */
  }
  @Override public void mouseDragged(MouseEvent e) {
    JList c = (JList)e.getSource();
    int row = c.locationToIndex(e.getPoint());
    if(row != rollOverRowIndex) {
      rollOverRowIndex = -1;
    }
    if(row != pressedRowIndex) {
      pressedRowIndex = -1;
    }
    if(row == pressStartIndex) {
      pressedRowIndex = row;
    }
    c.repaint();

  @Override public void mousePressed(MouseEvent e)  {
    /* not needed */
  }
  @Override public void mouseMoved(MouseEvent e) {
    JList c = (JList)e.getSource();
    int row = c.locationToIndex(e.getPoint());
    if(row != rollOverRowIndex) {
      rollOverRowIndex = row;
      c.repaint();
    }

  @Override public void mouseReleased(MouseEvent e) {
    /* not needed */
  }

  @Override public void mouseDragged(MouseEvent e)  {
    /* not needed */
  }
}
}}

**解説 [#u07c22e4]
- 左: "JCheckBox Cell in JList"
-- JCheckBoxを継承するListCellRendererを設定
-- チェックボックスのロールオーバーなどは、JListにマウスリスナーを設定して表示
-- JList#putClientProperty("List.isFileList", Boolean.TRUE);で、クリックが有効になる領域をチェックボックスの幅に制限
- 右: "JCheckBox in Box"
-- Box.createVerticalBox()にJCheckBoxを追加
-- JCheckBox#setAlignmentX(Component.LEFT_ALIGNMENT);で左揃えに設定
* 解説 [#explanation]
- `Box`
-- `Box.createVerticalBox()`に`JCheckBox`を追加
-- `JCheckBox#setAlignmentX(Component.LEFT_ALIGNMENT);`で左揃えに設定

----
[[JTreeの葉ノードをJCheckBoxにする>Swing/CheckBoxNodeTree]]のセルレンダラーを使ったJTreeに、JTree#setRootVisible(false)を設定しても、ほぼ同様のチェックボックスの一覧を作成することができます。
- `JList`
-- `JCheckBox`を継承する`ListCellRenderer`を設定
-- チェックボックスのロールオーバーなどは、`JList`にマウスリスナーを設定して描画
-- `JList#processMouseEvent`、`JList#processMouseMotionEvent`のオーバーライドと、`JList#putClientProperty("List.isFileList", Boolean.TRUE);`でクリックが有効になる領域をチェックボックスの幅に制限

#code{{
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;

public class TreeCheckBoxListTest {
  public JComponent makeUI() {
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
    for(int i=0; i<16; i++) {
      root.add(new DefaultMutableTreeNode(new CheckBoxNode("No."+i, i%2==0)));
JList list1 = new JList(model) {
  private CheckBoxCellRenderer renderer;
  @Override public void updateUI() {
    setForeground(null);
    setBackground(null);
    setSelectionForeground(null);
    setSelectionBackground(null);
    if (renderer != null) {
      removeMouseListener(renderer);
      removeMouseMotionListener(renderer);
    }
    JTree tree = new JTree(new DefaultTreeModel(root));
    tree.setEditable(true);
    tree.setRootVisible(false);
    tree.setCellRenderer(new CheckBoxNodeRenderer());
    tree.setCellEditor(new CheckBoxNodeEditor(tree));
    return new JScrollPane(tree);
    super.updateUI();
    renderer = new CheckBoxCellRenderer();
    setCellRenderer(renderer);
    addMouseListener(renderer);
    addMouseMotionListener(renderer);
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });

  // @see SwingUtilities2.pointOutsidePrefSize(...)
  private boolean pointOutsidePrefSize(Point p) {
    int index = locationToIndex(p);
    DefaultListModel m = (DefaultListModel) getModel();
    CheckBoxNode n = (CheckBoxNode) m.get(index);
    Component c = getCellRenderer().getListCellRendererComponent(
                    this, n, index, false, false);
    // c.doLayout();
    Dimension d = c.getPreferredSize();
    Rectangle rect = getCellBounds(index, index);
    rect.width = d.width;
    return index < 0 || !rect.contains(p);
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new TreeCheckBoxListTest().makeUI());
    f.setSize(320, 240);
    f.setLocationRelativeTo(null);
    f.setVisible(true);

  @Override protected void processMouseEvent(MouseEvent e) {
    if (!pointOutsidePrefSize(e.getPoint())) {
      super.processMouseEvent(e);
    }
  }
}
class CheckBoxNode {
  public final String text;
  public final boolean selected;
  public CheckBoxNode(String text, boolean selected) {
    this.text = text;
    this.selected = selected;

  @Override protected void processMouseMotionEvent(MouseEvent e) {
    if (!pointOutsidePrefSize(e.getPoint())) {
      super.processMouseMotionEvent(e);
    } else {
      e = new MouseEvent(
        (Component) e.getSource(), MouseEvent.MOUSE_EXITED, e.getWhen(),
        e.getModifiers(), e.getX(), e.getY(),
        e.getXOnScreen(), e.getYOnScreen(),
        e.getClickCount(), e.isPopupTrigger(), MouseEvent.NOBUTTON);
      super.processMouseEvent(e);
    }
  }
  @Override public String toString() {
    return text;
  }
}
};
}}

- `JTree`
-- `JCheckBox`を継承する`TreeCellRenderer`を設定
--- [[JTreeの葉ノードをJCheckBoxにする>Swing/CheckBoxNodeTree]]で作成したセルレンダラーを使用
-- `JTree#setRootVisible(false)`でルートノードを非表示に設定
--- [[JTreeのルートノードを非表示に設定する>Swing/TreeRootVisible]]

#code{{
class CheckBoxNodeRenderer extends JCheckBox implements TreeCellRenderer {
  private TreeCellRenderer renderer = new DefaultTreeCellRenderer();
  @Override public Component getTreeCellRendererComponent(
      JTree tree, Object value, boolean selected, boolean expanded,
      boolean leaf, int row, boolean hasFocus) {
    if(leaf && value != null && value instanceof DefaultMutableTreeNode) {
    JTree tree, Object value, boolean selected, boolean expanded,
    boolean leaf, int row, boolean hasFocus) {
    if (leaf && value instanceof DefaultMutableTreeNode) {
      this.setOpaque(false);
      Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
      if(userObject!=null && userObject instanceof CheckBoxNode) {
        CheckBoxNode node = (CheckBoxNode)userObject;
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        CheckBoxNode node = (CheckBoxNode) userObject;
        this.setText(node.text);
        this.setSelected(node.selected);
      }
      return this;
    }
    return renderer.getTreeCellRendererComponent(
        tree, value, selected, expanded, leaf, row, hasFocus);
             tree, value, selected, expanded, leaf, row, hasFocus);
  }
}

class CheckBoxNodeEditor extends JCheckBox implements TreeCellEditor {
  private final JTree tree;
  public CheckBoxNodeEditor(JTree tree) {
    super();
    this.tree = tree;
    setOpaque(false);
    setFocusable(false);
    addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        stopCellEditing();
      }
    });
  }

  @Override public Component getTreeCellEditorComponent(
      JTree tree, Object value, boolean isSelected, boolean expanded,
      boolean leaf, int row) {
    if(leaf && value != null && value instanceof DefaultMutableTreeNode) {
      Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
      if(userObject!=null && userObject instanceof CheckBoxNode) {
        this.setSelected(((CheckBoxNode)userObject).selected);
    JTree tree, Object value, boolean isSelected, boolean expanded,
    boolean leaf, int row) {
    if (leaf && value instanceof DefaultMutableTreeNode) {
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        this.setSelected(((CheckBoxNode) userObject).selected);
      } else {
        this.setSelected(false);
      }
      this.setText(value.toString());
    }
    return this;
  }

  @Override public Object getCellEditorValue() {
    return new CheckBoxNode(getText(), isSelected());
  }

  @Override public boolean isCellEditable(EventObject e) {
    return (e != null && e instanceof MouseEvent);
    return (e instanceof MouseEvent);
  }
  //Copid from AbstractCellEditor
  //protected EventListenerList listenerList = new EventListenerList();
  //transient protected ChangeEvent changeEvent = null;
  @Override public boolean shouldSelectCell(java.util.EventObject anEvent) {
    return true;
  }
  @Override public boolean stopCellEditing() {
    fireEditingStopped();
    return true;
  }
  @Override public void  cancelCellEditing() {
    fireEditingCanceled();
  }
  @Override public void addCellEditorListener(CellEditorListener l) {
    listenerList.add(CellEditorListener.class, l);
  }
  @Override public void removeCellEditorListener(CellEditorListener l) {
    listenerList.remove(CellEditorListener.class, l);
  }
  public CellEditorListener[] getCellEditorListeners() {
    return listenerList.getListeners(CellEditorListener.class);
  }
  protected void fireEditingStopped() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
      }
    }
  }
  protected void fireEditingCanceled() {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for(int i = listeners.length-2; i>=0; i-=2) {
      if(listeners[i]==CellEditorListener.class) {
        // Lazily create the event:
        if(changeEvent == null) changeEvent = new ChangeEvent(this);
        ((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
      }
    }
  }
}

  // Copied from AbstractCellEditor
  // protected EventListenerList listenerList = new EventListenerList();
  // transient protected ChangeEvent changeEvent = null;
  @Override public boolean shouldSelectCell(EventObject anEvent) {
    // ...
}}

//**参考リンク
**コメント [#d0ea5ae4]
* 参考リンク [#reference]
- [[JTreeの葉ノードをJCheckBoxにする>Swing/CheckBoxNodeTree]]
- [[JTreeのルートノードを非表示に設定する>Swing/TreeRootVisible]]

* コメント [#comment]
#comment
- 補足として追記していた`JTree`を使用するサンプルを本体に取り込んで、スクリーンショットなどを更新。 -- &user(aterai); &new{2013-10-15 (火) 11:56:41};

#comment