JAVAでの透過ウィンドウ

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Aozora0630
記事: 85
登録日時: 10年前
住所: 日本
連絡を取る:

JAVAでの透過ウィンドウ

#1

投稿記事 by Aozora0630 » 10年前

Javaで、デスクトップマスコットを作っているのですが、透過ウィンドウが作れません。
ということで、透過ウィンドウは諦めて、ここにあるコードを使ってやってみました。
Screenshot - 2015年07月20日 - 08時37分27秒.png
これで実質成功に見えるのですが、他のウィンドウを動かして、マスコットの下にやると、コンポーネントの再描画が行われず以下の様になります。
Screenshot - 2015年07月20日 - 08時40分04秒.png
タイマーで無理やり再描画させても全く変わりません。

どうすればいいのでしょう。

Sceon.java

コード:

import java.io.*;
import java.util.Properties;
import javax.swing.*;

class Sceon{
	//setting variant
	private static int WinX;
	private static int WinY;
	private static int adelay;
	private static int msgd;
	
	//component variant
	private static SceonWindow mwin;
	
	//images
	private static ImageIcon imgs[];
	private static int modeimg;
	
	private static Properties readini(String filename){
		Properties conf = new Properties();
		try {
			conf.load(new FileInputStream(filename));
		} catch (IOException e) {
			System.err.println("Cannot open " + filename + ".");
			e.printStackTrace();
			System.exit(-1);  // プログラム終了
		}
		
		return conf;
	}
	
	public static void refSetting(){
		
		Properties sysconf = readini("sysconf.ini");
		WinX = Integer.parseInt(sysconf.getProperty("WinX"));
		WinY = Integer.parseInt(sysconf.getProperty("WinY"));
		
		imgs[0] = new ImageIcon(sysconf.getProperty("Normal"));
		imgs[1] = new ImageIcon(sysconf.getProperty("Trouble"));
		imgs[2] = new ImageIcon(sysconf.getProperty("Smile"));
		imgs[3] = new ImageIcon(sysconf.getProperty("Angry"));
		modeimg = Integer.parseInt(sysconf.getProperty("DefaultMode"));
		
		adelay = Integer.parseInt(sysconf.getProperty("ActionDelay"));
		msgd = Integer.parseInt(sysconf.getProperty("MessageDelay"));
	}
	
	public static void main(String[] args){
		//Load Setting
		imgs = new ImageIcon[4];
		refSetting();
		
		//Initialize MainWindow
		mwin = new SceonWindow(WinX,WinY,adelay,msgd);
		
		//Visible Image
		mwin.SetPicture(imgs[0]);
		
		
	}
}
TransparentBackground.java

コード:

import javax.swing.*;
import java.awt.*;

public class TransparentBackground extends JComponent { 
    private JFrame frame; 
    private Image background;

public TransparentBackground(JFrame frame) {
    this.frame = frame;
    updateBackground( );
}

public void updateBackground( ) {
    try {
        Robot rbt = new Robot( );
        Toolkit tk = Toolkit.getDefaultToolkit( );
        Dimension dim = tk.getScreenSize( );
        background = rbt.createScreenCapture(
        new Rectangle(0,0,(int)dim.getWidth( ),
                          (int)dim.getHeight( )));
    } catch (Exception ex) {
        //p(ex.toString( ));何かエラー出るから消しておく
        ex.printStackTrace( );
    }
}
public void paintComponent(Graphics g) {
    Point pos = this.getLocationOnScreen( );
    Point offset = new Point(-pos.x,-pos.y);
    g.drawImage(background,offset.x,offset.y,null);
    
}
}
SceonWindow.java

コード:

import javax.swing.*;
import javax.swing.border.*;

import java.awt.*;
import java.awt.event.*;

class SceonWindow extends JFrame implements ActionListener{
	
	private TransparentBackground bg;
	
	private int WinX,WinY;
	
	private JLabel pic;
	
	private Timer timer;
	
	private JLabel msg;
	private int vt;
	private int vtd;
	
	public SceonWindow(int winw,int winh,int ad,int vt_){
		//Variant Initialize
			WinX = winw;
			WinY = winh;
			vtd = vt_;
		
		//JFrame Initialize
			Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
			setLocation(screen.width - WinX,screen.height - WinY);
			setSize(WinX,WinY);
			setAlwaysOnTop(true);//toBack();
			setUndecorated(true);
			//setBackground(new Color(0, 0, 0, 0));
			setDefaultCloseOperation(3);
			
		//bg component
			bg = new TransparentBackground(this);
			bg.setLayout(new BorderLayout( ));
			
		//JLabel Initialize
			pic = new JLabel();
			pic.setOpaque(false);
			bg.add(pic);//add(pic);
			
		//Msg Initialize
			msg = new JLabel("あいうえお",SwingConstants.CENTER);
			vt = 0;
			msg.setLayout(new BorderLayout());
			msg.setHorizontalAlignment(JLabel.CENTER);
			msg.setFont(new Font(Font.DIALOG,Font.PLAIN, 16));
			msg.setBorder(new LineBorder(Color.black,2,true));
			bg.add(msg,BorderLayout.PAGE_START);//add(msg,BorderLayout.PAGE_START);
			
		//Timer Initialize
			timer = new Timer(ad,this);
			timer.setActionCommand("update");
			Timer ptime = new Timer(10,this);
			ptime.setActionCommand("paint");
			
		//Visualize
			getContentPane().add(bg,BorderLayout.CENTER);
			setVisible(true);
			
		timer.start();
		ptime.start();
	}
	
	public void SetMsg(String setm){
		msg.setText(setm);
		vt = vtd;
	}
	
	public void SetPicture(ImageIcon img){
		pic.setIcon(img);
		pic.repaint();
	}
	
	public void actionPerformed(ActionEvent e){
		
		bg.repaint();
		
		if(e.getActionCommand()=="paint")return;
		
		System.out.println("Event start...");
		
		//Form Update
		if(vt>0){
			vt--;
			msg.setVisible(true);
		}else msg.setVisible(false);
		msg.repaint();
	}
	
}
実行はjava Sceonです。
環境はLinux beanです。

Javaバージョン

コード:

java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) Client VM (build 24.80-b11, mixed mode)

Aozora0630
記事: 85
登録日時: 10年前
住所: 日本
連絡を取る:

Re: JAVAでの透過ウィンドウ

#2

投稿記事 by Aozora0630 » 10年前

すみません。自己解決しました。
ただ単に自分がそのサイトを最後まで見てないだけでした。
あんなところに次ページのリンクがあるとは・・・。


しかし、また新しい問題にぶつかりました。
以下が改訂したソースなのですが、

TransparentBackground.java

コード:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class TransparentBackground extends JComponent
        implements ComponentListener, WindowFocusListener,
        Runnable {
    private JFrame frame;
    private Image background;
    private long lastupdate = 0;
    public boolean refreshRequested = true;
    public TransparentBackground(JFrame frame) { 
        this.frame = frame;
        updateBackground( );
        frame.addComponentListener(this); 
        frame.addWindowFocusListener(this);
        new Thread(this).start( );
    }
    public void componentShown(ComponentEvent evt) { repaint( ); }
    public void componentResized(ComponentEvent evt) { repaint( ); }
    public void componentMoved(ComponentEvent evt) { repaint( ); }
    public void componentHidden(ComponentEvent evt) { }

    public void windowGainedFocus(WindowEvent evt) { refresh( ); }    
    public void windowLostFocus(WindowEvent evt) { refresh( ); }
public void updateBackground( ) {
    try {
        Robot rbt = new Robot( );
        Toolkit tk = Toolkit.getDefaultToolkit( );
        Dimension dim = tk.getScreenSize( );
        background = rbt.createScreenCapture(
        new Rectangle(0,0,(int)dim.getWidth( ),
                          (int)dim.getHeight( )));
    } catch (Exception ex) {
        //p(ex.toString( ));
        ex.printStackTrace( );
    }
}
public void paintComponent(Graphics g) {
    Point pos = this.getLocationOnScreen( );
    Point offset = new Point(-pos.x,-pos.y);
    g.drawImage(background,offset.x,offset.y,null);
    
}
public void refresh( ) {
    if(frame.isVisible( )) {
        repaint( );
        refreshRequested = true;
        lastupdate = new Date( ).getTime( );
    }
}
public void run( ) {
    try {
        while(true) {
            Thread.sleep(250);
            long now = new Date( ).getTime( );
            if(refreshRequested &&
                ((now - lastupdate) > 1000)) {
                if(frame.isVisible( )) {
                    Point location = frame.getLocation( );
                    frame.hide();
                    updateBackground();
                    frame.show(true);
                frame.setLocation(location);
                    refresh( );
                }
                lastupdate = now;
                refreshRequested = false;
                }
            }
        } catch (Exception ex) {
            //p(ex.toString( ));
            ex.printStackTrace( );
        } 
    }
}
これを実行するとウィンドウが1秒ごとに消えて、点いてが繰り替えされてとても醜くなってしまいます。
からといって、62,64行目のshowとhideを消すと、画面の更新が行われなくなります。

どうすればウィンドウが点滅しなくなるのでしょうか。
題名詐欺になりそうですが、回答お願いします。

ISLe()

Re: JAVAでの透過ウィンドウ

#3

投稿記事 by ISLe() » 10年前

こちらで実験したところでは

Windows 7 とJava 8の組み合わせ
JFrameの背景色にアルファ値を0にしたColorを設定するだけで色の付いたところだけクリックに反応
透過部分の再描画も問題なし

VirvualBoxゲストのUbuntu15.04とJava 7の組み合わせ
上と同じコードでJava7をターゲットにコンパイルした
透過している部分も含めてJFrameのサイズでクリックに反応
透過部分の再描画は問題なし
なぜか色の付いている部分が半透明になる

という具合で環境によって大きく差があるようです。

これはJavaのVMが対応してくれるのを待つしかないかと。
期待薄ですが。

背景をキャプチャする場合、キャプチャする際は自分のウィンドウは消さなくてはいけないので、そこはどうしようもないですね。

思い付きですが
アプリとして、数秒ごとにポーズ(画像)が変わるような仕様にして
ポーズ(画像)が変わる際に、透明度でフェードアウト・インする
で、完全にフェートアウトした瞬間にキャプチャする
といった感じでごまかせないですかね。

Aozora0630
記事: 85
登録日時: 10年前
住所: 日本
連絡を取る:

Re: JAVAでの透過ウィンドウ

#4

投稿記事 by Aozora0630 » 10年前

30億のデバイスで走るjavaじゃないのかよおおお。。

>思い付きですが・・・・
確かにそれで行けるかもしれませんが、フェードアウトする間はやっぱり上の画像みたいにおかしくなってしまいますよね。。

ここは潔く諦めてJavaの更新を待とうと思います。
返信有難うございました。。

閉鎖

“C言語何でも質問掲示板” へ戻る