Java简易系统监视器system-monitoring:实时显示CPU使用率、内存使用率、笔记本电脑电池剩余电量、时间(时、分、秒)。创建系统托盘,设置系统托盘菜单,窗体置顶显示。通过jna调用dll文件读取电池数据。
目录
效果图
功能说明
项目与工具
项目说明
目录结构
核心代码
Window.java
Menu.java
Dll.java
AppUtil.java
BatteryLabel.java
C++创建dll
BatteryMonitor.h
BatteryMonitor.cpp
依赖
Github
Gitee
参考链接
后记
效果图 功能说明
- Java简易系统监视器system-monitoring:实时显示CPU使用率、内存使用率、笔记本电脑电池剩余电量、时间(时、分、秒)。CPU使用率、内存使用率和时间每秒更新一次,笔记本电脑电池剩余电量每15秒更新一次。创建系统托盘,设置系统托盘菜单,窗体置顶显示。系统托盘按钮:
- 移动(或固定):点击后可移动或固定窗体。刷新:点击可刷新窗体,当窗体显示异常时可使用。布局:可更改窗体布局,可选“单列布局”、“双列布局”。(效果图为双列布局)显示:可选择需要显示的参数,可勾选“CPU”、“内存”、“电量”、“时间”。退出程序:点击可退出程序。
Maven、Java 8、Swing、maven-assembly-plugin(jar-with-dependencies)、jna、dll
项目说明- 使用jna调用C++生成的动态链接库dll文件监控电池剩余电量。生成的system-monitoring-2.0.0-jar-with-dependencies.jar需与resources中的images文件夹和BatteryMonitor.dll文件同级。(与代码中读取相关文件路径的写法有关)
由于代码较多,此处就不全部贴出来了,需要完整代码的小伙伴请移步GitHub或Gitee。
Window.javapackage cxzgwing; import java.awt.*; import java.time.Clock; import javax.swing.*; import javax.swing.border.EmptyBorder; import cxzgwing.judgement.WindowMovable; import cxzgwing.label.impl.BatteryLabel; import cxzgwing.label.impl.CpuLabel; import cxzgwing.label.impl.MemoryLabel; import cxzgwing.label.impl.TimeLabel; import cxzgwing.listener.frameDragListener; import cxzgwing.listener.TrayIconMouseListener; import cxzgwing.task.BatteryMonitorTask; import cxzgwing.task.CpuMemoryTimeMonitorTask; import cxzgwing.task.GCTask; import cxzgwing.utils.AppUtil; import cxzgwing.utils.LabelLayout; import cxzgwing.utils.ThreadUtil; public class Window extends Jframe { private Menu menu; private WindowMovable windowMovable; private CpuLabel cpuLabel; private MemoryLabel memoryLabel; private BatteryLabel batteryLabel; private TimeLabel timeLabel; private Font font; private EmptyBorder emptyBorder; private GridLayout gridLayout; // 1-单列布局,2-双列布局 private int labelLayout; // 窗体位置最大横坐标 private int windowMaxX; // 窗体位置最大纵坐标 private int windowMaxY; public Window() throws Exception { // 初始化窗体是否可移动 windowMovable = new WindowMovable(false); font = new Font("黑体", Font.PLAIN, 16); labelLayout = LabelLayout.DOUBLE; // 设置显示标签边距 emptyBorder = new EmptyBorder(0, 5, 0, 0); // 设置布局 gridLayout = new GridLayout(2, 2); // 初始化窗体 initWindow(); // 设置CPU使用率显示标签 initCpuJLabel(); // 内存使用率显示标签 initMemJLabel(); // 电池电量百分比显示标签 initBatteryJLabel(); // 时间显示标签 initTimeJLabel(); // 初始化托盘菜单 initMenu(); // 设置窗体是否可移动监听 setWindowDragListener(); // 添加窗体状态监听,使窗体一直显示 setWindowAlwaysShow(); // 添加系统托盘 setSystemTray(); // 显示 this.setVisible(true); // 监控 monitoring(); } private void initMenu() { menu = new Menu(this, windowMovable, cpuLabel, memoryLabel, batteryLabel, timeLabel); } private void initWindow() { // 隐藏任务栏图标 this.setType(Jframe.Type.UTILITY); // 设置窗口置顶 this.setAlwaysonTop(true); // 设置无边框 this.setUndecorated(true); // 设置背景色 this.setBackground(new Color(0, 0, 0, 80)); // 设置窗体大小 this.setSize(150, 40); // 设置布局 this.setLayout(gridLayout); AppUtil.initWindowLocation(this); } private void setWindowDragListener() { // 设置鼠标监听,可通过鼠标移动窗体位置 // 通过系统托盘菜单中的第一个按钮(移动 / 固定)来控制是否能移动窗体 frameDragListener frameDragListener = new frameDragListener(this, windowMovable); this.addMouseListener(frameDragListener); this.addMouseMotionListener(frameDragListener); } private void initTimeJLabel() { // 时间显示标签 timeLabel = new TimeLabel(AppUtil.getTime()); timeLabel.setFont(font); timeLabel.setForeground(Color.WHITE); timeLabel.setBorder(emptyBorder); gridLayout.addLayoutComponent("timeLabel", timeLabel); this.add(timeLabel); } private void initBatteryJLabel() { // 电池电量百分比显示标签 batteryLabel = new BatteryLabel(AppUtil.getBatteryPercent()); batteryLabel.setFont(font); batteryLabel.setForeground(Color.WHITE); batteryLabel.setBorder(emptyBorder); gridLayout.addLayoutComponent("batteryLabel", batteryLabel); this.add(batteryLabel); } private void initMemJLabel() { // 内存使用率显示标签 memoryLabel = new MemoryLabel(AppUtil.getMemoryLoad()); memoryLabel.setFont(font); memoryLabel.setForeground(Color.WHITE); memoryLabel.setBorder(emptyBorder); gridLayout.addLayoutComponent("memoryLabel", memoryLabel); this.add(memoryLabel); } private void initCpuJLabel() { // CPU使用率显示标签 cpuLabel = new CpuLabel(AppUtil.getSystemCpuLoad()); cpuLabel.setFont(font); cpuLabel.setForeground(Color.WHITE); cpuLabel.setBorder(emptyBorder); gridLayout.addLayoutComponent("cpuLabel", cpuLabel); this.add(cpuLabel); } private void monitoring() { ThreadUtil.addTask(new CpuMemoryTimeMonitorTask(cpuLabel, memoryLabel, timeLabel)); ThreadUtil.addTask(new BatteryMonitorTask(batteryLabel)); ThreadUtil.addTask(new GCTask()); } private void setSystemTray() throws Exception { try { if (SystemTray.isSupported()) { // 获取当前平台的系统托盘 SystemTray tray = SystemTray.getSystemTray(); // 加载一个图片用于托盘图标的显示 Image image = Toolkit.getDefaultToolkit() .getImage(Clock.class.getResource("/images/system-monitoring-icon.png")); // 创建点击图标时的d出菜单方案三:Jframe + JPopupMenu TrayIcon trayIcon = new TrayIcon(image, "System Monitoring"); trayIcon.addMouseListener(new TrayIconMouseListener(menu)); // 托盘图标自适应尺寸 trayIcon.setImageAutoSize(true); // 添加托盘图标到系统托盘 tray.add(trayIcon); } else { throw new Exception("当前系统不支持系统托盘"); } } catch (Exception e) { e.printStackTrace(); throw e; } } private void setWindowAlwaysShow() { this.addWindowStateListener(e -> { int newState = e.getNewState(); if (Jframe.IConIFIED == newState) { this.setState(Jframe.NORMAL); } }); } public void setWindowMaxX(int windowMaxX) { this.windowMaxX = windowMaxX; } public void setWindowMaxY(int windowMaxY) { this.windowMaxY = windowMaxY; } public int getWindowMaxX() { return this.windowMaxX; } public int getWindowMaxY() { return this.windowMaxY; } public int getLabelLayout() { return labelLayout; } public void setLabelLayout(int labelLayout) { this.labelLayout = labelLayout; } }Menu.java
package cxzgwing; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Objects; import javax.swing.*; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import cxzgwing.judgement.WindowMovable; import cxzgwing.label.LabelAdaptor; import cxzgwing.label.impl.BatteryLabel; import cxzgwing.label.impl.CpuLabel; import cxzgwing.label.impl.MemoryLabel; import cxzgwing.label.impl.TimeLabel; import cxzgwing.utils.AppUtil; import cxzgwing.utils.LabelLayout; public class Menu { private Jframe jframe; private JPopupMenu jPopupMenu; private Font font; private Window window; private WindowMovable windowMovable; private CpuLabel cpuLabel; private MemoryLabel memoryLabel; private BatteryLabel batteryLabel; private TimeLabel timeLabel; private static final int WIDTH = 75; private static final int HEIGHT = 110; public Menu(Window window, WindowMovable windowMovable, CpuLabel cpuLabel, MemoryLabel memoryLabel, BatteryLabel batteryLabel, TimeLabel timeLabel) { this.window = window; this.windowMovable = windowMovable; this.cpuLabel = cpuLabel; this.memoryLabel = memoryLabel; this.batteryLabel = batteryLabel; this.timeLabel = timeLabel; this.font = new Font("宋体", Font.PLAIN, 13); init(); addJPopupMenuListener(); } private void hiddenframeIfShowing() { if (jframe.isShowing()) { jframe.setVisible(false); } } private void hiddenJPopupMenuIfShowing() { if (jPopupMenu.isShowing()) { jPopupMenu.setVisible(false); } } private void addJPopupMenuListener() { jPopupMenu.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {} @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { hiddenframeIfShowing(); } @Override public void popupMenuCanceled(PopupMenuEvent e) {} }); } private void init() { setJframe(); setJPopupMenu(); } private void setJPopupMenu() { jPopupMenu = new JPopupMenu(); jPopupMenu.setSize(WIDTH, HEIGHT); setMovableJMenu(); setRefreshJMenu(); setLayoutJMenu(); setDisplayJMenu(); setExitJMenu(); } private void setExitJMenu() { JMenuItem exitJMenuItem = new JMenuItem("退出程序"); exitJMenuItem.setFont(font); exitJMenuItem.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { System.exit(0); } }); jPopupMenu.add(exitJMenuItem); } private void setDisplayJMenu() { JMenu displayJMenu = new JMenu("显示"); displayJMenu.setFont(font); JCheckBox cpuJCheckBox = new JCheckBox("CPU"); cpuJCheckBox.setFont(font); cpuJCheckBox.setSelected(true); cpuJCheckBox.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { stateChange(cpuLabel, cpuJCheckBox.isSelected()); } }); JCheckBox memoryJCheckBox = new JCheckBox("内存"); memoryJCheckBox.setFont(font); memoryJCheckBox.setSelected(true); memoryJCheckBox.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { stateChange(memoryLabel, memoryJCheckBox.isSelected()); } }); JCheckBox batteryJCheckBox = new JCheckBox("电量"); batteryJCheckBox.setFont(font); batteryJCheckBox.setSelected(true); batteryJCheckBox.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { stateChange(batteryLabel, batteryJCheckBox.isSelected()); } }); JCheckBox timeJCheckBox = new JCheckBox("时间"); timeJCheckBox.setFont(font); timeJCheckBox.setSelected(true); timeJCheckBox.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { stateChange(timeLabel, timeJCheckBox.isSelected()); } }); displayJMenu.add(cpuJCheckBox); displayJMenu.add(memoryJCheckBox); displayJMenu.add(batteryJCheckBox); displayJMenu.add(timeJCheckBox); jPopupMenu.add(displayJMenu); } private void stateChange(LabelAdaptor label, boolean isSelected) { // 在复选框除按下鼠标,移除复选框区域后松开鼠标,此时复选框状态不变,无需 *** 作 if ((label.isDisplay() && isSelected) || (!label.isDisplay() && !isSelected)) { return; } label.setDisplay(isSelected); windowRemoveAll(); reloadWindow(); refreshWindow(); } private void reloadWindow() { int count = getDisplayLabelCount(); GridLayout gridLayout = updateWindowLabelLayout(count); reloadLabel(gridLayout); } private void reloadLabel(GridLayout gridLayout) { // 由于不显示标签时,不读取相关数据,所以重新显示时需要更新数据 if (cpuLabel.isDisplay()) { cpuLabel.setText(AppUtil.getSystemCpuLoad()); if (LabelLayout.DOUBLE == window.getLabelLayout() && !Objects.isNull(gridLayout)) { gridLayout.addLayoutComponent("cpuLabel", cpuLabel); } window.add(cpuLabel); } if (memoryLabel.isDisplay()) { memoryLabel.setText(AppUtil.getMemoryLoad()); if (LabelLayout.DOUBLE == window.getLabelLayout() && !Objects.isNull(gridLayout)) { gridLayout.addLayoutComponent("memoryLabel", memoryLabel); } window.add(memoryLabel); } if (batteryLabel.isDisplay()) { batteryLabel.setText(AppUtil.getBatteryPercent()); if (LabelLayout.DOUBLE == window.getLabelLayout() && !Objects.isNull(gridLayout)) { gridLayout.addLayoutComponent("batteryLabel", batteryLabel); } window.add(batteryLabel); } if (timeLabel.isDisplay()) { timeLabel.setText(AppUtil.getTime()); if (LabelLayout.DOUBLE == window.getLabelLayout() && !Objects.isNull(gridLayout)) { gridLayout.addLayoutComponent("timeLabel", timeLabel); } window.add(timeLabel); } } private int getDisplayLabelCount() { int count = 0; if (cpuLabel.isDisplay()) { count++; } if (memoryLabel.isDisplay()) { count++; } if (batteryLabel.isDisplay()) { count++; } if (timeLabel.isDisplay()) { count++; } return count; } private GridLayout updateWindowLabelLayout(int count) { GridLayout gridLayout = null; if (LabelLayout.SINGLE == window.getLabelLayout()) { // 单列布局 window.setSize(window.getWidth(), 20 * count); } else if (LabelLayout.DOUBLE == window.getLabelLayout()) { // 双列布局 switch (count) { case 1: window.setLayout(gridLayout = new GridLayout(1, 1)); window.setSize(75, 20); break; case 2: window.setLayout(gridLayout = new GridLayout(1, 2)); window.setSize(150, 20); break; case 3: case 4: window.setLayout(gridLayout = new GridLayout(2, 2)); window.setSize(150, 40); break; default: window.setLayout(gridLayout = new GridLayout(2, 2)); window.setSize(150, 40); } } AppUtil.initWindowLocation(window); return gridLayout; } private void windowRemoveAll() { window.remove(cpuLabel); window.remove(memoryLabel); window.remove(batteryLabel); window.remove(timeLabel); } private void setLayoutJMenu() { JMenu layoutJMenu = new JMenu("布局"); layoutJMenu.setFont(font); JMenuItem singleJMenuItem = new JMenuItem("单列布局"); singleJMenuItem.setFont(font); singleJMenuItem.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { window.setLabelLayout(LabelLayout.SINGLE); window.setSize(85, 80); window.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 1)); reloadWindow(); AppUtil.initWindowLocation(window); refreshWindow(); } }); JMenuItem doubleJMenuItem = new JMenuItem("双列布局"); doubleJMenuItem.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { window.setLabelLayout(LabelLayout.DOUBLE); window.setSize(150, 40); window.setLayout(new GridLayout(2, 2)); reloadWindow(); AppUtil.initWindowLocation(window); refreshWindow(); } }); doubleJMenuItem.setFont(font); layoutJMenu.add(singleJMenuItem); layoutJMenu.add(doubleJMenuItem); jPopupMenu.add(layoutJMenu); } private void setRefreshJMenu() { JMenuItem refreshJMenuItem = new JMenuItem("刷新"); refreshJMenuItem.setFont(font); refreshJMenuItem.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { refreshWindow(); } }); jPopupMenu.add(refreshJMenuItem); } private void refreshWindow() { window.setVisible(false); window.setVisible(true); } private void setMovableJMenu() { JMenuItem movableJMenuItem = new JMenuItem("移动"); movableJMenuItem.setFont(font); movableJMenuItem.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { windowMovable.setValue(!windowMovable.getValue()); if (windowMovable.isTrue()) { movableJMenuItem.setText("固定"); } else { movableJMenuItem.setText("移动"); } } }); jPopupMenu.add(movableJMenuItem); } private void setJframe() { this.jframe = new Jframe(); // 设置无边框 jframe.setUndecorated(true); // 隐藏任务栏图标 jframe.setType(Jframe.Type.UTILITY); // 设置窗口置顶 jframe.setAlwaysonTop(true); // 设置背景颜色 jframe.setBackground(new Color(255, 255, 255, 255)); // 设置窗体大小 jframe.setSize(WIDTH, HEIGHT); // 设置布局 jframe.setLayout(new GridLayout(5, 1)); } public void display(MouseEvent mouseEvent) { hiddenframeIfShowing(); hiddenJPopupMenuIfShowing(); jframe.setLocation(mouseEvent.getX() - WIDTH, mouseEvent.getY() - HEIGHT); jframe.setVisible(true); jPopupMenu.show(jframe, 0, 0); // System.out.println("Width=" + jPopupMenu.getWidth() + "Height=" + // jPopupMenu.getHeight()); } }Dll.java
package cxzgwing.dll; import com.sun.jna.Library; import com.sun.jna.Native; public interface Dll extends Library { Dll dll = Native.load("BatteryMonitor", Dll.class); int BatteryPercent(); }AppUtil.java
package cxzgwing.utils; import java.awt.*; import java.lang.management.ManagementFactory; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import com.sun.management.OperatingSystemMXBean; import cxzgwing.Window; import cxzgwing.dll.Dll; public class AppUtil { private static OperatingSystemMXBean systemMXBean; static { systemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); } public static String getTime() { return LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")); } public static String getMemoryLoad() { double totalPhysicalMemorySize = systemMXBean.getTotalPhysicalMemorySize(); double freePhysicalMemorySize = systemMXBean.getFreePhysicalMemorySize(); double value = freePhysicalMemorySize / totalPhysicalMemorySize; return "M: " + String.format("%.1f", (1 - value) * 100) + "%"; } public static String getSystemCpuLoad() { return "C: " + String.format("%.1f", systemMXBean.getSystemCpuLoad() * 100) + "%"; } public static String getBatteryPercent() { int value = 225; try { value = Dll.dll.BatteryPercent(); } catch (Exception e) { e.printStackTrace(); } return "B: " + value + "%"; } public static void initWindowLocation(Window window) { // 获得窗体宽 int windowWidth = window.getWidth(); // 获得窗体高 int windowHeight = window.getHeight(); // 定义工具包 Toolkit kit = Toolkit.getDefaultToolkit(); // 获取屏幕的尺寸 Dimension screenSize = kit.getScreenSize(); // 获取屏幕的宽 int screenWidth = screenSize.width; // 获取屏幕的高 int screenHeight = screenSize.height; // 获取任务栏 Insets screenInsets = kit.getScreenInsets(window.getGraphicsConfiguration()); window.setWindowMaxX(screenWidth - windowWidth); window.setWindowMaxY(screenHeight - windowHeight - screenInsets.bottom); // 设置窗口相对于指定组件的位置:置于屏幕的中央 // frame.setLocationRelativeTo(null); // 设置窗体默认在屏幕右下角 window.setLocation(window.getWindowMaxX(), window.getWindowMaxY()); } }BatteryLabel.java
package cxzgwing.label.impl; import cxzgwing.label.LabelAdaptor; public class BatteryLabel extends LabelAdaptor { private boolean display; public BatteryLabel(String text) { this.setText(text); this.display = true; } @Override public boolean isDisplay() { return display; } @Override public void setDisplay(boolean display) { this.display = display; } }C++创建dll BatteryMonitor.h
#ifdef BATTERYMONITOR_EXPORTS #define BATTERYMONITOR_API __declspec(dllexport) #else #define BATTERYMONITOR_API __declspec(dllimport) #endif extern "C" BATTERYMONITOR_API int BatteryPercent();BatteryMonitor.cpp
#include "pch.h" #include依赖#define BATTERYMONITOR_EXPORTS #include "BatteryMonitor.h" #include using namespace std; BATTERYMONITOR_API int BatteryPercent(){ SYSTEM_POWER_STATUS sysPowerStatus; GetSystemPowerStatus(&sysPowerStatus); return (int)sysPowerStatus.BatteryLifePercent; }
Github4.0.0 cxzgwing system-monitoring2.0.0 system-monitoring UTF-8 1.8 1.8 net.java.dev.jna jna5.5.0 maven-clean-plugin 3.1.0 maven-resources-plugin 3.0.2 maven-compiler-plugin 3.8.0 maven-surefire-plugin 2.22.1 maven-jar-plugin 3.0.2 maven-install-plugin 2.5.2 maven-deploy-plugin 2.8.2 maven-site-plugin 3.7.1 maven-project-info-reports-plugin 3.0.0 maven-assembly-plugin cxzgwing.App jar-with-dependencies package assembly
GitHub - cxzgwing/system-monitoring: Java Swing applet, real-time monitoring system CPU usage and memory usage.
Giteesystem-monitoring: Java Swing applet, real-time monitoring system CPU usage and memory usage.
参考链接[1] 晨曦之光Wing.Java简易系统监视器. 2021-06-13 18:08:36
https://blog.csdn.net/qq_36533690/article/details/117881862
[2] xietansheng.Java Swing 图形界面开发(目录).2017-05-30 23:50:42
https://xiets.blog.csdn.net/article/details/72814492
[3] lucky__cc.【Swing】Java Swing JPopupMenu:d出式菜单. 2019-08-06 22:03:34
https://blog.csdn.net/qq1437722579/article/details/98663286
[4] 艾斯曼.一个找了很久的API函数---GetSystemPowerStatus.2013-01-23 21:47:51
https://blog.csdn.net/wangqiulin123456/article/details/8535809
[5] 晨曦之光Wing.Java调用dll文件.2021-11-10 11:06:51
https://blog.csdn.net/qq_36533690/article/details/121239139
此次是在之前的基础之上修改的,由两个问题引发,一是笔记本电池之前鼓包,买了新电池,想要实时查看电量。二是全屏看视频时有时候想看时间(全屏看视频时,这个窗体会一直显示)。所以就加上了显示电量和时间两个指标。
经过一番查询,发现C++可以读取电池数据,C++创建动态链接库dll文件,再通过Java使用jna调用dll文件。于是就自己干!写C++,做dll!(我发现用C++创建动态链接库dll文件会上瘾,没办法,C++太强大了)
进一步考虑到有些小伙伴可能用台式机,不需要电量信息,甚至不需要CPU使用率或者内存使用率或者时间,干脆就做成动态的算了,于是托盘菜单新增了“显示”按钮,可勾选显示的内容。
考虑到如果只显示一个参数或两个参数,原布局就很丑了,干脆加一个调整布局按钮,于是托盘菜单新增了“布局”按钮,可修改布局。
后面发现,布局和显示混合使用,情况多样,有的情况又很丑了,索性做成了动态布局,窗体根据显示的参数个数自动适配布局。
进一步开发,后来发现已采用的菜单栏的创建方案二使用JPopupMenu会导致多级菜单栏有问题,jPopupMenu.show(Component invoker, int x, int y)需要有Component(不加的话多级菜单的子菜单就d不出来),但是系统托盘这玩意儿SystemTray它不是Component……所以想了个方法,直接新建一个Jframe,用它来和JPopupMenu绑定,Jframe和JPopupMenu大小一致,且显示和隐藏的状态一致就哦了,然后就大改了……嗯,面目全非、惨不忍睹。
然后发现新方法在d出系统托盘菜单后,鼠标点击非菜单区域,菜单会自动消失(之前是不会的,所以用了jnativehook做全局鼠标监听,配合鼠标的位置判断,达到该效果),所以就不需要用jnativehook了。
最后为了方便和那什么,封装了Label,用了线程池。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)