JTreeに設定したJPopupMenuが非表示の場合のみJTreeの行をロールオーバー状態で描画する
Total: 38
, Today: 2
, Yesterday: 6
Posted by aterai at
Last-modified:
Summary
JTree
にマウスカーソル下の行をロールオーバー描画する機能を追加し、JPopupMenu
が表示されている場合はそのロールオーバー状態を維持するよう設定します。
Screenshot

Advertisement
Source Code Examples
class FileSystemViewTree extends JTree {
private static final Color SELECTED_COLOR = new Color(0x00_78_D7);
private static final Color ROLLOVER_COLOR = new Color(0x64_96_C8);
protected int rollOverRowIndex = -1;
protected transient MouseAdapter rolloverHandler;
private transient FileSystemView fileSystemView;
protected FileSystemViewTree() {
super();
}
@Override protected void paintComponent(Graphics g) {
int[] sr = getSelectionRows();
if (sr == null) {
super.paintComponent(g);
return;
}
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
Graphics2D g2 = (Graphics2D) g.create();
if (rollOverRowIndex >= 0) {
g2.setPaint(ROLLOVER_COLOR);
Rectangle rect = getRowBounds(rollOverRowIndex);
g2.fillRect(0, rect.y, getWidth(), rect.height);
}
g2.setPaint(SELECTED_COLOR);
Arrays.stream(sr).mapToObj(this::getRowBounds)
.forEach(r -> g2.fillRect(0, r.y, getWidth(), r.height));
super.paintComponent(g);
if (hasFocus()) {
Optional.ofNullable(getLeadSelectionPath()).ifPresent(path -> {
Rectangle r = getRowBounds(getRowForPath(path));
g2.setPaint(SELECTED_COLOR.darker());
g2.drawRect(0, r.y, getWidth() - 1, r.height - 1);
});
}
g2.dispose();
}
@Override public void updateUI() {
setCellRenderer(null);
removeMouseListener(rolloverHandler);
removeMouseMotionListener(rolloverHandler);
super.updateUI();
setUI(new WholeRowSelectTreeUI());
UIManager.put("Tree.repaintWholeRow", Boolean.TRUE);
fileSystemView = FileSystemView.getFileSystemView();
addTreeSelectionListener(new FolderSelectionListener(fileSystemView));
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
setCellRenderer((tree, value, selected, expanded, leaf, row, focus) -> {
Component c = renderer.getTreeCellRendererComponent(
tree, value, selected, expanded, leaf, row, false);
boolean rollover = row == rollOverRowIndex;
updateFgc(c, renderer.getTextSelectionColor(), rollover);
updateBgc(c, tree.getBackground(), selected, rollover);
updateIcon(c, value, selected);
return c;
});
setOpaque(false);
rolloverHandler = new RolloverHandler();
addMouseListener(rolloverHandler);
addMouseMotionListener(rolloverHandler);
EventQueue.invokeLater(() -> setModel(makeFileTreeModel(fileSystemView)));
}
public void updateRolloverIndex() {
EventQueue.invokeLater(() -> {
Point pt = getMousePosition();
if (pt == null) {
clearRollover();
} else {
updateRolloverIndex(pt);
}
});
}
public void updateRolloverIndex(Point pt) {
int row = getRowForLocation(pt.x, pt.y);
boolean isPopupVisible = getComponentPopupMenu().isVisible();
if (rollOverRowIndex != row && !isPopupVisible) {
rollOverRowIndex = row;
repaint();
}
}
private void clearRollover() {
rollOverRowIndex = -1;
repaint();
}
protected class RolloverHandler extends MouseAdapter {
@Override public void mouseMoved(MouseEvent e) {
updateRolloverIndex(e.getPoint());
}
@Override public void mouseEntered(MouseEvent e) {
updateRolloverIndex(e.getPoint());
}
@Override public void mouseExited(MouseEvent e) {
boolean isPopupVisible = getComponentPopupMenu().isVisible();
if (!isPopupVisible) {
clearRollover();
}
}
}
}
View in GitHub: Java, KotlinExplanation
JTree
にMouseAdapter
を追加し、マウスカーソルが移動したらJTree#getRowForLocation(x, y)
メソッドでマウスカーソル下の行番号を記憶し、その行の文字色はセルレンダラー、背景色はJTree#paintComponent(...)
をオーバーライドして変更- デフォルトでは
JTree#setComponentPopupMenu(JPopupMenu)
で設定したJPopupMenu
が表示されるとmouseExited
イベントが発生したり、そのJPopupMenu
が表示中でもJTree
上にマウスカーソルが移動するとmouseMoved
イベントが発生してロールオーバー状態の行番号が変化してしまう - このサンプルではマウスカーソル移動時に
JPopupMenu
が表示中出ない場合のみロールオーバー状態の行番号を変更するよう設定してこれを回避 - さらに
JPopupMenu
側にはPopupMenuListener
を追加し、JPopupMenu
が非表示になる際にマウスカーソル位置をJTree#getMousePosition()
で取得してロールオーバー状態の行番号を更新するJTree#getMousePosition()
で取得したPoint
がnull
の場合、マウスカーソルはJTree
の外に存在するのでロールオーバー状態はクリアする
- デフォルトでは