📄 transparencybaseonbgdrpl_2.java
字号:
package com.jixy.baseOnAWT;
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 java.util.Date;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TransparencyBaseOnBgdRpl_2 extends JComponent implements
ComponentListener, WindowFocusListener, Runnable {
private JFrame frame;
private Image background;
private long lastupdate = 0;
public boolean refreshRequested = true;
public TransparencyBaseOnBgdRpl_2(JFrame frame) {
this.frame = frame;
updateBackground();
frame.addComponentListener(this);
frame.addWindowFocusListener(this);
new Thread(this).start();
}
/**
* @todo 获取屏幕快照后立即更新窗口背景
*/
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) {
ex.printStackTrace();
}
}
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();
}
// 半透明窗口panel实现ComponentListener接口,
// WindowFocusListener接口和Runnable接口。Listener接口可以帮助我们捕获到窗口的移动,大小变化,和焦点变化。
// 实现Runnable接口可以使得panel生成一个线程去控制定制的repaint()方法。
//
// ComponentListener接口带有四个component开头的方法。它们都可以很方便地调用repaint()方法,所以窗口的背景
// 也就可以随着窗口的移动,大小的变化而相应地更新。还有两个是焦点处理的,它们只调用refresh(),如下示意:
/**
*
*/
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();
frame.setLocation(location);
refresh();
}
lastupdate = now;
refreshRequested = false;
}
}
} catch (Exception ex) {
// p(ex.toString());
ex.printStackTrace();
}
}
// refresh()可以保证frame可见,并适时得调用repaint()。它也会对refreshRequest变量置真(true),
// 同时保存当前时间值,现在所做的这些对接下来要做的事是非常重要的铺垫。
//
// 除了每四分之一秒被唤醒一次,用来检测是否有新的刷新的要求或者是否离上次刷新时间超过了一秒,方法run()
// 一般地处于休眠状态。如果离上次刷新超过了一秒并且frame是可见的,那么run()将保存frame的位置,隐藏frame,
// 获取一个screenshot,更新frame背景,再根据隐藏frame时保存的位置信息,重新显示已经更新了背景的frame,
// 接着调用refresh()方法。通过这样的控制,使得背景更新不至于比需要的多太多。
//
// 那么我们为什么要对用一个线程控制刷新如此长篇大论呢?一个词:递归。事件处理可以直接轻松地调用repaint(),
// 但是隐藏和显示窗口已便于获取screenshot
// 却交替了很多“得焦”和“失焦”事件。所有这些都会触发一个新的背景更新,导致窗口再次被隐藏,如此往返,将导致
// 永无止境的循环。一个新的“得焦”事件,将在执行refresh()几毫秒之后被调用,所以简单地检测isRecursing标志是
// 无法阻止循环的继续。
//
// 另外,用户任意一个改变屏幕的动作,将会随之引出一堆的事件来,而不仅仅是简单一个。应该是最后一个事件去触发
// updateBackground(),而不是第一个。为了全面解决这些问题,代码产生一个线程,然后用这个线程去监控重画
// (repaint)要求,并保证当前的执行动作是发生在过去的1000毫秒内没有发生过此动作。如果一个客户每五秒不间断地
// 产生事件(比如,寻找丢失的浏览窗口),那么只有在其它所有工作在一秒内完成才执行更新。这样就避免了,用户不至于
// 在移动东西时,窗口却消失不见了的尴尬。
//
// 另一件烦恼的事就是,我们的窗口仍旧有边框,这条边框使得我们无法完美和背景融为一体。更为痛苦的是使用
// setUndecorated(true)移除边框时,我们的标题栏和窗口控制栏也跟着移除了。可是这也算不上是什么大问题,
// 因为那类使用定形窗口的应用程序一般都具有可拖动的背景【Hack#34】
//
// 接下来,我们在下面这个简单的测试程序中把所讲的东西落实进去:
public static void main(String[] args) {
JFrame frame = new JFrame("Transparent Window");
frame.setUndecorated(true);
TransparencyBaseOnBgdRpl_2 bg = new TransparencyBaseOnBgdRpl_2(frame);
// bg.snapBackground();
bg.setLayout(new BorderLayout());
JPanel panel = new JPanel() {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
Image img = new ImageIcon("icons/boy.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();
}
}
// 用setUndecorated()来隐藏边框和标题栏。调用setOpaque(false),将隐藏默认的背景(一般为灰色)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -