---
category: swing
folder: GroupingListItems
title: JListのアイテムをグループ化する
title-en: Group JList items
tags: [JList, JSeparator, MatteBorder]
author: aterai
pubdate: 2025-12-22T00:43:07+09:00
description: JListのアイテムをJSeparator、またはMatteBorderを使用してグループ化して表示します。
summary-jp: JListのアイテムをJSeparator、またはMatteBorderを使用してグループ化して表示します。
summary-en: Group JList items using JSeparator or MatteBorder.
image: https://drive.google.com/uc?id=1hvXo9HYNh6KEPOif0oeVK7wDWVdYxCZG
---
* Summary [#summary]
JListのアイテムをJSeparator、またはMatteBorderを使用してグループ化して表示します。
`JList`のアイテムを`JSeparator`、または`MatteBorder`を使用してグループ化して表示します。
// #en{{Group JList items using JSeparator or MatteBorder.}}
#download(https://drive.google.com/uc?id=1hvXo9HYNh6KEPOif0oeVK7wDWVdYxCZG)
* Source Code Examples [#sourcecode]
#code(link){{
class GroupList<E> extends JList<E> {
protected GroupList(ListModel<E> model) {
super(model);
initActionMpa(this);
}
@Override public void updateUI() {
setCellRenderer(null);
super.updateUI();
ListCellRenderer<? super E> r = getCellRenderer();
setCellRenderer((l, v, index, isSelected, cellHasFocus) ->
v instanceof JSeparator
? (JSeparator) v
: r.getListCellRendererComponent(l, v, index, isSelected, cellHasFocus)
);
}
private static <E> void initActionMpa(JList<E> list) {
ActionMap am = list.getActionMap();
String selectPrevKey = "selectPreviousRow";
Action prev = am.get(selectPrevKey);
am.put(selectPrevKey, new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
prev.actionPerformed(e);
JList<?> l = (JList<?>) e.getSource();
int index = l.getSelectedIndex();
Object o = l.getModel().getElementAt(index);
if (o instanceof JSeparator) {
prev.actionPerformed(e);
}
}
});
String selectNextKey = "selectNextRow";
Action next = am.get(selectNextKey);
am.put(selectNextKey, new AbstractAction() {
@Override public void actionPerformed(ActionEvent e) {
next.actionPerformed(e);
JList<?> l = (JList<?>) e.getSource();
int index = l.getSelectedIndex();
Object o = l.getModel().getElementAt(index);
if (o instanceof JSeparator) {
next.actionPerformed(e);
}
}
});
InputMap im = list.getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), selectPrevKey);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, 0), selectPrevKey);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), selectNextKey);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, 0), selectNextKey);
}
}
}}
* Description [#description]
- `Box + Multiple JList + JSeparator`
-- グループごとに`JList`を作成し、これを`Box.createVerticalBox()`で作成した垂直配置`Box`に`JSeparator`で区切って追加
-- このサンプルでは`new JTree()`で生成されるサンプル`TreeModel`を利用してリストアイテムのグループ化をテストしている
-- フォーカスオーナーである`JList`のみ選択状態ハイライト表示するよう各`JList`のセルレンダラーを変更し、グループ内でのみ選択可能なように見せかけている
-- [[BoxLayoutでリスト状に並べる>Swing/ComponentList]]
#code{{
private static Container makeListBox() {
Box box = Box.createVerticalBox();
JTree tree = new JTree();
TreeModel model = tree.getModel();
TreeNode root = (TreeNode) model.getRoot();
Collections.list((Enumeration<?>) root.children())
.stream()
.filter(TreeNode.class::isInstance)
.map(TreeNode.class::cast)
.forEach(node -> {
if (!node.isLeaf()) {
Enumeration<?> children = node.children();
box.add(makeList(Collections.list(children).toArray()));
box.add(new JSeparator());
}
});
JPanel p = new JPanel(new BorderLayout());
p.setBackground(UIManager.getColor("List.background"));
p.add(box, BorderLayout.NORTH);
return p;
}
private static JList<Object> makeList(Object... ary) {
JList<Object> c = new JList<>(ary) {
@Override public void updateUI() {
setCellRenderer(null);
super.updateUI();
ListCellRenderer<? super Object> r = getCellRenderer();
setCellRenderer((l, v, index, isSelected, cellHasFocus) -> {
boolean selected = isSelected && l.isFocusOwner();
return r.getListCellRendererComponent(l, v, index, selected, cellHasFocus);
});
}
};
int height = c.getPreferredSize().height;
c.setMaximumSize(new Dimension(Short.MAX_VALUE, height));
return c;
}
}}
- `JSeparator + ListModel`
-- レイアウトが`VERTICAL`の`JList`に`ListModel<Object>`モデルを適用し、セルアイテムとして`JSeparator`を追加してグループ化の区切りとする
-- デフォルトセルレンダラーの描画用コンポーネントである`JLabel`の代わりに`ListModel<Object>`に追加された`JSeparator`をそのまま使用する
-- カーソルキーによる選択状態移動で`JSeparator`をスキップするよう`selectPreviousRow`、`selectNextRow`を置き換えている
--- `selectPreviousRowExtendSelection`、`selectPreviousRowExtendSelection`アクションなどの選択行の拡張には未対応
--- マウスカーソルによる`JSeparator`アイテムの選択は制限していないが、表示上は`JSeparator`は選択状態にならないようセルレンダラーで設定
-- [[JComboBoxにJSeparatorを挿入>Swing/ComboBoxSeparator]]
- `CellRenderer + MatteBorder`
-- `new JTree()`で生成されるサンプル`TreeModel`の`2`レベルの`TreeNode`を利用してグループ化した`ListModel<TreeNode>`を作成し、セルレンダラーで現在の`TreeNode`とその次の`TreeNode`の親`TreeNode`が異なる場合は現在の`TreeNode`がグループの末尾であるとして`MatteBorder`で下線を設定している
-- `JList`のレイアウトは上記の`JSeparator + ListModel`と同様にセルが単一列で並ぶ`VERTICAL`の場合のみ想定している
-- [[JComboBoxのアイテムをBorderで修飾してグループ分け>Swing/BorderSeparator]]
#code{{
class GroupBorderList<E extends TreeNode> extends JList<E> {
protected GroupBorderList(ListModel<E> model) {
super(model);
}
@Override public void updateUI() {
setCellRenderer(null);
super.updateUI();
ListCellRenderer<? super E> r = getCellRenderer();
setCellRenderer((l, v, index, isSelected, cellHasFocus) -> {
Component c = r.getListCellRendererComponent(l, v, index, isSelected, cellHasFocus);
if (c instanceof JComponent) {
Border outside = getOutsideBorder(l, v, index);
String key = isSelected ? "List.focusCellHighlightBorder" : "List.noFocusBorder";
Border inside = UIManager.getBorder(key);
((JComponent) c).setBorder(BorderFactory.createCompoundBorder(outside, inside));
}
return c;
});
}
private static Border getOutsideBorder(JList<?> l, TreeNode v, int index) {
int max = l.getModel().getSize();
int next = index + 1;
Object n = next < max ? l.getModel().getElementAt(next) : null;
return Optional.ofNullable(n)
.filter(TreeNode.class::isInstance)
.map(TreeNode.class::cast)
.map(TreeNode::getParent)
.filter(p -> !Objects.equals(p, v.getParent()))
.<Border>map(p -> BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY))
.orElseGet(() -> BorderFactory.createMatteBorder(0, 0, 1, 0, l.getBackground()));
}
}
}}
* Reference [#reference]
- [[BoxLayoutでリスト状に並べる>Swing/ComponentList]]
- [[JComboBoxにJSeparatorを挿入>Swing/ComboBoxSeparator]]
- [[JComboBoxのアイテムをBorderで修飾してグループ分け>Swing/BorderSeparator]]
* Comment [#comment]
#comment
#comment