1. 前言
萌新上班摸鱼的时候玩了玩Swing,想着弄个桌面精灵玩玩,结果一百度发现过气Swing,根本没多少教程,只能东找找,西凑凑,终于拼出了想要的效果。给张效果图:
本质上就是个隐藏了边框的窗口,然后把背景一换,再加点自己喜欢的功能就完成了。
2. 要点讲解
因为没有一个完整的教程,所以做的时候也踩了蛮多坑,这里记录一下以备不时之需,先上一段示例代码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TestFrame extends JDialog {
/** 主窗口 */
public TestFrame(){
// 获取图片
ImageIcon icon = new ImageIcon(this.getClass().getResource("/pics/bp.png"));
Dimension size = new Dimension(icon.getIconWidth(), icon.getIconHeight());
setAlwaysOnTop(true); // 窗体置顶
setDefaultCloseOperation(this.DISPOSE_ON_CLOSE); // 窗体关闭策略(关闭后挂起)
setSize(size); // 窗体大小和图片一致
setUndecorated(true); // 窗体无边框
setBackground(new Color(0, 0, 0, 0)); // 窗体背景色为透明(alpha = 0)
// 设置背景
JLabel background = new JLabel(icon);
background.setSize(background.getPreferredSize());
JLayeredPane.putLayer(background, Integer.MIN_VALUE); // 将背景放在最底层
getLayeredPane().add(background);
// 添加侦听器使窗口可拖动
MouseDragAdapter dragAdapter = new MouseDragAdapter();
addMouseListener(dragAdapter);
addMouseMotionListener(dragAdapter);
setVisible(true);
}
@Override
public void dispose() {
super.dispose();
// 主窗口销毁后直接强退
System.exit(0);
}
/**
* 鼠标拖动适配器
*/
private class MouseDragAdapter extends MouseAdapter {
private boolean isDragging;
final private Point oldPoint = new Point();
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
oldPoint.setLocation(e.getX(), e.getY());
isDragging = true;
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
isDragging = false;
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (isDragging) {
Component c = e.getComponent();
c.setLocation(c.getX() - oldPoint.x + e.getX(), c.getY() - oldPoint.y + e.getY());
}
}
}
}
Swing的基础知识就不拿来水了,这个网上教程还是有的,主要说一些要注意的地方
2.1. 参数配置
setAlwaysOnTop(true); // 窗体置顶
setDefaultCloseOperation(this.DISPOSE_ON_CLOSE); // 窗体关闭策略(关闭后挂起)
setSize(size); // 窗体大小和图片一致
setUndecorated(true); // 窗体无边框
setBackground(new Color(0, 0, 0, 0)); // 窗体背景色为透明(alpha = 0)
主要是窗体大小,无边框和背景色透明的设置,不然带个框或者灰底也挺丑的。
这里我的TestFrame继承自JDialog,也可以用JFrame,只不过用JDialog就不会显示在任务栏里了,置顶同样,看个人喜好设置。
2.2. 设置背景
// 设置背景
JLabel background = new JLabel(icon);
background.setSize(background.getPreferredSize());
JLayeredPane.putLayer(background, Integer.MIN_VALUE); // 将背景放在最底层
getLayeredPane().add(background);
把图片放在layeredPane底部作为背景(好像放contentPane也行,但是我自己做的时候老是有bug),注意要通过JLayeredPane.putLayer方法设置index,直接用
getLayeredPane().add(background, Integer.MIN_VALUE);
是不行的,因为实际不用这个index做层级判断…(翻了老半天源码才找到原因,太坑了)
2.3. 设置拖动
// 添加侦听器使窗口可拖动
MouseDragAdapter dragAdapter = new MouseDragAdapter();
addMouseListener(dragAdapter);
addMouseMotionListener(dragAdapter);
两种侦听器都需要,mouseListener负责鼠标点击事件,mouseMotionListener负责鼠标拖动事件
/**
* 鼠标拖动适配器
*/
private class MouseDragAdapter extends MouseAdapter {
private boolean isDragging;
final private Point oldPoint = new Point();
/** 处理鼠标按下 */
@Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
oldPoint.setLocation(e.getX(), e.getY());
isDragging = true;
}
}
/** 处理鼠标抬起 */
@Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
isDragging = false;
}
}
/** 处理鼠标拖动 */
@Override
public void mouseDragged(MouseEvent e) {
if (isDragging) {
Component c = e.getComponent();
c.setLocation(c.getX() - oldPoint.x + e.getX(), c.getY() - oldPoint.y + e.getY());
}
}
}
拖动的逻辑为:
- 当鼠标左键按下时,检测按下处的本地坐标并记录,并激活拖动状态(isDragging = true);
- 当鼠标拖动时,根据鼠标的相对位置变化,移动窗口,由于c.getX()和c.getY()获取的是窗口左上角的全局坐标,而e.getX()和e.getY()获取的是点击时鼠标的本地坐标,所以减去我们之前记录的本地坐标,就能保证相对位置变化都可看做基于窗口左上角拖动(可以尝试不用oldPoint记录坐标,拖动的时候窗口直接飞不见了XD);
- 鼠标左键抬起时,取消激活拖动状态(isDragging = false).
2.4. 窗口销毁时结束进程
@Override
public void dispose() {
super.dispose();
// 主窗口销毁后直接强退
System.exit(0);
}
重载dispose方法,在窗口被销毁时结束进程。有时候组件多了,窗口消失后进程还没关闭,debug也找不到原因,只能听大佬的,直接exit一了百了。
3.结尾
以上都是我从原码中摘出来的感觉比较关键的部分,详细的原码在此