---
category: swing
folder: TranslucentSubMenu
title: JMenuなどから開くPopupMenuを半透明化
tags: [JMenu, JMenuItem, JPopupMenu, Translucent]
author: aterai
pubdate: 2012-10-22T18:38:06+09:00
description: JPopupMenuの親のJWindow、JMenuやJMenuItemなどを透明にして、JPopupMenuを半透明にします。
image: https://lh5.googleusercontent.com/-MKRZgWcSrRw/UIT3NRGfX9I/AAAAAAAABUk/fOYdfJmIt4g/s800/TranslucentSubMenu.png
---
* Summary [#summary]
`JPopupMenu`の親の`JWindow`、`JMenu`や`JMenuItem`などを透明にして、`JPopupMenu`を半透明にします。
#download(https://lh5.googleusercontent.com/-MKRZgWcSrRw/UIT3NRGfX9I/AAAAAAAABUk/fOYdfJmIt4g/s800/TranslucentSubMenu.png)
* Source Code Examples [#sourcecode]
#code(link){{
class TransparentMenu extends JMenu {
private JPopupMenu popupMenu;
public TransparentMenu(String title) {
super(title);
}
// https://bugs.openjdk.org/browse/JDK-4688783
private void ensurePopupMenuCreated() {
if (popupMenu == null) {
this.popupMenu = new TranslucentPopupMenu();
popupMenu.setInvoker(this);
popupListener = createWinListener(popupMenu);
}
}
@Override public JPopupMenu getPopupMenu() {
ensurePopupMenuCreated();
return popupMenu;
}
@Override public JMenuItem add(JMenuItem menuItem) {
ensurePopupMenuCreated();
menuItem.setOpaque(false);
return popupMenu.add(menuItem);
}
@Override public Component add(Component c) {
ensurePopupMenuCreated();
if (c instanceof JComponent) {
((JComponent) c).setOpaque(false);
}
popupMenu.add(c);
return c;
}
@Override public void addSeparator() {
ensurePopupMenuCreated();
popupMenu.addSeparator();
}
@Override public void insert(String s, int pos) {
if (pos < 0) {
throw new IllegalArgumentException("index less than zero.");
}
ensurePopupMenuCreated();
popupMenu.insert(new JMenuItem(s), pos);
}
@Override public JMenuItem insert(JMenuItem mi, int pos) {
if (pos < 0) {
throw new IllegalArgumentException("index less than zero.");
}
ensurePopupMenuCreated();
popupMenu.insert(mi, pos);
return mi;
}
@Override public void insertSeparator(int index) {
if (index < 0) {
throw new IllegalArgumentException("index less than zero.");
}
ensurePopupMenuCreated();
popupMenu.insert(new JPopupMenu.Separator(), index);
}
@Override public boolean isPopupMenuVisible() {
ensurePopupMenuCreated();
return popupMenu.isVisible();
}
}
}}
* Description [#explanation]
* Description [#description]
上記のサンプルでは、`JMenu`を継承する`TransparentMenu`を作成して`JMenu`自身と子の`JMenuItem`などを透明化しています。また`JMenu`から開く`JPopupMenu`も[[JPopupMenuを半透明にする>Swing/TranslucentPopupMenu]]を使用して半透明になるよう設定しています。
- [http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html Translucent and Shaped Swing Windows | Java.net] を参考に `PopupFactory#getPopup(...)`をオーバーライドし、常に`JPopupMenu`(半透明)の親に`JWindow`(完全に透明、`Heavy weight`)を使用するように設定
#code{{
PopupFactory.setSharedInstance(new TranslucentPopupFactory());
// ...
class TranslucentPopupFactory extends PopupFactory {
@Override public Popup getPopup(Component owner, Component contents, int x, int y)
throws IllegalArgumentException {
return new TranslucentPopup(owner, contents, x, y);
}
}
}}
----
- [[JPopupMenuを半透明にする>Swing/TranslucentPopupMenu]]では、`JPopupMenu#show(...)`メソッドをオーバーライドすることでポップアップが親フレームからはみ出して`Heavy weight`の`JWindow`が`JPopupMenu`の親となる場合のみ`JWindow#setBackground(ALPHA_ZERO)`などで透明化(`JPopupMenu`は半透明)
-- [https://bugs.openjdk.org/browse/JDK-7156657 Bug ID: 7156657 Version 7 doesn't support translucent popup menus against a translucent window] が原因?で`1.7.0_06`以前ではサブメニューが半透明化されない場合がある
-- `PopupFactory.setSharedInstance(new TranslucentPopupFactory())`を使用する場合はバグの影響を受けない
-- 上記のバグ?以外にも、[[JPopupMenuを半透明にする>Swing/TranslucentPopupMenu]]でサブメニューを半透明にする場合、`Heavy weight`の`JPopupMenu`に使用する`JWindow`の`ContentPane`と`JRootPane`の不透明設定(`isOpaque()`)に注意する必要がある
----
HeavyWeightWindow: win0, JPopupMenu: base
javax.swing.JPanel: false
javax.swing.JLayeredPane: false
javax.swing.JRootPane: false
----
HeavyWeightWindow: win1, JPopupMenu: sub
javax.swing.JPanel: true
javax.swing.JLayeredPane: false
javax.swing.JRootPane: true
#code{{
class TranslucentPopupMenu extends JPopupMenu {
@Override public void show(Component c, int x, int y) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
Window p = SwingUtilities.getWindowAncestor(TranslucentPopupMenu.this);
if (p != null && p instanceof JWindow) {
JWindow w = (JWindow) p;
w.setBackground(ALPHA_ZERO);
System.out.format("HeavyWeightWindow: %s, JPopupMenu: %s\n", w.getName(), getName());
Container c = (Container) w.getContentPane();
while (c != null && c instanceof JComponent) {
JComponent jc = (JComponent) c;
System.out.format("%s: %s\n", c.getClass().getName(), jc.isOpaque());
if (jc.isOpaque()) {
jc.setOpaque(false);
}
c = c.getParent();
}
} else {
System.out.println("Light weight");
}
}
});
super.show(c, x, y);
}
@Override protected void paintComponent(Graphics g) {
// ...
}}
* Reference [#reference]
- [http://today.java.net/pub/a/today/2008/03/18/translucent-and-shaped-swing-windows.html Translucent and Shaped Swing Windows | Java.net]
- [https://bugs.openjdk.org/browse/JDK-7156657 JDK-7156657 Version 7 doesn't support translucent popup menus against a translucent window]
- [[JPopupMenuを半透明にする>Swing/TranslucentPopupMenu]]
- [[JRootPaneの背景として画像を表示>Swing/RootPaneBackground]]
* Comment [#comment]
#comment
#comment