经过反复的研究探索,终于获得了一个完美的解决方法:不用shape、不用抓图、不用workaround,真正的、彻底的、完全的、随意的在桌面上任意绘图、涂鸦、撒野,真正的属于程序员的Freedom!下面就来一起揭开这层窗户纸吧!
在程序中依次设置以下几个参数:
(不好意思,暴露我的桌面了)
通过上面方法,可以做一个任意大小、任意位置的window,在相应的桌面位置上随意显示Swing组件,或做任意Java2D画图。比如下面小例子可以在屏幕上直接画一个红色的立体矩形,而没有显示窗口:
import com.sun.awt.AWTUtilities;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setBounds(500, 500, 300, 300);
AWTUtilities.setWindowOpaque(frame, false);
JPanel pane = new JPanel() {
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.red);
g.fill3DRect(10, 10, 100, 100, true);
}
};
frame.setContentPane(pane);
frame.setVisible(true);
}
}
运行效果如下图:
窗口不再规则,窗口标题栏不再出现,如何移动窗口?按照其他类似软件的习惯做法,应当允许用鼠标直接拖拽窗体任意位置进行窗口移动。做一个鼠标监听器对窗口中的元素进行拖动监听,对窗口进行相应拖动距离的移动:
private MouseAdapter moveWindowListener = new MouseAdapter() {
private Point lastPoint = null;
@Override
public void mousePressed(MouseEvent e) {
lastPoint = e.getLocationOnScreen();
}
@Override
public void mouseDragged(MouseEvent e) {
Point point = e.getLocationOnScreen();
int offsetX = point.x - lastPoint.x;
int offsetY = point.y - lastPoint.y;
Rectangle bounds = FreeLoginUI.this.getBounds();
bounds.x += offsetX;
bounds.y += offsetY;
FreeLoginUI.this.setBounds(bounds);
lastPoint = point;
}
};
图片的切割
要做好的界面,需要一个耐心、有创意的美工大力协助,例如图片的切割就很重要。下图展示了如何从效果图进行具体切割素材:
仔细观察中间的输入区域部分,其背景是有渐变设计的。其制作方法也很简单:首先让美工帮助制作一个一个像素宽、整个panel高度的小图片作为素材;然后用这个图片创建纹理Paint;最后用这个纹理对真个panel进行fill。
private JPanel inputPane = new JPanel() {
private String backgroundImageURL = FreeUtil.getImageURL("login_background.png");
private TexturePaint paint = FreeUtil.createTexturePaint(backgroundImageURL);
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(paint);
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
}
};
肆虐你的桌面:六月飘雪!
既然窗户纸捅破了,在桌面上就随意折腾吧。这几天窗外酷热难耐,咱们就来个桌面飘雪,也许可以望梅止渴,带来丝丝清凉吧!
先准备一个雪花的png透明图片,然后在桌面上随机生成50个雪花坐标,每次paint让每个雪花的左右略微抖一下(snowX[i] += TWaverUtil.getRandomInt(5) - 3),垂直距离下坠5像素(snowY[i] += 5),再旋转个小角度。然后,用一个线程不停的repaint窗口。
雪花png图片:
程序代码如下:
public class TestSnow {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setAlwaysOnTop(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
AWTUtilities.setWindowOpaque(frame, false);
final JPanel pane = new JPanel() {
private int[] snowX = null;
private int[] snowY = null;
private int[] angles = null;
private int count = 50;
@Override
public void paint(Graphics g) {
super.paint(g);
Rectangle bounds = frame.getBounds();
if (snowX == null) {
snowX = new int[count];
for (int i = 0; i < snowX.length; i++) {
snowX[i] = TWaverUtil.getRandomInt(bounds.width);
}
snowY = new int[count];
for (int i = 0; i < snowY.length; i++) {
snowY[i] = TWaverUtil.getRandomInt(bounds.height);
}
angles = new int[count];
for (int i = 0; i < snowY.length; i++) {
angles[i] = TWaverUtil.getRandomInt(360);
}
}
Graphics2D g2d = (Graphics2D) g;
Image image = TWaverUtil.getImage("/free/test/snow.png");
for (int i = 0; i < count; i++) {
snowX[i] += TWaverUtil.getRandomInt(5) - 3;
snowY[i] += 5;
angles[i] += i / 5;
snowY[i] = snowY[i] > bounds.height ? 0 : snowY[i];
angles[i] = angles[i] > 360 ? 0 : angles[i];
int x = snowX[i];
int y = snowY[i];
int angle = angles[i];
g2d.translate(x, y);
double angleValue = Math.toRadians(angle);
g2d.rotate(angleValue);
g2d.drawImage(image, 0, 0, null);
g2d.rotate(-angleValue);
g2d.translate(-x, -y);
}
}
};
frame.setContentPane(pane);
frame.setVisible(true);
Thread thread = new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (Exception ex) {
ex.printStackTrace();
}
pane.repaint();
}
}
};
thread.start();
}
}
快快运行代码,让雪花飘起来吧!
如果愿意折腾,还可以修改代码中的:
别说你不知道怎么结束程序啊,不会Alt+F4的话,你这个程序员肯定不合格了。
当把透明窗口Frame设置特别大以后(例如10000*10000),你会发现不但界面变得极其缓慢,而且还会内存溢出。Sun的秘密不言自明了:还是使用了BufferedImage。否则,鼠标点击你画的椭圆或桌面的图标,它如何知道是点击了窗体,还是操作了桌面?只能生成内存图片,在里面进行像素判断了。要挖掘再深入的秘密,我也不清楚了,自己继续探索吧!