---
category: swing
folder: OverlapBorderPaintLayer
title: JLayerで隣接する別コンポーネント上に縁を描画
tags: [JLayer, JRadioButton, JPanel, Icon]
author: aterai
pubdate: 2013-12-16T00:12:50+09:00
description: JLayerを使用して隣接する別コンポーネント上にも縁や影を描画します。
image: https://lh5.googleusercontent.com/-VshDpoewqBc/Uq2wDsedThI/AAAAAAAAB8g/TFMskJO7jys/s800/OverlapBorderPaintLayer.png
---
* 概要 [#summary]
`JLayer`を使用して隣接する別コンポーネント上にも縁や影を描画します。

#download(https://lh5.googleusercontent.com/-VshDpoewqBc/Uq2wDsedThI/AAAAAAAAB8g/TFMskJO7jys/s800/OverlapBorderPaintLayer.png)

* サンプルコード [#sourcecode]
#code(link){{
class BreadcrumbLayerUI extends LayerUI<JPanel> {
  private Shape shape;
  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    if (shape != null) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);

      Rectangle r = new Rectangle(c.getWidth(), c.getHeight());
      Area area = new Area(r);
      area.subtract(new Area(shape));
      g2.setClip(area);

      g2.setPaint(new Color(0x55_66_66_66, true));
      g2.setStroke(new BasicStroke(3f));
      g2.draw(shape);
      g2.setStroke(new BasicStroke(2f));
      g2.draw(shape);

      g2.setStroke(new BasicStroke(1f));
      g2.setClip(r);
      g2.setPaint(Color.WHITE);
      g2.draw(shape);

      g2.dispose();
    }
  }

  @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);
  }

  private void update(MouseEvent e, JLayer<? extends JPanel> l) {
    int id = e.getID();
    Shape s = null;
    if (id == MouseEvent.MOUSE_ENTERED || id == MouseEvent.MOUSE_MOVED) {
      Component c = e.getComponent();
      if (c instanceof AbstractButton) {
        AbstractButton b = (AbstractButton) c;
        if (b.getIcon() instanceof ToggleButtonBarCellIcon) {
          ToggleButtonBarCellIcon icon = (ToggleButtonBarCellIcon) b.getIcon();
          Rectangle r = c.getBounds();
          AffineTransform at = AffineTransform.getTranslateInstance(r.x, r.y);
          s = at.createTransformedShape(icon.area);
        }
      }
    }
    if (!Objects.equals(s, shape)) {
      shape = s;
      l.getView().repaint();
    }
  }

  @Override protected void processMouseEvent(
      MouseEvent e, JLayer<? extends JPanel> l) {
    update(e, l);
  }

  @Override protected void processMouseMotionEvent(
      MouseEvent e, JLayer<? extends JPanel> l) {
    update(e, l);
  }
}
}}

* 解説 [#explanation]
上記のサンプルでは、`JLayer#processMouseEvent(...)`と`JLayer#processMouseMotionEvent(...)`メソッドをオーバーライドして、カーソルの下にある`JRadioButton`を取得し、その周辺に`JLayer#paint(...)`メソッドを使って影と縁を描画しています。

- `JLayer`を使用しているのでこれらの影と縁は各コンポーネントより一番手前の別レイヤーに上書き可能
- `JLayer#processMouseEvent(...)`と`JLayer#processMouseMotionEvent(...)`メソッドをオーバーライドしてカーソルの下にある`JRadioButton`を取得し、その周辺に`JLayer#paint(...)`メソッドを使って影と縁を描画
-- `JLayer`を使用しているのでこれらの影と縁は各コンポーネントより一番手前の別レイヤーに上書き可能
-- 隣接したり奥に重なったりしているコンポーネントなどの上に描画が可能

* 参考リンク [#reference]
- [[FlowLayoutでボタンを重ねてパンくずリストを作成する>Swing/BreadcrumbList]]
- [[JMenuItemの内部にJButtonを配置する>Swing/ButtonsInMenuItem]]

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