JAVA8 Swing 简单的桌面精灵(踩坑点记录)

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.结尾

以上都是我从原码中摘出来的感觉比较关键的部分,详细的原码在此
桌面精灵GitHub仓库
写这个玩意的初衷只是想随便写写玩一下,没想到花了大量的时间…资料也查了一堆,还硬着头皮看了原码,好在最后折腾出来了。所以写代码用摸鱼的心态是很难有成果的(当然也许是我太菜了2333)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值