• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JListのアイテムを範囲指定で選択
#navi(../)
*JListのアイテムを範囲指定で選択 [#va695101]
>編集者:[[Terai Atsuhiro>terai]]~
作成日:2006-08-14~
更新日:&lastmod;
---
category: swing
folder: RubberBanding
title: JListのアイテムを範囲指定で選択
tags: [JList, MouseListener, MouseMotionListener]
author: aterai
pubdate: 2006-08-14T01:35:31+09:00
description: JListのアイテムをラバーバンドで範囲指定して選択します。
image: https://lh6.googleusercontent.com/_9Z4BYR88imo/TQTSd-lu2aI/AAAAAAAAAi0/AQTsBqR1OUc/s800/RubberBanding.png
hreflang:
    href: https://java-swing-tips.blogspot.com/2008/10/using-rubber-band-selection-in-jlist.html
    lang: en
---
* 概要 [#summary]
`JList`のアイテムをラバーバンドで範囲指定して選択します。

#contents
#download(https://lh6.googleusercontent.com/_9Z4BYR88imo/TQTSd-lu2aI/AAAAAAAAAi0/AQTsBqR1OUc/s800/RubberBanding.png)

**概要 [#x17f8eee]
JListのアイテムをラバーバンドで範囲指定して選択します。

#screenshot

**サンプルコード [#t7aa984b]
#code{{
class MyList extends JList {
  private final Color rcolor;
  private final Color pcolor;
  private final AlphaComposite alcomp
  private final Polygon polygon = new Polygon();
  private final Line2D line = new Line2D.Double();
  private Point srcPoint = null;
  public MyList(ListModel model) {
* サンプルコード [#sourcecode]
#code(link){{
class RubberBandSelectionList<E extends ListItem> extends JList<E> {
  private static final AlphaComposite ALPHA =
    AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .1f);
  private RubberBandListCellRenderer<E> renderer;
  private Color polygonColor;
  public RubberBandSelectionList(ListModel<E> model) {
    super(model);
    rcolor = SystemColor.activeCaption;
    pcolor = makeColor(rcolor);
    alcomp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f);
    setLayoutOrientation(JList.HORIZONTAL_WRAP);
    setVisibleRowCount(0);
    setFixedCellWidth(62);
    setFixedCellHeight(62);
    setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    setCellRenderer(new ListCellRenderer() {
      public Component getListCellRendererComponent(
          JList list, Object value, int index, boolean isSelected,
          boolean cellHasFocus) {
        MyIcon icon = (MyIcon)getModel().getElementAt(index);
        icon.setSelected(isSelected);
        icon.setFocused(cellHasFocus);
        return icon;
      }
    });
    addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseDragged(MouseEvent e){
        if(srcPoint==null) srcPoint = e.getPoint();
        Point destPoint = e.getPoint();
        polygon.reset();
        polygon.addPoint(srcPoint.x,  srcPoint.y);
        polygon.addPoint(destPoint.x, srcPoint.y);
        polygon.addPoint(destPoint.x, destPoint.y);
        polygon.addPoint(srcPoint.x,  destPoint.y);
        if(srcPoint.getX()==destPoint.getX() ||
           srcPoint.getY()==destPoint.getY()) {
          line.setLine(srcPoint.getX(),srcPoint.getY(),
                       destPoint.getX(),destPoint.getY());
          setSelectedIndices(getIntersectsIcons(line));
        }else{
          setSelectedIndices(getIntersectsIcons(polygon));
        }
        repaint();
      }
    });
    addMouseListener(new MouseAdapter() {
      public void mouseReleased(MouseEvent e) {
        srcPoint = null;
        repaint();
      }
      public void mousePressed(MouseEvent e) {
        if(locationToIndex(e.getPoint())<0) {
          clearSelection();
          repaint();
        }else{
          int index = locationToIndex(e.getPoint());
          MyIcon icon = (MyIcon)getModel().getElementAt(index);
          Rectangle rect = getCellBounds(index,index);
          if(!rect.contains(e.getPoint())) {
            clearSelection();
            repaint();
          }
        }
      }
    });
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if(srcPoint==null) return;
    Graphics2D g2d = (Graphics2D) g;
    g2d.setPaint(rcolor);
    g2d.drawPolygon(polygon);
    g2d.setComposite(alcomp);
    g2d.setPaint(pcolor);
    g2d.fillPolygon(polygon);
  }
  private int[] getIntersectsIcons(Shape p) {
    ListModel model = getModel();
    Vector list = new Vector(model.getSize());
    for(int i=0;i<model.getSize();i++) {
      Rectangle r = MyList.this.getCellBounds(i,i);
      if(p.intersects(r)) {
        list.add(i);
      }

  @Override public void updateUI() {
    setSelectionForeground(null);
    setSelectionBackground(null);
    setCellRenderer(null);
    if (renderer == null) {
      renderer = new RubberBandListCellRenderer<E>();
    } else {
      removeMouseMotionListener(renderer);
      removeMouseListener(renderer);
    }
    int[] il = new int[list.size()];
    for(int i=0;i<list.size();i++) {
      il[i] = list.get(i);
    }
    return il;
  }
  private Color makeColor(Color c) {
    super.updateUI();
    EventQueue.invokeLater(() -> {
      setCellRenderer(renderer);
      addMouseMotionListener(renderer);
      addMouseListener(renderer);
      setLayoutOrientation(JList.HORIZONTAL_WRAP);
      setVisibleRowCount(0);
      setFixedCellWidth(62);
      setFixedCellHeight(62);
      setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    });
    Color c = getSelectionBackground();
    int r = c.getRed();
    int g = c.getGreen();
    int b = c.getBlue();
    if(r>g) return (r>b)?new Color(r,0,0):new Color(0,0,b);
    else    return (g>b)?new Color(0,g,0):new Color(0,0,b);
    polygonColor = r > g ? r > b ? new Color(r, 0, 0) : new Color(0, 0, b)
                         : g > b ? new Color(0, g, 0) : new Color(0, 0, b);
  }

  @Override protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (renderer != null && renderer.polygon != null) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(getSelectionBackground());
      g2.draw(renderer.polygon);
      g2.setComposite(ALPHA);
      g2.setPaint(polygonColor);
      g2.fill(renderer.polygon);
      g2.dispose();
    }
  }
}
}}
-&jnlp;
-&jar;
-&zip;

**解説 [#e178a26c]
JListにマウスリスナーを設定して、ドラッグに応じて矩形を描画しています。このとき、その矩形の内部にアイテムアイコンが重なる場合は、それを選択状態に変更しています。選択範囲が矩形にならずに直線になっている場合は、別途その直線と交差するアイテムを選択するようにしています。
* 解説 [#explanation]
上記のサンプルでは、`JList`にマウスリスナーを設定してドラッグに応じた矩形を描画しています。

JList内のアイテムの配置は、JList#setLayoutOrientation(JList.HORIZONTAL_WRAP)メソッドを使っているため、水平方向に整列されます。
- `JList`内のアイテムの配置は`JList#setLayoutOrientation(JList.HORIZONTAL_WRAP)`メソッドを使っているため水平方向で整列される
- ラバーバンド矩形内部に重なるアイテムアイコンを検索しそれを`JList#setSelectedIndices(int[])`で選択状態に変更
-- %%選択範囲が矩形にならずに直線になっている場合は別途その直線と交差するアイテムを選択%% `Polygon`ではなく`Path2D`を使用することでこれを回避
-- `JDK 1.8.0`以降なら以下を`l.setSelectedIndices(IntStream.range(0, l.getModel().getSize()).filter(i -> p.intersects(l.getCellBounds(i, i))).toArray());`で置き換え可能

**参考リンク [#o8eb5cf2]
-[[Can someone optimise the following code ?>http://forum.java.sun.com/thread.jspa?threadID=757107]]
-[[XP Style Icons - Windows Application Icon, Software XP Icons>http://www.icongalore.com/]]
#code{{
private int[] getIntersectsIcons(JList l, Shape p) {
  ListModel model = l.getModel();
  List<Integer> list = new ArrayList<>(model.getSize());
  for (int i = 0; i < model.getSize(); i++) {
    Rectangle r = l.getCellBounds(i, i);
    if (p.intersects(r)) {
      list.add(i);
    }
  }
  // JDK 1.8.0以降のstreamでList<Integer>をプリミティブなint配列に変換:
  // return list.stream().mapToInt(i -> i).toArray();
  int[] il = new int[list.size()];
  for (int i = 0; i < list.size(); i++) {
    il[i] = list.get(i);
  }
  return il;
}
}}

**コメント [#re81cc47]
* 参考リンク [#reference]
- [https://community.oracle.com/thread/1378164 Swing - Can someone optimise the following code ?]
- [https://xp-style-icons.en.softonic.com/ XP Style Icons - Download]
- [[JListのアイテムをラバーバンドで複数選択、ドラッグ&ドロップで並べ替え>Swing/DragSelectDropReordering]]
-- このラバーバンドで選択したアイテムを実際にドラッグして並べ替えるサンプル

* コメント [#comment]
#comment
- 点線のアニメーション: [http://programamemo2.blogspot.com/2007/08/java.html プログラマメモ2: java ラバーバンドを表現するためのした調べ] -- &user(aterai); &new{2008-08-01 (金) 16:24:28};
- スクリーンショットなどを更新 -- &user(aterai); &new{2008-10-06 (月) 21:29:19};

#comment