先日作成したSwingによる非矩形ウィンドウはJWindowのみ可能で、JFrameでは対応できなかった。
その後、いろいろと調べて試行錯誤を繰り返してみた結果、JFrameでも同様に非矩形ウィンドウを作成することができた。以下、そのソースコード。
先日作成したソースコードを流用して、またいじくりまわしているので、汚いのはご容赦のほど。
package swing;
import java.awt.Component;
import java.awt.Container;
//import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.GeneralPath;
//import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
//import javax.swing.JPanel;
import javax.swing.JPopupMenu;
//import javax.swing.JWindow;
//import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import com.sun.awt.AWTUtilities;
public class TransSwingWindow extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String TITLE = "Swing版 半透明ウィンドウテスト";
public static final String VERSION = "Ver0.01";
public static final int DEFAULT_SIZE = 64;
Window window = null;
Shape shape = null;
Image image = null;
private Point start = null;
private Point point = null;
private JPopupMenu popupMenu = null;
public static void main(String[] args) {
// new TransSwingWindow();
// SwingUtilities.invokeLater(new Runnable(){
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// javax.swing.JFrame.setDefaultLookAndFeelDecorated(true);
new TransSwingWindow();
}
});
}
public TransSwingWindow() {
super();
getRootPane().setDoubleBuffered(true);
((JComponent)getContentPane()).setDoubleBuffered(true);
//UIをWindowsに設定
//右クリックメニューで使用
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (ClassNotFoundException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (InstantiationException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
window = this;
//イメージファイルの読み込み
ImageIcon icon = null;
int width = DEFAULT_SIZE;
int height = DEFAULT_SIZE;
try {
icon = new ImageIcon(getClass().getClassLoader().getResource("suiseiseki.png"));
shape = getImageShape(icon);
image = icon.getImage();
width = Math.max(icon.getIconWidth(), DEFAULT_SIZE);
height = Math.max(icon.getIconHeight(), DEFAULT_SIZE);
} catch (Exception e) {
System.out.println("ImageLoadError");
}
addPopupMenu();
addMouseEvent();
if (window instanceof javax.swing.JFrame) {
((JFrame)window).setTitle(TITLE + " " + VERSION);
}
this.setSize(width, height);
if (window instanceof javax.swing.JFrame) {
((JFrame)window).setUndecorated(true);
}
Container contentPane = getContentPane();
//JWindowに非矩形ウィンドウの元となる画像を描画させると
//右クリックでJPopupMenuを起動した時に表示ががおかしくなるので、
//JPanelに画像を描画させる
// Canvas panel = new Canvas(image);
// contentPane.add(panel);
//今回はJWindowに画像を描画させないで、JLabelを使用してみた
JLabel label = new JLabel(icon);
contentPane.add(label);
// pack();
if (window instanceof javax.swing.JFrame) {
((JFrame)window).setResizable(false);
((JFrame)window).setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
// this.setLocationRelativeTo(null);
this.setLocation(100, 100);
// AWTUtilities.setWindowShape(this, new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 100, 100));
AWTUtilities.setWindowShape(this, shape);
AWTUtilities.setWindowOpacity(this, (Float)0.70f);
this.setVisible(true);
}
private void addPopupMenu() {
popupMenu = new JPopupMenu();
JMenuItem menuItem = new JMenuItem();
menuItem.setText("終了" );
menuItem.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
System.exit(0);
}
});
popupMenu.add(menuItem);
}
private void addMouseEvent() {
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
Component com = (Component)e.getComponent();
switch (e.getButton()) {
case MouseEvent.BUTTON1:
popupMenu.setVisible(false);
break;
case MouseEvent.BUTTON3:
popupMenu.show(com, e.getX(), e.getY());
break;
default:
popupMenu.setVisible(false);
break;
}
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO 自動生成されたメソッド・スタブ
Component c = (Component) e.getComponent();
point = c.getLocation();
c.setLocation(point);
}
@Override
public void mousePressed(MouseEvent e) {
// TODO 自動生成されたメソッド・スタブ
start = e.getPoint();
point = e.getPoint();
// if (e.getClickCount() >= 2) {
// System.exit(0);
// }
}
});
this.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseMoved(MouseEvent e) {
// TODO 自動生成されたメソッド・スタブ
}
@Override
public void mouseDragged(MouseEvent e) {
// TODO 自動生成されたメソッド・スタブ
Component c = (Component) e.getComponent();
point = c.getLocation(point);
int x = point.x - start.x + e.getX();
int y = point.y - start.y + e.getY();
c.setLocation(x, y);
}
});
}
/**
* ImageIconからアルファ値が0でない画素からなるShapeを得る
* */
public Shape getImageShape(ImageIcon icon){
GeneralPath shape = new GeneralPath();
final BufferedImage bi = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
icon.paintIcon(null, bi.createGraphics(), 0, 0);
ColorModel cm = bi.getColorModel();
//元(上)のループが縦方向にスキャンしていたので、横方向にスキャンするように変更してみた
//いまどきのOS/PCではグラフィック処理にラスターを意識しなくても良い?
for (int y = 0; y < bi.getHeight(); y++) {
for (int x = 0; x < bi.getWidth(); x++) {
//完全に透過していなければshapeに追加
if (cm.getAlpha(bi.getRGB(x, y)) > 0) {
int start = x;
int width = 0;
while (x < bi.getWidth() && cm.getAlpha(bi.getRGB(x++, y)) > 0) { //forをwhileに変更
width++;
}
shape.append(new Rectangle(start, y, width, 1), true);
}
}
}
return shape;
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自動生成されたメソッド・スタブ
}
// private class Canvas extends JPanel {
// /**
// *
// */
// private static final long serialVersionUID = 1L;
//
//
// private Image image = null;
//
//
// public Canvas(Image image) {
// super();
// this.image = image;
// }
//
//
// public void paintComponent(Graphics g) {
// if (image == null) {
// return;
// }
//
// g.drawImage(image, 0, 0, this);
// }
// }
}
今回は、先日のものをJFrameに対応させた上で半透明処理を施してみた。
ソースコード中でウィンドウを非矩形、半透明にしている部分は、以下の部分。
AWTUtilities.setWindowShape(this, shape);
AWTUtilities.setWindowOpacity(this, (Float)0.70f);
実際に動作している様子を動画にしたものがこちら。動画の中で実行しているjarファイルはこれ。
動画サイズを抑えた結果、画質がやや荒くなってしまっているが、翠星石の形状にウィンドウが作られて、かつ半透明になっているのが確認できると思う。
ただ1点問題があって、ポップアップメニューなどの表示も非矩形・半透明の設定の影響を受けてしまい、表示が欠けてしまう(下図参照)。
この点については、私の実装方法に問題があるのか、JFrameで非矩形ウィンドウを実装した際に起きる不具合なのかは不明。
とりあえず、SwingでJWindowだけでなくJFrameでも半透明+非矩形ウィンドウが実装できたので、よしとしよう。