概要

JListのアイテムをラバーバンドで範囲指定して選択します。

サンプルコード

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);
  }
  @Override public void updateUI() {
    setSelectionForeground(null);
    setSelectionBackground(null);
    setCellRenderer(null);
    if (renderer == null) {
      renderer = new RubberBandListCellRenderer<E>();
    } else {
      removeMouseMotionListener(renderer);
      removeMouseListener(renderer);
    }
    super.updateUI();
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        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();
    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();
    }
  }
}
view all

解説

上記のサンプルでは、JListにマウスリスナーを設定して、ドラッグに応じた矩形を描画しています。

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());で置き換え可能
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;
}

参考リンク

コメント