关于截屏实现透明窗体的bug


     首先我说的这个“截屏实现透明窗体”指的是java程序。

     大家百度一下不难找到很多“一模一样”的代码,其中我也转载了一篇,但是“这些”代码都有一个通病。

 创建透明窗体,其实就是通过截屏,然后把截屏截来的图片做为背景,这样给人的感觉就好像就是透明窗体。代码如下:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TransparentBackground extends JComponent {

	private JFrame frame;             // JFrame窗体
	private Image background;         // 截屏

	public TransparentBackground(JFrame frame) {  // 构造方法
		this.frame = frame;
		updateBackground();
	}

	public void updateBackground() {             // 截屏,获得背景图片
		try {
                        // Robot类用于为测试自动化、自运行演示程序和其他需要控制鼠标和键盘的应用程序生成本机系统输入事件。
			//Robot 的主要目的是便于 Java 平台实现自动测试。
			Robot rbt = new Robot();
			Toolkit tk = Toolkit.getDefaultToolkit();
			Dimension dim = tk.getScreenSize();

                        //Robot.createScreenCapture(Rectangle)截屏
			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);
                // 载入图片,x,y 是      图片的左上角       相对于       窗口的左上角       的坐标
		g.drawImage(background, offset.x, offset.y, null);
	}

	public static void main(String[] args) {

                // 创建窗体
		JFrame frame = new JFrame(" Transparent Window ");

                // 去掉窗体框
		//frame.setUndecorated(true);

		TransparentBackground bg = new TransparentBackground(frame);

                // 绘制背景(截屏)
		bg.getBackground();

                // 设置布局
		bg.setLayout(new BorderLayout());

                // 创建容器
		JPanel panel = new JPanel() {
			public void paintComponent(Graphics g) {
				g.setColor(Color.blue);
                                // 此处可以根据个人喜好填入图片的url,最好是背景透明的图片(这样才更能体现透明窗的效果)
				Image img = new ImageIcon("C:\\Users\\Administrator\\Pictures\\08.gif").getImage();
				g.drawImage(img, 0, 0, null);
			}
		};
		panel.setOpaque(false);

		bg.add("Center", panel);

		frame.getContentPane().add("Center", bg);
		//frame.pack();
		frame.setSize(200, 200);
		frame.setLocation(500, 500);
		frame.show();
	}
}

通过上面的代码你会发现貌似真的有了“透明”的效果,实际上不然,你移动一下窗体试试?你会发现,哎貌似透明效果消失了。读者肯定会认为这不简单么,我再给frame加一个移动事件ComponentListener,每次移动我就重绘repaint()一下,我也是这样认为的,当然还不能少了改变窗体大小事件,以上代码修改如下:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TransparentBackground extends JComponent implements ComponentListener{

	private JFrame frame;             // JFrame窗体
	private Image background;         // 截屏

	public TransparentBackground(JFrame frame) {  // 构造方法
		this.frame = frame;
		updateBackground();
	}

	public void updateBackground() {             // 截屏,获得背景图片
		try {
                        // Robot类用于为测试自动化、自运行演示程序和其他需要控制鼠标和键盘的应用程序生成本机系统输入事件。
			//Robot 的主要目的是便于 Java 平台实现自动测试。
			Robot rbt = new Robot();
			Toolkit tk = Toolkit.getDefaultToolkit();
			Dimension dim = tk.getScreenSize();

                        //Robot.createScreenCapture(Rectangle)截屏
			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);
                // 载入图片,x,y 是      图片的左上角       相对于       窗口的左上角       的坐标
		g.drawImage(background, offset.x, offset.y, null);
	}

	public static void main(String[] args) {

                // 创建窗体
		JFrame frame = new JFrame(" Transparent Window ");

                // 去掉窗体框
		//frame.setUndecorated(true);

		TransparentBackground bg = new TransparentBackground(frame);

                // 绘制背景(截屏),注意此处只调用了  一次 截屏 ,一次 
		bg.getBackground();

                // 设置布局
		bg.setLayout(new BorderLayout());

                // 创建容器
		JPanel panel = new JPanel() {
			public void paintComponent(Graphics g) {
				g.setColor(Color.blue);
                                // 此处可以根据个人喜好填入图片的url,最好是背景透明的图片(这样才更能体现透明窗的效果)
				Image img = new ImageIcon("C:\\Users\\Administrator\\Pictures\\08.gif").getImage();
				g.drawImage(img, 0, 0, null);
			}
		};
		panel.setOpaque(false);

		bg.add("Center", panel);

		frame.getContentPane().add("Center", bg);
		//frame.pack();
		frame.setSize(200, 200);
		frame.setLocation(500, 500);
		
		// 添加事件
		frame.addComponentListener(bg);
		
		frame.setVisible(true);
	}

	@Override // 隐藏窗体事件
	public void componentHidden(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override // 窗体移动事件
	public void componentMoved(ComponentEvent e) {
		// TODO Auto-generated method stub
		repaint();		// 重绘相当于调用public void paintComponent(Graphics g)方法
	}

	@Override // 窗体大小改变事件
	public void componentResized(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override // 
	public void componentShown(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}
}

说到这里大家是不是觉得,嗯,貌似真的达到了以假乱真的地步了,其实不然,当你改变桌面,例如在打开一个其他的应用程序,或者关一个应用程序,那么此时的桌面已经改变,你以前的截屏不在生效。有人会说这个也简单,大不了在加一个WindowFocusListener事件,问题就来了。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TransparentBackground extends JComponent implements ComponentListener, WindowFocusListener{

	private JFrame frame;             // JFrame窗体
	private Image background;         // 截屏

	public TransparentBackground(JFrame frame) {  // 构造方法
		this.frame = frame;
		updateBackground();
	}

	public void updateBackground() {             // 截屏,获得背景图片
		try {
                        // Robot类用于为测试自动化、自运行演示程序和其他需要控制鼠标和键盘的应用程序生成本机系统输入事件。
			//Robot 的主要目的是便于 Java 平台实现自动测试。
			Robot rbt = new Robot();
			Toolkit tk = Toolkit.getDefaultToolkit();
			Dimension dim = tk.getScreenSize();

                        //Robot.createScreenCapture(Rectangle)截屏
			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);
                // 载入图片,x,y 是      图片的左上角       相对于       窗口的左上角       的坐标
		g.drawImage(background, offset.x, offset.y, null);
	}

	public static void main(String[] args) {

                // 创建窗体
		JFrame frame = new JFrame(" Transparent Window ");

                // 去掉窗体框
		//frame.setUndecorated(true);

		TransparentBackground bg = new TransparentBackground(frame);

                // 绘制背景(截屏)
		bg.getBackground();

                // 设置布局
		bg.setLayout(new BorderLayout());

                // 创建容器
		JPanel panel = new JPanel() {
			public void paintComponent(Graphics g) {
				g.setColor(Color.blue);
                                // 此处可以根据个人喜好填入图片的url,最好是背景透明的图片(这样才更能体现透明窗的效果)
				Image img = new ImageIcon("C:\\Users\\Administrator\\Pictures\\08.gif").getImage();
				g.drawImage(img, 0, 0, null);
			}
		};
		panel.setOpaque(false);

		bg.add("Center", panel);

		frame.getContentPane().add("Center", bg);
		//frame.pack();
		frame.setSize(200, 200);
		frame.setLocation(500, 500);
		
		// 添加事件
		frame.addComponentListener(bg);
		
                frame.addWindowFocusListener(bg);
		frame.setVisible(true);
	}

	@Override // 隐藏窗体事件
	public void componentHidden(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override // 窗体移动事件
	public void componentMoved(ComponentEvent e) {
		// TODO Auto-generated method stub
		repaint();		// 重绘相当于调用public void paintComponent(Graphics g)方法
	}

	@Override // 窗体大小改变事件
	public void componentResized(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}

	@Override // 
	public void componentShown(ComponentEvent e) {
		// TODO Auto-generated method stub
		
	}

	
	@Override
	public void windowGainedFocus(WindowEvent arg0) {
		// TODO Auto-generated method stub
		// 重新绘制一次背景(截屏)
		updateBackground();
		// 重绘窗体
		repaint();
	}

	@Override
	public void windowLostFocus(WindowEvent arg0) {
		// TODO Auto-generated method stub
		
	}
}

写到这里maybe大家运行之后又会发现一个问题就是,背景把自己本身窗体也截进去当背景了。那么这个问题怎么解决呢?

网上大多数人都是如下解决方案(我也没在网上找到其他方案):


 把windowGainedFocus(WindowEvent arg0) 方法改成如下:

public void windowGainedFocus(WindowEvent arg0) {
		// TODO Auto-generated method stub

                frame.setVisable(false);
		// 重新绘制一次背景(截屏)
		updateBackground();
                frame.setVisable(true);
		// 重绘窗体
		repaint();
	}

其实大家如果真的如上那样修改你的程序,你会发现,啊,貌似自己的程序出问题了,在不断的闪屏。这是为什么呢?

因为这里陷入了一个死循环,你每次frame.setVisabel(false)窗体就lose focus,而frame.setVisable(true)窗体就gain focus,当gain focus是就会运行windowGainedFocus方法,而此方法又要运行frame.setVisable(false)和frame.setVisable(true),so……

 

既然这个方法不行那我们又有什么其他的办法呢?哈哈,别人没有并不代表我没有,下面就是本人自创

原理:我们可以把窗体移出桌面,在截屏

把windowGainedFocus(WindowEvent arg0) 方法改成如下:

public void windowGainedFocus(WindowEvent e) {

		
                Toolkit tk = Toolkit.getDefaultToolkit(); //Toolkit 的子类被用于将各种组件绑定到特定本机工具包实现
                   dim = tk.getScreenSize();    // 获取屏幕大小
		
		Rectangle rr = frame.getBounds();
		
		System.out.println(dim);
		
		frame.setLocation(dim.width, dim.height);
		updateBackground();
		frame.setLocation(rr.x, rr.y);
	}

 

好吧,到此终于大功告成了。 


===================  华丽的分割线 =========================

上面讲了透明背景的bug,下面封装下,把这个透明背景封装到一个panel里面,以后用的时候直接引用这个panel就行了

TransparentPanel.java

package com.szyooge.panel;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;

import javax.swing.JComponent;
import javax.swing.JFrame;

/**
 * 透明容器<br/>
 * eg:<br/>
 * 1、创建窗体<br/>
 * JFrame frame = new JFrame(" Transparent Window ");<br/>
 * 2、创建透明容器<br/>
 * TransparentPanel bg = new TransparentPanel(frame);<br/>
 * 3、把容器放入frame中<br/>
 * frame.getContentPane().add("Center", bg);<br/>
 * 
 * @author HugSunshine
 *
 */
public class TransparentPanel extends JComponent implements ComponentListener,
		WindowFocusListener {

	private static final long serialVersionUID = 4030186289286359101L;
	/**
	 * 窗体
	 */
	private Window frame;
	/**
	 * 截屏背景
	 */
	private Image background;
	/**
	 * 屏幕大小
	 */
	private Dimension screenDim;
	private Robot rbt;

	public TransparentPanel(Window frame) { // 构造方法
		this.frame = frame;
		this.initRobot();
		this.setScreenDim();
		updateBackground();

		// 添加事件
		frame.addComponentListener(this);

		frame.addWindowFocusListener(this);
	}

	private void initRobot() {
		try {
			// Robot类用于为测试自动化、自运行演示程序和其他需要控制鼠标和键盘的应用程序生成本机系统输入事件。
			// Robot 的主要目的是便于 Java 平台实现自动测试。
			rbt = new Robot();
		} catch (AWTException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取屏幕大小
	 */
	private void setScreenDim() {
		Toolkit tk = Toolkit.getDefaultToolkit(); // Toolkit的子类被用于将各种组件绑定到特定本机工具包实现
		screenDim = tk.getScreenSize(); // 获取屏幕大小
	}

	/**
	 * 截屏,获得背景图片
	 */
	public void updateBackground() { 
		try {
			// Robot.createScreenCapture(Rectangle)截屏
			background = rbt.createScreenCapture(new Rectangle(0, 0,
					(int) screenDim.getWidth(), (int) screenDim.getHeight()));
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public void paintComponent(Graphics g) {

		// 获取窗口当前位置,左上角坐标
		Point pos = this.getLocationOnScreen();
		Point offset = new Point(-pos.x, -pos.y);
		// 载入图片,x,y 是 图片的左上角 相对于 窗口的左上角 的坐标
		g.drawImage(background, offset.x, offset.y, null);
	}

	/**
	 * 隐藏窗体事件
	 */
	@Override
	public void componentHidden(ComponentEvent e) {

	}

	/**
	 * 窗体移动事件
	 */
	@Override
	public void componentMoved(ComponentEvent e) {
		repaint(); // 重绘相当于调用public void paintComponent(Graphics g)方法
	}

	/**
	 * 窗体大小改变事件
	 */
	@Override
	public void componentResized(ComponentEvent e) {

	}

	@Override
	public void componentShown(ComponentEvent e) {

	}

	@Override
	public void windowGainedFocus(WindowEvent arg0) {
		Rectangle rr = frame.getBounds();
		frame.setLocation(screenDim.width, screenDim.height);
		updateBackground();
		frame.setLocation(rr.x, rr.y);
	}

	@Override
	public void windowLostFocus(WindowEvent arg0) {

	}
}

使用方法如下:

public static void main(String[] args) {

	// 创建窗体
	JFrame frame = new JFrame(" Transparent Window ");

	// 去掉窗体框
	// frame.setUndecorated(true);

	TransparentPanel bg = new TransparentPanel(frame);

	// 绘制背景(截屏)
	// bg.getBackground();

	frame.getContentPane().add("Center", bg);
	// frame.pack();
	frame.setSize(200, 200);
	frame.setLocation(500, 500);

	frame.setVisible(true);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值