概要

JListのセルに配置されているコンポーネントをマウスの座標から検索し、それに設定されたカーソルをJListに適用します。

サンプルコード

class LinkCellList<E> extends JList<E> {
  private int prevIndex = -1;
  public LinkCellList(ListModel<E> model) {
    super(model);
  }
  @Override public void updateUI() {
    setForeground(null);
    setBackground(null);
    setSelectionForeground(null);
    setSelectionBackground(null);
    super.updateUI();
    setFixedCellHeight(32);
    setCellRenderer(new LinkCellRenderer<E>());
  }
  @Override protected void processMouseMotionEvent(MouseEvent e) {
    Point pt = e.getPoint();
    int i = locationToIndex(pt);
    E s = ((ListModel<E>) getModel()).getElementAt(i);
    Component c = getCellRenderer().getListCellRendererComponent(
        this, s, i, false, false);
    Rectangle r = getCellBounds(i, i);
    c.setBounds(r);
    if (prevIndex != i) {
      c.doLayout();
    }
    prevIndex = i;
    pt.translate(-r.x, -r.y);
    Component cmp = SwingUtilities.getDeepestComponentAt(c, pt.x, pt.y);
    if (cmp == null) {
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    } else {
      setCursor(cmp.getCursor());
    }
  }
}
view all

解説

上記のサンプルでは、以下の手順でイベントを取得しないセルレンダラー中のコンポーネントに応じたカーソルの変更を行っています。

  • JList上をマウスカーソルを移動した時に、カーソルのある行を取得
  • セルレンダラーに行番号や文字列などの値を渡して描画用のコンポーネントを取得
  • 描画用コンポーネントの位置とサイズをJList#getCellBounds()で取得したセル領域に変更
    • Component#setBounds(...)で描画用コンポーネントの位置とサイズを変更してもその子コンポーネントのレイアウトは更新されない
  • Component#doLayout()で、レイアウトを更新
    • このサンプルで使用しているFlowLayoutでは、JLabelに設定する文字列の長さで後続のコンポーネントの位置が変化するので、Component#doLayout()が必要
    • ただし、同じ行の場合は描画用コンポーネントも前回と同じはずなのでレイアウトを更新する必要はない
    • JListのセル内にJButtonを配置するなどのように、すべての行のレイアウトが同じ(内容に応じて変化しない)場合も同様に更新する必要はない
  • JList基準のカーソル位置座標を描画用コンポーネント基準に変更
  • SwingUtilities.getDeepestComponentAt(...);で描画用コンポーネントから変更した座標の下にある子コンポーネントを取得
  • 取得した子コンポーネントのカーソルをJListのカーソルとして設定

コメント