Spring Boot使用系统托盘图标

时间:2014-10-25 15:33:15

标签: java spring system-tray headless

我正在设置一个Spring Boot应用程序,并希望主机可以访问一个可以访问某些bean信息的系统托盘图标。

我目前尝试使用@Autowired详细信息为我的SystemTrayIcon创建一个Bean。但是,当我尝试添加SystemTrayIcon时,我得到以下异常:

Caused by: java.awt.HeadlessException
    at java.awt.TrayIcon.<init>(Unknown Source)
    at java.awt.TrayIcon.<init>(Unknown Source)
    at java.awt.TrayIcon.<init>(Unknown Source)
    at hermes.subsrciber.systemTray.HermesTrayIcon.<init>(HermesTrayIcon.java:36)
    at hermes.subscriber.boot.AppStarter.trayIcon(AppStarter.java:83)
    at hermes.subscriber.boot.AppStarter$$EnhancerBySpringCGLIB$$c4f80849.CGLIB$trayIcon$3(<generated>)
    at hermes.subscriber.boot.AppStarter$$EnhancerBySpringCGLIB$$c4f80849$$FastClassBySpringCGLIB$$d62ab0bd.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312)
    at hermes.subscriber.boot.AppStarter$$EnhancerBySpringCGLIB$$c4f80849.trayIcon(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
    ... 17 more

SpringBoot应用程序有没有办法访问系统托盘图标?

我相信我可以简单地在Main方法中定义它(沿着SpringApplication.run)但是我无法自动装配我的属性。

这种设置可以吗?

3 个答案:

答案 0 :(得分:5)

解决方案是,而不是使用SpringApplication.run(MyConfig.class,args),使用以下设置:

    SpringApplicationBuilder builder = new SpringApplicationBuilder(MyConfig.class);
    builder.headless(false);
    ConfigurableApplicationContext context = builder.run(args);

要实际添加系统图标,我添加了一个以下类型的bean:

public class MyTrayIcon extends TrayIcon {

    private static final String IMAGE_PATH = "/path/icon_16x16.png";
    private static final String TOOLTIP = "Text";

    private PopupMenu popup;
    private SystemTray tray;

    public MyTrayIcon(){
        super(createImage(IMAGE_PATH,TOOLTIP),TOOLTIP);
        popup = new PopupMenu();
        tray = SystemTray.getSystemTray();
    }

    @PostConstruct
    private void setup() throws AWTException{
        // popup.add(itemAbout);
        // here add the items to your popup menu. These extend MenuItem
        // popup.addSeparator();
        setPopupMenu(popup);
        tray.add(this);
    }

    protected static Image createImage(String path, String description){
        URL imageURL = MyTrayIcon.class.getResource(path);
        if(imageURL == null){
            System.err.println("Failed Creating Image. Resource not found: "+path);
            return null;
        }else {
            return new ImageIcon(imageURL,description).getImage();
        }
    }
}

答案 1 :(得分:0)

如果有人想要完整的代码......

import java.awt.AWTException;
import java.awt.CheckboxMenuItem;
import java.awt.Image;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;

import javax.annotation.PostConstruct;
import javax.swing.ImageIcon;

import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;

public class MyTrayIcon extends TrayIcon {

    private static final String IMAGE_PATH = "/PATH.png";
    private static final String TOOLTIP = "Running";
    private PopupMenu popup;
    final SystemTray tray;

    public MyTrayIcon(){
        super(createImage(IMAGE_PATH,TOOLTIP),TOOLTIP);
        popup = new PopupMenu();
        tray = SystemTray.getSystemTray();
        try {
            setup();
        } catch (AWTException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }



    @PostConstruct
    private void setup() throws AWTException{
        // Create a pop-up menu components
        MenuItem exitItem = new MenuItem("Exit");
        popup.add(exitItem);
        exitItem.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                final int exitCode = 0;
                ExitCodeGenerator exitCodeGenerator = new ExitCodeGenerator() {

                    @Override
                    public int getExitCode() {
                      return exitCode;
                    }

                  };

                  tray.remove(MyTrayIcon.this);
                  SpringApplication.exit(Application.context, exitCodeGenerator);
            }
        });
        // popup.addSeparator();
        setPopupMenu(popup);
        tray.add(this);



    }

    protected static Image createImage(String path, String description){

        URL imageURL = MyTrayIcon.class.getResource(path);
        if(imageURL == null){
            System.err.println("Failed Creating Image. Resource not found: "+path);
            return null;
        }else {
            return new ImageIcon(imageURL,description).getImage();
        }
    }
}

在你的Application.java中:

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class Application {

    public static ConfigurableApplicationContext context;

    public static void main(String[] args) {
        //SpringApplication.run(Application.class, args);

        SpringApplicationBuilder builder = new SpringApplicationBuilder(Application.class);
        builder.headless(false);
        context = builder.run(args);

        MyTrayIcon m = new MyTrayIcon();
    }
}

答案 2 :(得分:0)

正在使用的代码逻辑

/**
 * @author lotuso
 * @version 1.0 2021/2/21 14:55
 **/
@Slf4j
public class TrayIcon extends Application {
    private final AtomicInteger runCount = new AtomicInteger(0);
    private final RegistryTrayConfiguration configuration = new RegistryTrayConfiguration();
    
    @Override
    public void start(final Stage stage) throws Exception {
        //构建系统托盘图标
        if(configuration.isEnabled()) {
            var stream = TrayIcon.class.getClassLoader().getResource(configuration.getIcon());
            assert stream != null;
            var image = ImageIO.read(stream);
            var trayIcon = new java.awt.TrayIcon(image, configuration.getTip());
            trayIcon.setImageAutoSize(true);
            //添加托盘事件
            {
                trayIcon.addMouseListener(new MouseAdapter() {
                    //鼠标点击事件
                    @Override
                    public void mouseClicked(final MouseEvent event) {
                        //双击左键
                        if(event.getButton() == MouseEvent.BUTTON1 && event.getClickCount() == 2) {
                            log.info("托盘双击事件");
                            Platform.runLater(() -> {
                                //TODO 如果需要显示一个窗口,哪么在这里进行,暂不实现
                            });
                        }
                        else if(event.getButton() == MouseEvent.BUTTON3) {
                            log.info("托盘右击事件");
                            //TODO 可以做些其它的事
                        }
                    }
                });
            }
            
            //获取系统托盘
            var trayPanel = SystemTray.getSystemTray();
            trayPanel.add(trayIcon);
        }
    }
    
    public synchronized void run(String... args) {
        if(runCount.get() <= 0){
            runCount.set(runCount.get()+1);
            launch(args);
        }
    }
    
    public static class RegistryTrayConfiguration {
        
        public boolean isEnabled() {
            return !Objects.isNull(getIcon());
        }
        
        public String getIcon() {
            var icon = getYML("spring.application.tray.icon");
            return String.valueOf(icon);
        }
        
        public String getTip() {
            var tip = getYML("spring.application.tray.tip");
            return String.valueOf(tip);
        }
        
        public static Object getYML(Object key) {
            return getYML(key, "application.yml");
        }
        
        public static Object getYML(Object key, String yml) {
            var resource = new ClassPathResource(yml);
            Properties properties;
            try {
                var factory = new YamlPropertiesFactoryBean();
                factory.setResources(resource);
                properties = factory.getObject();
            }
            catch(Exception ex) {
                log.warn(ex.getLocalizedMessage());
                return null;
            }
            assert properties != null;
            return properties.get(key);
        }
    }
}

在类中使用@Configuration 标签定义以下代码:

@Bean
public Application trayIcon() {
    var trayIcon = new TrayIcon();
    //TODO 此处将托盘与SpringBoot线程分开
    new Thread(trayIcon::run).start();
    return trayIcon;
}

添加具有以下结构的资源文件。 将图像目录添加到项目的资源目录并放置图标。 PNG 文件在那里

|_ src
  |_ main
    |_ java
    |_ resources
      |_ images
        |_ icon.png
      |_ application.yml

Application.yml 需要以下设置

spring:
  application:
    tray:
      tip: Used to indicate the prompt when the mouse is moved over the icon
      icon: Directory path relative to resources, such as images/icon.png

这样,TrayIcon就交给了Spring的容器来管理。如果对你有帮助,如果你有更好的建议,请告诉我