TITLE:JPopupMenuに半透明の影を付ける
Posted by aterai at 2006-07-03

JPopupMenuに半透明の影を付ける

Robotで画面をキャプチャーするなどして、半透明の影をJPopupMenuに付けます。
  • category: swing folder: DropShadowPopup title: JPopupMenuに半透明の影を付ける tags: [JPopupMenu, Border, Robot, JMenuItem, Translucent] author: aterai pubdate: 2006-07-03T12:55:36+09:00 description: Robotで画面をキャプチャーするなどして、半透明の影をJPopupMenuに付けます。 image: https://lh3.googleusercontent.com/_9Z4BYR88imo/TQTMBgsMvZI/AAAAAAAAAYg/QBh9VXR7P-I/s800/DropShadowPopup.png

概要

Robotで画面をキャプチャーするなどして、半透明の影をJPopupMenuに付けます。
DropShadowPopup.png

サンプルコード

#spanend
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
class ShadowBorder extends AbstractBorder {
  private final int xoff, yoff;
  private final Insets insets;
  private BufferedImage screen = null;
  private BufferedImage shadow = null;
  private final transient BufferedImage screen;
  private transient BufferedImage shadow;

  public ShadowBorder(int x, int y, JComponent c, Point p) {
    super();
    this.xoff = x;
    this.yoff = y;
    this.insets = new Insets(0,0,xoff,yoff);
    try{
    BufferedImage bi = null;
    try {
      Robot robot = new Robot();
      Dimension d = c.getPreferredSize();
      screen = robot.createScreenCapture(
          new Rectangle(p.x, p.y, d.width+xoff, d.height+yoff));
    }catch (java.awt.AWTException ex) {
      bi = robot.createScreenCapture(
          new Rectangle(p.x, p.y, d.width + xoff, d.height + yoff));
    } catch (AWTException ex) {
      ex.printStackTrace();
    }
    screen = bi;
  }
#spanadd

#spanend
  @Override public Insets getBorderInsets(Component c) {
    return insets;
    return new Insets(0, 0, xoff, yoff);
  }
  @Override public void paintBorder(Component c, Graphics g,
                                    int x, int y, int w, int h) {
    if(screen==null) return;
    if(shadow==null || shadow.getWidth()!=w || shadow.getHeight()!=h) {
#spanadd

#spanend
  @Override public void paintBorder(
        Component c, Graphics g, int x, int y, int w, int h) {
    if (screen == null) {
      return;
    }
    if (shadow == null || shadow.getWidth() != w || shadow.getHeight() != h) {
      shadow = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = shadow.createGraphics();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setComposite(
          AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .2f));
      g2.setPaint(Color.BLACK);
      for(int i=0;i<xoff;i++) {
        g2.fillRoundRect(xoff, xoff, w-xoff-xoff+i, h-xoff-xoff+i, 4, 4);
      for (int i = 0; i < xoff; i++) {
        g2.fillRoundRect(xoff, xoff, w - xoff - xoff + i, h - xoff - xoff + i, 4, 4);
      }
      g2.dispose();
    }
    Graphics2D g2d = (Graphics2D)g;
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.drawImage(screen, 0, 0, c);
    g2d.drawImage(shadow, 0, 0, c);
    g2d.setPaint(c.getBackground()); //??? 1.7.0_03
    g2d.fillRect(x, y, w - xoff, h - yoff);
    g2d.dispose();
  }
}

解説

解説

ポップアップメニューに半透明の影をつける際、フレームからはみ出すかどうかで異なる処理を行っています。
上記のサンプルコードは、フレームからはみ出す場合に使用するBorderクラスです。 上記のサンプルコードは、フレームからはみ出す場合に使用するBorderクラスです。
  • フレーム内
    • JPopupMenu#paintComponentメソッドで半透明の影を描画しています。
  • フレーム内
    • JPopupMenu#paintComponentメソッドで半透明の影を描画
  • フレーム外
    • Robotを使って画面全体をキャプチャーしこれを利用して半透明の影をBorderとして作成
    • このためポップアップメニューがはみ出しても影を付けることが可能
  • フレーム外
    • Robotを使って画面全体をキャプチャーし、これを利用して半透明の影をBorderとして作成しています。このためポップアップメニューがはみ出しても、影を付けることができますが、多少時間が掛かります。
      • -
  • 1.6.0_10以上の場合フレーム外でもRobotを使用せず以下のようにJPopupMenuの上位Windowの背景色を透明にすれば影の描画が可能
    #spanend
    #spanadd
    class DropShadowPopupMenu extends JPopupMenu {
    #spanend
      private static final int OFFSET = 4;
      private final Dimension dim = new Dimension();
      private transient BufferedImage shadow;
    
    #spandel
    **参考リンク [#jb8cc361]
    #spanend
    -[[Menuに半透明の影を付ける>Swing/MenuWithShadow]]
      @Override public void updateUI() {
        setBorder(null);
        super.updateUI();
        Border inner = getBorder();
        Border outer = BorderFactory.createEmptyBorder(0, 0, OFFSET, OFFSET);
        setBorder(BorderFactory.createCompoundBorder(outer, inner));
      }
    
    #spandel
    **コメント [#e88a4825]
    #spanend
    - キャプチャーが遅いのは画面全体を撮っているからで、必要なサイズだけにすれば結構速いようです。サンプルを修正してみたところ、毎回キャプチャするようにしても特に気にならない速度で動いてます。 -- [[aterai]] &new{2006-07-18 (火) 12:02:13};
    - ソース中でisInRootPanelがおかしい気がするのですが・・・ 
    #spandel
    //convertPointToScreenga -- [[sawshun]] &new{2006-10-05 (木) 11:20:25};
    #spanend
    #spandel
    //- #改行しようとしたらコメント入ってしまいました  
    #spanend
      @Override public boolean isOpaque() {
        return false;
      }
    #spanadd
    
    #spanend
      @Override protected void paintComponent(Graphics g) {
        // super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g.create();
        g2.drawImage(shadow, 0, 0, this);
        g2.setPaint(getBackground()); // ??? 1.7.0_03
        g2.fillRect(0, 0, getWidth() - OFFSET, getHeight() - OFFSET);
        g2.dispose();
      }
    #spanadd
    
    #spanend
      @Override public void show(Component c, int x, int y) {
        Dimension d = getPreferredSize();
        int w = d.width;
        int h = d.height;
        if (dim.width != w || dim.height != h) {
          dim.setSize(w, h);
          shadow = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
          Graphics2D g2 = shadow.createGraphics();
          g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                              RenderingHints.VALUE_ANTIALIAS_ON);
          g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .2f));
          g2.setPaint(Color.BLACK);
          for (int i = 0; i < OFFSET; i++) {
            g2.fillRoundRect(
                OFFSET, OFFSET, w - OFFSET - OFFSET + i,
                h - OFFSET - OFFSET + i, 4, 4);
          }
          g2.dispose();
        }
        EventQueue.invokeLater(() -> {
          Window top = SwingUtilities.getWindowAncestor(this);
          if (top != null && top.getType() == Window.Type.POPUP) {
            // Popup$HeavyWeightWindow
            top.setBackground(new Color(0x0, true)); // JDK 1.7.0
          }
        });
        super.show(c, x, y);
      }
    #spanadd
    }
    #spanend
    #spanadd
    

参考リンク

コメント