Recently (in my hobby area) I often write a lot of server-side code using the Java Spring Boot framework, but suddenly I wanted to make it resident by displaying an icon in the system tray. Considering distributing server applications to people who are unrelated to information systems, I thought that it was not very kind to display a console such as a terminal, and I thought it would be nice if I could perform the minimum operations with the icon in the system tray. Is the trigger.
The system tray referred to in this article is the red part in the image below.
It is the "notification area" in Windows and the "menu bar" in Mac. I think there are similar ones in other OS. (Current Windows is no longer called the task tray ...)
This time I will try it with two OSs.
OS: Windows 10 Pro / macOS High Sierra
Java: 1.8
Unless otherwise stated, we will not use anything specialized for Spring Boot.
In this article, Illustration of "A" by Irasutoya is used as the sample icon ʻicon.png`.
First, the code that just puts out an icon.
IconDemo.java
import java.awt.AWTException;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
public class IconDemo {
public static void main(String[] args) {
IconDemo app = new IconDemo();
app.run();
}
/**
*Method to display icon in system tray
*/
private void run() {
Image image = Toolkit.getDefaultToolkit().createImage( ClassLoader.getSystemResource("icon.png ") ); //Prepare icon image
TrayIcon icon = new TrayIcon(image, "Sample Java App"); //* 1 Generated as a tray icon
icon.setImageAutoSize(true); //resize
try {
SystemTray.getSystemTray().add(icon); //* 2 Added to system tray
} catch (AWTException e) {
e.printStackTrace();
}
}
}
It was added like this. However, on a Mac, the Java icon was also displayed in the Dock. If you do not intend this, it seems that you can avoid it by adding the following option as a VM argument when starting the program.
-Dapple.awt.UIElement=true
In other words, if you are using plain java, start it as follows.
java -Dapple.awt.UIElement=true IconDemo
Also, hover your mouse cursor over the icon to see it.
You see "Sample Java App". This is called a tooltip, and the above code instructs you to display the specified character string.
※1 TrayIcon
The TrayIcon
class represents an icon that can be added to the system tray. The above code takes two arguments, but you can take up to three arguments.
/*
1st argument:image Icon image (required)
2nd argument:Character string displayed by hovering the mouse cursor over the tooltip icon (optional)
3rd argument:popup Pop-up menu (optional) Used in subsequent STEP
*/
TrayIcon icon = new TrayIcon(Image image, String tooltip, PopupMenu popup);
※2 SystemTray
The SystemTray
class represents the system tray for each OS. Get the entity of the system tray with SystemTray.getSystemTray ()
and add the tray icon to it using the ʻadd ()` method.
You can also use the SystemTray.isSupported ()
method, like the code below, to see if java can manipulate the system tray in the execution environment in the first place.
IconDemo.java
import java.awt.SystemTray;
public class IconDemo {
public static void main(String[] args) {
IconDemo app = new IconDemo();
app.check();
}
/**
*Check if the execution environment supports the system tray
*/
private void check() {
Boolean check = SystemTray.isSupported();
System.out.println("Support status: " + check);
}
}
However, it seems that ** not necessarily the system tray is unavailable ** if this becomes false
. The source is myself. When I tried to do the same in Spring Boot, I got a Headless Exception, which I'll discuss later, and the ʻisSupported ()method became
false. However, I was able to display it even if it was
false` by taking the corrective action described later.
In my case, it happened when I was using Spring Boot. The cause is that AWT's headless mode is enabled, and when it is enabled, AWT-related features are rarely available.
It can be disabled by specifying the following in the VM argument.
-Djava.awt.headless=false
In other words, if you are using plain java, start it as follows.
java -Djava.awt.headless=false IconDemo
If you are using Spring Boot, you can also disable it by changing the main ()
method as follows.
/*****Before changing*****/
public static void main(String[] args) {
SpringApplication.run(IconDemo.class, args);
}
/*****After changing*****/
public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(IconDemo.class);
builder.headless(false).run(args);
}
(However, I do not understand the harmful effects of disabling what was originally enabled in Spring Boot etc.)
On Windows, create a menu that appears when you right-click an icon, and on Mac, when you click the icon. It also describes what happens when you click each menu. In addition, the writing style is slightly different from STEP1.
IconDemo.java
import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class IconDemo {
public static void main(String[] args) {
IconDemo app = new IconDemo();
app.run();
}
/**
*Method to display icon in system tray
*/
private void run() {
SystemTray tray = SystemTray.getSystemTray(); //Get system tray
PopupMenu popup = new PopupMenu(); //* 4 Generate pop-up menu
Image image = Toolkit.getDefaultToolkit().createImage( ClassLoader.getSystemResource("icon.png ") ); //Prepare icon image
TrayIcon icon = new TrayIcon(image, "Sample Java App", popup); //* 4 Generated as a tray icon
icon.setImageAutoSize(true); //resize
//* 3 Create the contents of the pop-up menu
MenuItem item1 = new MenuItem("Hello");
item1.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello world!!");
}
});
MenuItem item2 = new MenuItem("Exit");
item2.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
tray.remove(icon);
// System.exit(0);
}
});
popup.add(item1); // ※4
popup.add(item2); // ※4
try {
tray.add(icon); //Add to system tray
} catch (AWTException e) {
e.printStackTrace();
}
}
}
Right-click the icon on Windows and click on Mac.
Click "Hello" to display "Hello world !!" on the console, and click "Exit" to exit the program.
※3 MenuItem
Each item in the pop-up menu is created with the MenuItem
class. In the argument of the constructor, specify the character string of the display name on the menu.
Also, the processing after clicking the menu is added using the ʻaddActionListener () method. Is this writing method familiar to anyone who has created GUI applications? The argument of the ʻaddActionListener ()
method is an instance that implements the ʻActionListener interface. The above code is implemented using an anonymous class. The ʻActionListener
interface must always override the ʻactionPerformed (ActionEvent e)` method, in which you write what happens after the menu is clicked.
In the "Exit" menu, the process to terminate the program is written. You can use System.exit (0);
to force it to exit, but it doesn't seem to be a good idea to use it too much. In this program, just write tray.remove (icon);
and remove the tray icon from the system tray, and the program will end.
※4 PopupMenu
The PopupMenu
class represents a single pop-up menu. By specifying this PopupMenu in the 3rd argument when generating TrayIcon
, you can display the menu when you right-click or click the icon.
The MenuItem
created earlier can be added by using the ʻadd ()` method of this PopupMenu.
In the STEP2 code above, when I clicked the "Hello" menu, "Hello world !!" was displayed on the console, but let's display something as a message in the system tray instead of the console.
Change the contents of the ʻactionPerformed () method of ʻitem1
in STEP2 as follows.
import java.awt.TrayIcon.MessageType; //Add to import
/**
*Before changing
*/
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello world!!");
}
/**
*After changing
*/
@Override
public void actionPerformed(ActionEvent e) {
// ※5
icon.displayMessage("Sample program", "Hello World! This is a sample message.", MessageType.INFO);
}
Try clicking the "Hello" menu.
Unfortunately, it didn't show up on the Mac. In the case of Mac, it seems that the message is displayed using the notification center of Mac, but there seems to be no way to use it directly from Java. The person who answered here introduced the solution, but it has not been done yet. It is a verification.
I feel like it's not a very modern way.
If you know a better way, I would appreciate it if you could tell me.