Das Beispiel zeigt wie ein Tray-Icon mit einer Badge eingerichtet werden kann.
Im Beispiel wird ein kleines GUI erzeugt, das nur aus
einem JFrame
mit einem Button zum Beenden
der Application besteht. Im System-Tray, unter MacOSX
der Fensterleiste oben-rechts, zeigt das Programm ein
eigenes Icon, auf dem innerhalb einer Badge ein
Countdown heruntergezählt wird.
Programmaufbau
Der Konstruktor dient zum Aufruf von init()
[27]. In der Methode werden die Oberfläche und das
Tray-Icon eingerichtet. Um eine Badge mit ihrem
dynamischen Zähler unabhängig und parallel zur
Anzeige des Hauptfensters zu ermöglichen, muss dies
nebenläufig erfolgen. Dies wird durch ein ExecutorService
-Objekt
gewährleistet, das einen Thread bereitstellt, in
dem showTrayIcon()
aufgerufen wird [37].
Der Methode werden hierbei drei Parameter
übergeben: ein PopupMenu
, das vorher
gebildet wurde, ein numerischer Startwert für den
Countdown der Badge und ein boolscher Wert. Ist dieser true
wird der Zahlenwert auf der Badge statisch angezeigt,
bei false
dient er als Startwert eines
Countdowns. Sobald sie nicht mehr benötigt werden,
müssen die benötigten Resourcen des Threads
durch shutdown()
wieder freigegeben werden
[40].
Da nicht jedes Betriebssystem ein System-Tray
bereitstellt, sollte dies vorher durch die statische
Methode SystemTray.isSupported()
abgefragt
werden [35].
import java.awt.AWTException; import java.awt.Color; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Image; import java.awt.PopupMenu; import java.awt.SystemTray; import java.awt.TrayIcon; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class MacOSXTrayIconMitBadge { private static final String ICON_PATH = "/img/icon512.png"; public MacOSXTrayIconMitBadge() { init(); } private void init() { PopupMenu menu = new PopupMenu("Application"); menu.add("Test"); menu.add("Test 2"); ExecutorService executor = Executors.newCachedThreadPool(); if (SystemTray.isSupported()) { executor.execute(() -> { showTrayIcon(menu, 15, false); }); } executor.shutdown(); JButton butt = new JButton("Ende"); butt.addActionListener(e -> System.exit(0)); JFrame frame = new JFrame("Tray-Demo"); frame.setLayout(new FlowLayout()); frame.add(butt); frame.setSize(200, 80); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLocationRelativeTo(null); frame.setVisible(true); } private void showTrayIcon(PopupMenu menu, int number, boolean isFixed) { SystemTray tray = SystemTray.getSystemTray(); Image icon = createTrayIcon(ICON_PATH, number); TrayIcon trayIcon = new TrayIcon(icon, "Tray Demo", menu); try { tray.add(trayIcon); } catch (AWTException e) { } if (!isFixed) { while (number > -1) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } icon = createTrayIcon(ICON_PATH, --number); trayIcon.setImage(icon); } tray.remove(trayIcon); } } private Image loadImage(String imgPath) { Image img = null; try { img = ImageIO.read(getClass().getResource(imgPath)); } catch (IOException e) { System.err.println("Dock-Icon kann nicht geladen werden!"); } return img; } private Image createTrayIcon(String imgPath, int num) { BufferedImage img = (BufferedImage) loadImage(imgPath); int imgwidth = img.getWidth(); int fontSize = (int) (imgwidth / 2.5); int imgx = (int) (imgwidth / 2); Graphics2D g2d = (Graphics2D) img.getGraphics(); g2d.setFont(new Font(Font.SANS_SERIF, Font.BOLD, fontSize)); g2d.setColor(Color.RED); int fontx = imgx; int fonty = (int) (imgwidth * 0.9); g2d.fillArc(imgx, imgx, imgx, imgx, 0, 360); if (num < 10) fontx = imgx + fontSize / 3; g2d.setColor(Color.WHITE); g2d.drawString(Integer.valueOf(num).toString(), fontx, fonty); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ImageIO.write(img, "png", baos); } catch (IOException e) { e.printStackTrace(); } byte[] bArr = baos.toByteArray(); ImageIcon imgTmp = new ImageIcon(bArr); return imgTmp.getImage(); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> new MacOSXTrayIconMitBadge()); } }
Tray-Icon mit Tooltip und Menu
In showTrayIcon()
wird zunächst das
System-Tray durch SystemTray.getSystemTray()
ermittelt und instanziert [53]. Unter Übergabe des
Pfades zur Bilddatei und dem numerischen Wert für
die Badge wird dann ein Image
-Objekt
erzeugt [54], das im nächsten Schritt Verwendung
findet, um das TrayIcon
zu erzeugen [55].
Dem Konstruktor werden hierbei neben dem Image
-Objekt
noch ein Tooltiptext und das PopupMenu
übergeben. Tooltip und Menu sind optional, da der
Konstruktor dreifach überladen ist. Das Menu
enthält hier zwei Elemente, Test und Test
2. Beide bleiben der Übersichtlichkeit
halber funktionslos, können jedoch nach dem Start
des Programms durch Klick auf das Tray-Icon bestaunt
werden.
Zeichnen des Tray-Icons
Da die Java-Bibliotheken keine Möglichkeit bieten,
ein Tray-Icon mit einer Badge zu versehen, muss diese
selbst gezeichnet werden. Dazu wird in createTrayIcon()
zunächst die Icondatei geladen [85]. Dies geschieht
mit Hilfe der ImageIO
-API unter
Übergabe des Dateipfades in der Methode loadImage()
[74]. Sie liefert ein Image
-Objekt, das zum
BufferedImage
gecastet wird, um die
nachfolgenden Schritte etwas zu vereinfachen.
Erzeugen der Badge
Das hier verwendete Icon ist quadratisch. Zur
Dimensionierung und Positionierung der Badge und der
verwendeten Schrift wird die Seitenlänge des Icons
herangezogen [86]. Unter Verwendung des ermittelten
Wertes werden ein Kreis [94] und anschließend der
String der der Methode übergebenen Zahl [98]
gezeichnet. Da die kleine Schriftgröße
empfindlich hinsichtlich der Positionierung ist, wird
hierbei noch zwischen ein- und zweistelligen Zahlen
unterschieden [95].
Der Zeichenvorgang selbst findet
auf dem Graphics-Kontext des BufferedImage
statt [89]. Alle Größen richten sich somit
nach den Dimensionen der Ausgangsdatei und nicht
denjenigen des skalierten Tray-Icons!
Die auf diese
Weise erzeugte Graphik wird in einen ByteArrayOutputStream
geschrieben [102], aus dem das erzeugte Array extrahiert
[106] und schließlich an den Konstruktor eines ImageIcon
übergeben wird [107]. Zurückgegeben wird dann
das von diesem gelieferte Image
-Objekt
[108].
Anzeigen des Tray-Icons
Das Image
-Objekt wird wie oben beschrieben
zur Erzeugung des Tray-Icons an den entsprechenden
Konstruktor übergeben. Innerhalb eines
try-catch-Blocks wird das Icon auf das Tray gesetzt [57].
Wird
das Icon mit Countdown angezeigt, so wird nach dessen Ablauf
auch das Icon aus dem Tray entfernt [70].
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.