Swing/DropShadowPopup のバックアップ(No.15)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/DropShadowPopup へ行く。
- 1 (2007-03-22 (木) 12:29:12)
- 2 (2007-04-18 (水) 12:39:57)
- 3 (2008-04-10 (木) 18:24:18)
- 4 (2011-04-19 (火) 15:43:56)
- 5 (2011-04-19 (火) 18:55:52)
- 6 (2012-02-05 (日) 14:22:34)
- 7 (2012-02-09 (木) 13:47:39)
- 8 (2012-02-21 (火) 16:45:48)
- 9 (2012-11-23 (金) 04:40:42)
- 10 (2013-02-27 (水) 13:58:49)
- 11 (2013-03-02 (土) 05:32:25)
- 12 (2013-03-04 (月) 09:53:00)
- 13 (2013-03-05 (火) 20:22:26)
- 14 (2013-03-11 (月) 17:09:34)
- 15 (2013-08-09 (金) 13:15:04)
- 16 (2013-08-20 (火) 14:37:23)
- 17 (2013-09-12 (木) 01:13:54)
- 18 (2014-06-10 (火) 18:59:39)
- 19 (2014-09-19 (金) 16:51:25)
- 20 (2014-10-18 (土) 12:45:15)
- 21 (2014-11-25 (火) 03:03:31)
- 22 (2015-02-12 (木) 15:49:13)
- 23 (2015-03-20 (金) 15:23:39)
- 24 (2015-04-04 (土) 22:09:18)
- 25 (2016-01-27 (水) 18:20:45)
- 26 (2016-05-26 (木) 14:50:42)
- 27 (2016-09-20 (火) 04:59:27)
- 28 (2017-03-30 (木) 14:05:39)
- 29 (2017-08-15 (火) 14:13:00)
- 30 (2018-08-16 (木) 15:36:50)
- 31 (2018-12-05 (水) 18:50:12)
- 32 (2020-11-10 (火) 13:48:52)
- 33 (2022-11-06 (日) 20:06:50)
- 34 (2024-02-03 (土) 13:59:47)
- 35 (2025-01-03 (金) 08:57:02)
- 36 (2025-01-03 (金) 09:01:23)
- 37 (2025-01-03 (金) 09:02:38)
- 38 (2025-01-03 (金) 09:03:21)
- 39 (2025-01-03 (金) 09:04:02)
- 40 (2025-06-19 (木) 12:41:37)
- 41 (2025-06-19 (木) 12:43:47)
TITLE:JPopupMenuに半透明の影を付ける
Posted by aterai at 2006-07-03
JPopupMenuに半透明の影を付ける
`Robotで画面をキャプチャーするなどして、半透明の影をJPopupMenu`に付けます。
- &jar;
- &zip;
サンプルコード
class ShadowBorder extends AbstractBorder {
private final int xoff, yoff;
private final Insets insets;
private BufferedImage screen = null;
private BufferedImage shadow = null;
public ShadowBorder(int x, int y, JComponent c, Point p) {
this.xoff = x;
this.yoff = y;
this.insets = new Insets(0,0,xoff,yoff);
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) {
ex.printStackTrace();
}
}
@Override public Insets getBorderInsets(Component c) {
return insets;
}
@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.setPaint(Color.BLACK);
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;
g2d.drawImage(screen, 0, 0, c);
g2d.drawImage(shadow, 0, 0, c);
}
}
View in GitHub: Java, Kotlin解説
ポップアップメニューに半透明の影をつける際、フレームからはみ出すかどうかで異なる処理を行っています。
上記のサンプルコードは、フレームからはみ出す場合に使用する`Border`クラスです。
- フレーム内
- `
JPopupMenu#paintComponent`メソッドで半透明の影を描画しています。
- `
- フレーム外
- `
Robotを使って画面全体をキャプチャーし、これを利用して半透明の影をBorder`として作成しています。このためポップアップメニューがはみ出しても、影を付けることができますが、多少時間が掛かります。
- `
`JDK 1.7.0や、1.6.0_10以上の場合は、フレーム外でもRobotを使用せず、以下のようにJPopupMenuの上位Window`の背景色を透明にすることで影をつけることができます。
class DropShadowPopupMenu extends JPopupMenu {
private static final int off = 4;
private BufferedImage shadow = null;
private Border border = null;
@Override public boolean isOpaque() {
return false;
}
@Override public void paintComponent(Graphics g) {
((Graphics2D)g).drawImage(shadow, 0, 0, this);
super.paintComponent(g);
}
@Override public void show(Component c, int x, int y) {
if(border==null) {
Border inner = getBorder();
Border outer = BorderFactory.createEmptyBorder(0, 0, off, off);
border = BorderFactory.createCompoundBorder(outer, inner);
}
setBorder(border);
Dimension d = getPreferredSize();
int w = d.width, h = d.height;
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.setPaint(Color.BLACK);
for(int i=0;i<off;i++) {
g2.fillRoundRect(off, off, w-off-off+i, h-off-off+i, 4, 4);
}
g2.dispose();
}
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
Window pop = SwingUtilities.getWindowAncestor(DropShadowPopupMenu.this);
if(pop instanceof JWindow) {
System.out.println(pop instanceof JWindow);
pop.setBackground(new Color(0,0,0,0)); //JDK 1.7.0
//com.sun.awt.AWTUtilities.setWindowOpaque(pop, false); //JDK 1.6.0_10
}
}
});
super.show(c, x, y);
}
}
参考リンク
コメント
- キャプチャーが遅いのは画面全体を撮っているからで、必要なサイズだけにすれば結構速いようです。サンプルを修正してみたところ、毎回キャプチャするようにしても特に気にならない速度で動いてます。 -- aterai
- ソース中で`
isInRootPanel`がおかしい気がするのですが・・・ `convertPointToScreenがいらないのとreturn r.contains(pt)&&r.contains(p)`にしないとフレーム内の判定がおかしいようです -- sawshun?- ご指摘ありがとうごさいます。`
convertPointToScreenを削除して、MyPopupMenu#isInRootPanel`は以下のように修正しました。 -- aterai
- ご指摘ありがとうごさいます。`
private boolean isInRootPanel(JComponent root, Point p) {
Rectangle r = root.getBounds();
Dimension d = this.getPreferredSize();
//pointed out by sawshun
return r.contains(p.x, p.y, d.width+off, d.height+off);
}
final MyPopupMenu pop = new MyPopupMenu();
pop.add(new JMenuItem("Open"));
pop.add(new JMenuItem("Save"));
pop.add(new JMenuItem("Close"));
//pop.addSeparator(); //XXX: Nimbus
JSeparator s = new JSeparator();
s.setOpaque(true);
pop.add(s);
pop.add(new JMenuItem("Exit"));
JLabel label = new JLabel(icon);
label.setComponentPopupMenu(pop);
//JDK 1.5 label.addMouseListener(new MouseAdapter() {});
//addMouseListener(new MouseAdapter() {
// public void mouseReleased(MouseEvent e) {
// if(e.isPopupTrigger()) {
// Point pt = e.getPoint();
// pop.show(e.getComponent(), pt.x, pt.y);
// }
// repaint();
// }
//});
- `
SynthLookAndFeel(Nimbusなど)で、JSeparatorだけでなくJMenuItem`まで透明になった修正?に対応。 -- aterai - `
1.7.0_03`でなにか変更があった?のか、変な挙動をするようになったので、調査中。 -- aterai - `
exitやcloseが動作するのかと思ったのですが動かないんですよね?JPopupMenuに表示させているだけでしょうか、もしそうならExit`を押したときにフレームが終了するようなコードはどう書けばいいのでしょうか? -- hshs?- 影を付けるだけのサンプルコードなので、`
JMenuItemは名前だけのダミーになっています。「フレームを終了するコード…」は、複数のJFrame`が開いているかもしれない場合を考慮して、以下のような方法を使用するのがいいかもしれません。 -- aterai
- 影を付けるだけのサンプルコードなので、`
JMenuItem mi = new JMenuItem(new AbstractAction("Exit") {
@Override public void actionPerformed(ActionEvent e) {
JMenuItem m = (JMenuItem)e.getSource();
JPopupMenu popup = (JPopupMenu)m.getParent();
JComponent invoker = (JComponent)popup.getInvoker();
Window f = SwingUtilities.getWindowAncestor(invoker);
if(f!=null) f.dispose();
}
});
- 返信ありがとうございます、当方Netbeansで開発してまして、上記のコードをjPopupMenu1.add(この中);にnew JMenuItem以降を入れたのですが動きませんでした。よってJMenuItem m~f.dispose();までを削除し、かわりにjFrame1.setVisible(false);を入れると動作しました。 -- hshs?
- メモ: せっかくなので?、JPopupMenuなどからWindowを閉じるを作成してみました。 -- aterai
