如何防止TrayIconpopup窗口占用整个调度程序线程

我有一个Java应用程序,使用JFrame以及TrayIcon ,我添加一个PopupMenu的TrayIcon 。

当我点击TrayIconpopup菜单出现,但只要PopupMenu可见,主框架就会冻结。

我的第一个想法是事件调度线程被某人占用。 所以我写了一个小的示例应用程序,它使用了一个swing工作器和一个进度条。

public class TrayIconTest { public static void main(String[] args) throws AWTException { JFrame frame = new JFrame("TrayIconTest"); frame.setDefaultCloSEOperation(WindowConstants.EXIT_ON_CLOSE); Container contentPane = frame.getContentPane(); JProgressBar jProgressBar = new JProgressBar(); BoundedRangeModel boundedRangeModel = jProgressBar.getModel(); contentPane.add(jProgressBar); boundedRangeModel.setMinimum(0); boundedRangeModel.setMaximum(100); PopupMenu popup = new PopupMenu(); TrayIcon trayIcon = new TrayIcon(new BufferedImage(64,64,BufferedImage.TYPE_INT_RGB),"TEST"); // Create a popup menu components MenuItem aboutItem = new MenuItem("About"); popup.add(aboutItem); trayIcon.setPopupMenu(popup); SystemTray tray = SystemTray.getSystemTray(); tray.add(trayIcon); IndeterminateSwingWorker indeterminateSwingWorker = new IndeterminateSwingWorker(boundedRangeModel); indeterminateSwingWorker.execute(); frame.setSize(640,80); frame.setLocationRelativeto(null); frame.setVisible(true); } } class IndeterminateSwingWorker extends SwingWorker<Void,Integer> { private BoundedRangeModel boundedRangeModel; public IndeterminateSwingWorker(BoundedRangeModel boundedRangeModel) { this.boundedRangeModel = boundedRangeModel; } @Override protected Void doInBackground() throws Exception { int i = 0; long start = System.currentTimeMillis(); long runtime = TimeUnit.MILLISECONDS.convert(5,TimeUnit.MINUTES); while (true) { i++; i %= boundedRangeModel.getMaximum(); publish(i); Thread.sleep(20); long Now = System.currentTimeMillis(); if ((Now - start) > runtime) { break; } System.out.println("i = " + i); } return null; } @Override protected void process(List<Integer> chunks) { for (Integer chunk : chunks) { boundedRangeModel.setValue(chunk); } } }

如何重现

如何将另一个应用程序的窗口句柄最小化到系统托盘?

系统托盘上下文菜单空白

如何更改由Shell_NotifyIcon设置的systray图标文本样式

使用系统托盘图标创build一个后台进程

如何检查NotifyIcon是否隐藏

当您启动示例应用程序时,您将看到一个带有进度条的框架。 SwingWorker模拟进程动作。

SwingWorker发布进度值并将其打印到System.out 。

当我点击托盘图标('黑色')时,进度条冻结,popup菜单出现。 我可以看到SwingWorker仍然在后台运行,因为它将进度值输出到命令行。

调查

所以我使用jvisualvm来看看应用程序,它告诉我,只要popup窗口是可见的,事件分派器线程就非常忙碌。

我可以弄清楚看sun.awt.windows.WTrayIconPeer.showPopupMenu(int,int)是由sun.awt.windows.WTrayIconPeer.showPopupMenu(int,int)方法可见的。

方法使用EventQueue.invokelater提交可运行的事件到事件派发线程。 在这个可运行的方法中,调用sun.awt.windows.WPopupMenuPeer._show方法。 这种方法是native , 它似乎实现了一个繁忙的等待循环,以便事件调度线程完全被这个方法占用 。 因此,只要popup菜单是可见的,其他与UI相关的任务就不会被执行。

有没有人知道一个解决scheme,使TrayIconpopup窗口显示时保持事件调度线程响应?

正确的行为托盘图标点击?

如何确保我的启动程序在加载系统托盘后运行?

为什么系统托盘图标会清除先前显示的图标?

Windows任务栏(Windows 7?) – 如何在“控制面板”通知对话框中设置应用程序名称

如何从C#系统托盘应用程序作为线程运行C程序?

我现在使用jpopupmenu作为解决方法,但不能将jpopupmenu直接添加到TrayIcon 。

诀窍是写一个Mouselistner

public class jpopupmenuMouseAdapter extends MouseAdapter { private jpopupmenu popupMenu; public jpopupmenuMouseAdapter(jpopupmenu popupMenu) { this.popupMenu = popupMenu; } @Override public void mouseReleased(MouseEvent e) { maybeShowPopup(e); } @Override public void mousepressed(MouseEvent e) { maybeShowPopup(e); } private void maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger()) { Dimension size = popupMenu.getPreferredSize(); popupMenu.setLocation(e.getX() - size.width,e.getY() - size.height); popupMenu.setInvoker(popupMenu); popupMenu.setVisible(true); } }

并将其连接到TrayIcon

TrayIcon trayIcon = ...; jpopupmenu popupMenu = ...; jpopupmenuMouseAdapter jpopupmenuMouseAdapter = new jpopupmenuMouseAdapter(popupMenu); trayIcon.addMouselistner(jpopupmenuMouseAdapter);

相关文章

本篇内容主要讲解“gitee如何上传代码”,感兴趣的朋友不妨来...
这篇“从gitee上下的代码如何用”文章的知识点大部分人都不太...
这篇文章主要介绍“gitee如何下载仓库里的项目”,在日常操作...
本篇内容主要讲解“怎么在Gitee上更新代码”,感兴趣的朋友不...
本文小编为大家详细介绍“怎么将工程托管到gitee”,内容详细...
这篇文章主要介绍了gitee中图片大小如何调整的相关知识,内容...