Java Robot 从 Windows 系统托盘启动

Posted

技术标签:

【中文标题】Java Robot 从 Windows 系统托盘启动【英文标题】:Java Robot launched from Windows System Tray 【发布时间】:2020-11-19 03:41:04 【问题描述】:

我希望我的程序从系统托盘菜单执行屏幕截图。

我让它工作了,但 MenuItem 本身隐藏得不够快,并且与屏幕的其余部分一起被捕获。

这是一个 MCVE,基于 Java 教程的 TrayIconDemo 示例的精简版本: (编辑:感谢 Holger 的评论,使之变得更加简单)

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TrayCapture 
    public static void main(String[] args) 
        SwingUtilities.invokeLater(new Runnable() 
            public void run() 
                try 
                    createAndShowGUI();
                
                catch (Exception e) 
                    e.printStackTrace();
                
            
        );
    

    private static void createAndShowGUI() throws Exception 
        //Check the SystemTray support
        if (!SystemTray.isSupported()) 
            System.err.println("SystemTray is not supported");
            return;
        
        final PopupMenu popup = new PopupMenu();
        final TrayIcon trayIcon = new TrayIcon(ImageIO.read(new URL("https://i.stack.imgur.com/wCF8S.png")));
        trayIcon.setImageAutoSize(true);

        // Create a popup menu components
        MenuItem captureItem = new MenuItem("Capture");
        MenuItem exitItem = new MenuItem("Exit");

        //Add components to popup menu
        popup.add(captureItem);
        popup.add(exitItem);

        trayIcon.setPopupMenu(popup);

        SystemTray.getSystemTray().add(trayIcon);

        captureItem.addActionListener(new ActionListener() 
            public void actionPerformed(ActionEvent e) 
                try 
                    File outputFile = new File("capture.png");
                    ImageIO.write(new Robot().createScreenCapture(
                            new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())),
                            "png",
                            outputFile);
                    JOptionPane.showMessageDialog(null, "Screenshot saved to: " + outputFile.getAbsolutePath());
                
                catch (AWTException | IOException ex) 
                    ex.printStackTrace();
                
            
        );

        exitItem.addActionListener(new ActionListener() 
            public void actionPerformed(ActionEvent e) 
                SystemTray.getSystemTray().remove(trayIcon);
                System.exit(0);
            
        );
    

这是一个结果捕获。

如您所见,PopupMenu 已关闭,但“Capture”MenuItem 在机器人进行捕获时仍然可见,并且是捕获图像的一部分。

有没有办法强制重绘以隐藏 MenuItem 或在它隐藏时收到通知,以便我可以确保机器人不会捡起它?我宁愿避免任意的 Thread.sleep() 因为它只是一个盲目的竞争条件......

KR, 维克内

【问题讨论】:

使用SwingUtilities.invokeLater() 将您的代码包装在您截取屏幕截图的位置。这将允许关闭弹出窗口。 菜单已关闭。您看到的是 Windows 自身为所选菜单项创建的淡出效果。当您仔细观察时,您会注意到所有托盘图标菜单都有这种效果,无论应用程序如何。我不知道有什么方法可以确定在您自己的应用程序之外执行的动画是否已经结束。附带说明一下,createScreenCapture 确实已经返回了 BufferedImage,这会使您的一半代码过时,您只需要 ImageIO.write(new Robot() .createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())), "png", outputFile) 看看this。 Windows 10 中也存在此设置。转到 Control Panel --> System and Security --> System 然后 高级设置 或其他什么...我找到了路径,但我没有不知道英文是怎么翻译的。我知道,但这不会回答这个问题。我没有尝试过,但它可能会有所帮助。 @weisj:试过invokeLater(),无济于事:-( @Holger:哦,Windows 动画,不错。我想睡觉是唯一的选择:-(。确实图像复制代码是多余的,不知道我在想什么。我修好了。谢谢 【参考方案1】:

这不是 Java 应用程序的产物,因为它是选定菜单项的 Windows 特定动画,在弹出菜单已关闭时继续播放。

据我所知,没有办法从 Java 端监控动画进度(除了捕获屏幕并查看工件是否仍然存在)。

最好的选择是在截屏之前设置一个可配置的延迟(合理的默认值,例如 300 毫秒)。我知道的所有截图工具,无论如何都使用可配置的延迟。

【讨论】:

以上是关于Java Robot 从 Windows 系统托盘启动的主要内容,如果未能解决你的问题,请参考以下文章

开发一个简单的 Windows 系统托盘桌面应用程序来使用 .NET Web 服务

WPF - 将应用程序从系统托盘置于最前面

开发一个简单的Windows系统托盘桌面应用程序来使用.NET Web服务

Pgadmin4 不会显示在 Windows 10 系统托盘上

如何制作仅在系统托盘中运行的 .NET Windows 窗体应用程序?

Windows Phone 7编程学习点滴二——设备方向系统主题和系统托盘