• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JListのセル中に配置したコンポーネント毎にカーソルを変更する
#tags(JList, Cursor, ListCellRenderer)
#author(aterai)
#pubdate(2013-10-21T00:16:35+09:00)
* 概要 [#l0ef8c80]
---
category: swing
folder: CursorOfCellComponent
title: JListのセル中に配置したコンポーネント毎にカーソルを変更する
tags: [JList, Cursor, ListCellRenderer]
author: aterai
pubdate: 2013-10-21T00:16:35+09:00
description: JListのセルに配置されているコンポーネントをマウスの座標から検索し、それに設定されたカーソルをJListに適用します。
image: https://lh3.googleusercontent.com/-v3ugRz81Y0Q/UmPxM3SwOYI/AAAAAAAAB4Y/PqZaNMCPgN0/s800/CursorOfCellComponent.png
---
* 概要 [#summary]
`JList`のセルに配置されているコンポーネントをマウスの座標から検索し、それに設定されたカーソルを`JList`に適用します。

#download(https://lh3.googleusercontent.com/-v3ugRz81Y0Q/UmPxM3SwOYI/AAAAAAAAB4Y/PqZaNMCPgN0/s800/CursorOfCellComponent.png)

* サンプルコード [#bbc6a488]
* サンプルコード [#sourcecode]
#code(link){{
JList<String> list = new JList<String>(model) {
  private LinkCellRenderer renderer;
class LinkCellList<E> extends JList<E> {
  private int prevIndex = -1;
  protected LinkCellList(ListModel<E> model) {
    super(model);
  }

  @Override public void updateUI() {
    setForeground(null);
    setBackground(null);
    setSelectionForeground(null);
    setSelectionBackground(null);
    super.updateUI();
    renderer = new LinkCellRenderer();
    setCellRenderer(renderer);
    setFixedCellHeight(32);
    setCellRenderer(new LinkCellRenderer<>());
    // TEST: putClientProperty("List.isFileList", Boolean.TRUE);
  }
  private int prevIndex = -1;

  @Override protected void processMouseMotionEvent(MouseEvent e) {
    Point pt = e.getPoint();
    int i = locationToIndex(pt);
    String s = ((ListModel<String>)getModel()).getElementAt(i);
    E s = getModel().getElementAt(i);
    Component c = getCellRenderer().getListCellRendererComponent(
        this, s, i, false, false);
    Rectangle r = getCellBounds(i, i);
    c.setBounds(r);
    if(prevIndex!=i) {
    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(cmp.getCursor());
    }else{
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }
    setCursor(
      Optional.ofNullable(SwingUtilities.getDeepestComponentAt(c, pt.x, pt.y))
        .map(Component::getCursor)
        .orElse(Cursor.getDefaultCursor()));
  }
};
}
}}

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

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

//** 参考リンク
* コメント [#qc093f08]
* 参考リンク [#reference]
[[JListのセルレンダラーとして設定したJEditorPaneからHyperlinkEventを取得する>Swing/ListCellHyperlinkListener]]

* コメント [#comment]
#comment
#comment