【问题标题】:How to prevent the TrayIcon popup to occupy the whole dispatcher thread如何防止 TrayIcon 弹窗占据整个 Dispatcher 线程
【发布时间】:2016-07-05 09:55:47
【问题描述】:

我有一个使用JFrameTrayIcon 的Java 应用程序,我在TrayIcon 中添加了PopupMenu

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

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

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);
    }
  }
}

如何重现

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

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

当我点击托盘图标(“黑色的”)时,进度条会冻结并显示弹出菜单。我可以看到 SwingWorker 仍在后台运行,因为它会将进度值输出到命令行。

调查

所以我查看了使用jvisualvm 的应用程序,它表明只要弹出窗口可见,事件调度程序线程就非常繁忙。

我可以发现通过sun.awt.windows.WTrayIconPeer.showPopupMenu(int, int) 方法可以看到弹出窗口。

此方法使用EventQueue.invokeLater 向事件调度线程提交一个runnable。在这个 runnable 中,方法 sun.awt.windows.WPopupMenuPeer._show 被调用。这个方法是native好像实现了一个繁忙的等待循环,所以事件派发线程完全被这个方法占用了。 因此,只要弹出菜单可见,其他与 ui 相关的任务就不会执行。

有人知道在显示TrayIcon 弹出窗口时保持事件调度线程响应的解决方法吗?

【问题讨论】:

    标签: java windows swing system-tray


    【解决方案1】:

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

    诀窍是写一个MouseListener

    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.addMouseListener(jPopupMenuMouseAdapter);   
    

    【讨论】:

      猜你喜欢
      • 2013-08-20
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      • 2015-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多