概要
JList
のセルハイライト表示とコンテキストメニュー表示用のJButton
をJLayer
上で描画します。
Screenshot
Advertisement
サンプルコード
class RolloverLayerUI extends LayerUI<JScrollPane> {
private final JPanel renderer = new JPanel();
private int rolloverIdx = -1;
private final Point loc = new Point(-100, -100);
private final JButton button = new JButton(new ThreeDotsIcon()) {
@Override public void updateUI() {
super.updateUI();
setBorderPainted(false);
setContentAreaFilled(false);
setFocusPainted(false);
setFocusable(false);
setOpaque(false);
setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4));
}
};
@Override public void installUI(JComponent c) {
super.installUI(c);
if (c instanceof JLayer) {
((JLayer<?>) c).setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
}
@Override public void uninstallUI(JComponent c) {
if (c instanceof JLayer) {
((JLayer<?>) c).setLayerEventMask(0);
}
super.uninstallUI(c);
}
@Override protected void processMouseEvent(MouseEvent e, JLayer<? extends JScrollPane> l) {
super.processMouseEvent(e, l);
Component c = e.getComponent();
if (c instanceof JList) {
JList<?> list = (JList<?>) c;
int id = e.getID();
if (id == MouseEvent.MOUSE_CLICKED && SwingUtilities.isLeftMouseButton(e)) {
Rectangle r = list.getCellBounds(rolloverIdx, rolloverIdx);
Dimension d = button.getPreferredSize();
r.width = l.getView().getViewportBorderBounds().width - d.width;
JPopupMenu popup = ((JComponent) c).getComponentPopupMenu();
Point pt = e.getPoint();
if (popup != null && !r.contains(pt)) {
popup.show(c, pt.x, pt.y);
}
} else if (id == MouseEvent.MOUSE_EXITED) {
list.repaint(list.getCellBounds(rolloverIdx, rolloverIdx));
rolloverIdx = -1;
loc.setLocation(-100, -100);
}
}
}
@Override protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends JScrollPane> l) {
super.processMouseMotionEvent(e, l);
Component c = e.getComponent();
if (e.getID() == MouseEvent.MOUSE_MOVED && c instanceof JList) {
JList<?> list = (JList<?>) c;
Point pt = e.getPoint();
loc.setLocation(pt);
rolloverIdx = list.locationToIndex(pt);
Rectangle r = list.getCellBounds(rolloverIdx, rolloverIdx);
r.width = l.getView().getViewportBorderBounds().width;
r.grow(0, r.height);
list.repaint(r);
}
}
@Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
JList<?> list = getList(c);
if (list != null && rolloverIdx >= 0) {
JScrollPane scroll = (JScrollPane) ((JLayer<?>) c).getView();
Graphics2D g2 = (Graphics2D) g.create();
Rectangle cellBounds = list.getCellBounds(rolloverIdx, rolloverIdx);
Component rc = getRendererComponent(list, rolloverIdx);
Dimension d = button.getPreferredSize();
cellBounds.width = scroll.getViewportBorderBounds().width - d.width;
boolean buttonRollover = !cellBounds.contains(loc);
button.getModel().setRollover(buttonRollover);
Rectangle rect = SwingUtilities.convertRectangle(list, cellBounds, c);
SwingUtilities.paintComponent(g2, rc, renderer, rect);
rect.x += rect.width;
rect.width = d.width;
g2.setPaint(rc.getBackground());
g2.fill(rect);
SwingUtilities.paintComponent(g2, button, renderer, rect);
g2.dispose();
}
}
private static JList<?> getList(JComponent layer) {
JList<?> list = null;
if (layer instanceof JLayer) {
JScrollPane scroll = (JScrollPane) ((JLayer<?>) layer).getView();
Component view = scroll.getViewport().getView();
if (view instanceof JList) {
list = (JList<?>) view;
}
}
return list;
}
private static <E> Component getRendererComponent(JList<E> list, int idx) {
E value = list.getModel().getElementAt(idx);
ListCellRenderer<? super E> r = list.getCellRenderer();
boolean isSelected = list.isSelectedIndex(idx);
boolean cellHasFocus = list.getSelectionModel().getLeadSelectionIndex() == idx;
Component c = r.getListCellRendererComponent(list, value, idx, isSelected, cellHasFocus);
if (!isSelected) {
c.setBackground(Color.GRAY);
c.setForeground(Color.WHITE);
}
return c;
}
}
View in GitHub: Java, Kotlin解説
JList
の親JScrollPane
にJLayer
を設定- セルのハイライト表示を
ListCellRenderer
ではなくLayerUI#paint(...)
をオーバーライドして、JList#getListCellRendererComponent(...)
で取得した描画用コンポーネントの文字色、背景色を変更しSwingUtilities.paintComponent(...)
で描画 - 描画領域をセル全体の幅ではなくコンテキストメニュー表示用の
JButton
の幅を除外するよう縮小する - コンテキストメニュー表示用の
JButton
も同様にSwingUtilities.paintComponent(...)
でセル右端に描画 SwingUtilities.paintComponent(...)
での描画でJButton
のMouseListener
などは反応しないため、LayerUI#processMouseEvent(...)
中でクリックされたかを判別しJList#getComponentPopupMenu()
で取得したJPopupMenu
を表示Windows
環境で右クリックだけではなく、このJButton
の左クリックで一般的?なWeb
アプリのようにJPopupMenu
が表示可能になる
- セルのハイライト表示を