Java SystemTray图标并不总是有效

时间:2015-05-01 21:56:29

标签: java system-tray kde gnome-shell

我需要你的帮助:我正在开发一个小型Java应用程序(Java版本7),它必须最小化到系统托盘中。

我正在使用Class SystemTray,使用SystemTray.isSupported(),然后

SystemTray systemTray = SystemTray.getSystemTray();
ImageIcon icon = new javax.swing.ImageIcon(getClass().getResource("icon.png"));

[...]

systemTray.add(trayIcon);

(当然有弹出窗口)

在Windows上,它运行良好。在XFCE,Xubuntu,没问题,图标正在使用弹出窗口。但是在KDE和Gnome shell上......它不起作用。

KDE(4.14.1)

(Qt:4.8.6工具等离子:4.11.12)

SystemTray.isSupported()= true,当程序到达该行时: systemTray.add(托盘图标);抓住了一个例外:

  托盘过程中出现

错误:   java.awt.AWTException:无法显示TrayIcon。

因此,图标为白色,当用户点击它时不起作用,没有弹出窗口。

Gnome Shell(3.12.2)

SystemTray.isSupported()= true,图标位于底部的通知区域,但鼠标事件不起作用...

为了解决这些问题,我认为SWT可能是一个好主意。但是当我实现它(上一版本)时,我收到了这个警告:

  

警告**:无法连接到辅助功能总线:连接失败   to socket / tmp /[...]

它不起作用...... 编辑:不再了,我可以用外部类修复SWT的问题。警告不是由SWT引起的,而是由环境系统引起的(我和终端中的其他应用程序有相同的警告)。


那么现在,我该怎么办? 我认为check environment systemSystem.getenv("XDG_CURRENT_DESKTOP")& System.getenv("GDMSESSION"),然后启用或禁用系统托盘,如果它是KDE或Gnome 3 ...但此解决方案实际上不是因为它是本地解决方案对于多平台(在我的意思是OS的功能),而不是全局解决方案(所有操作系统的一种方法)......

那么,其他想法?我不知道......有没有办法将嵌入式JWindow定义到系统托盘中?

1 个答案:

答案 0 :(得分:2)

我自己也遇到过这个问题,而且我记得我跑出了一堵砖墙,用合法的解决方案将其整理出来。我跟踪问题来调用TrayIcon.addNotify()方法随机失败。我似乎记得这是因为内部的竞争条件是对X11系统的调用花了太长时间才完成所以java方面放弃了。

但是如果你有一个带有不错的显卡的忍者电脑,你可能永远不会遇到这种情况,这可能就是为什么它还没有被修复。我的开发机器速度很慢,所以50%的时间都发生在我身上。

我一起做了 hack 一个快速而又脏的解决方案,其中包括尝试重复调用addNotify(在每次尝试之间暂停),直到成功(或最多次失败)。不幸的是,唯一的方法是通过反射,因为addNotify方法是包私有的。

代码如下:

function build_menu($array, $parent = 0, $level = 0)
{
    $ret = '';
    foreach($array as $m)
    {
        if($m['parent_menu'] == $parent)
        {
            $ret .= '<ul>';
            $ret .= '<li>' . $m['menu_link'] . '</li>';
            $ret .= $this -> build_menu($array, $m['menu_link'], $level + 1);
            $ret .= '</ul>';
        }
    }

    return $ret;
}

要使用它,只需致电:

public class HackyLinuxTrayIconInitialiser extends SwingWorker<Void, TrayIcon> {
    private static final int    MAX_ADD_ATTEMPTS    = 4;
    private static final long   ADD_ICON_DELAY      = 200;
    private static final long   ADD_FAILED_DELAY    = 1000;

    private TrayIcon[]  icons;

    public HackyLinuxTrayIconInitialiser(TrayIcon... ic) {
        icons = ic;
    }

    @Override
    protected Void doInBackground() {
        try {
            Method addNotify = TrayIcon.class.getDeclaredMethod("addNotify", (Class<?>[]) null);
            addNotify.setAccessible(true);
            for (TrayIcon icon : icons) {
                for (int attempt = 1; attempt < MAX_ADD_ATTEMPTS; attempt++) {
                    try {
                        addNotify.invoke(icon, (Object[]) null);
                        publish(icon);
                        pause(ADD_ICON_DELAY);
                        break;
                    } catch (NullPointerException | IllegalAccessException | IllegalArgumentException e) {
                        System.err.println("Failed to add icon. Giving up.");
                        e.printStackTrace();
                        break;
                    } catch (InvocationTargetException e) {
                        System.err.println("Failed to add icon, attempt " + attempt);
                        pause(ADD_FAILED_DELAY);
                    }
                }
            }
        } catch (NoSuchMethodException | SecurityException | NoSuchFieldException e1) {
            Log.err(e1);
        }
        return null;
    }

    private void pause(long delay) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e1) {
            Log.err(e1);
        }
    }

    @Override
    protected void process(List<TrayIcon> icons) {
        for (TrayIcon icon : icons) {
            try {
                tray.add(icon);
            } catch (AWTException e) {
                Log.err(e);
            }
        }
    }
}

我似乎记得当时我无法继续调用SystemTray.add(icon)因为它会离开&#34; ghost&#34;如果我这样做,系统托盘后面的托盘。

希望这有帮助。