2016-07-05 2 views
1

TrayIcon뿐만 아니라 JFrame을 사용하는 java 응용 프로그램이 있으며 TrayIconPopupMenu을 추가했습니다.전체 디스패처 스레드를 차지하도록 TrayIcon 팝업을 방지하는 방법

TrayIcon을 클릭하면 팝업 메뉴가 나타나지만 PopupMenu이 보이는 한 메인 프레임이 멈 춥니 다.

내 첫 번째 생각은 이벤트 발송 스레드가 누군가에 의해 점유되었다는 것입니다. 그래서 스윙 작업자와 진행률 막대를 사용하는 작은 예제 응용 프로그램을 작성했습니다. 당신이 진행 표시 줄과 프레임을 볼 수 예제 응용 프로그램을 시작할 때

를 재현하는 방법

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은 진행 작업을 시뮬레이션합니다.

진도 값을 게시하고 System.out으로 인쇄합니다.

트레이 아이콘 ('검은 색')을 클릭하면 진행률 표시 줄이 멈추고 팝업 메뉴가 나타납니다. 진도 값을 명령 줄에 출력하기 때문에 SwingWorker가 백그라운드에서 계속 실행되고 있음을 알 수 있습니다.

조사는 그래서 jvisualvm를 사용하여 응용 프로그램에서 확인해 보니 이벤트 디스패처 스레드가 한 팝업을 볼 수 있습니다로 매우 바쁘다을 보여줍니다.

jvisualvm analysis

나는 팝업에서 그 모습을 알아낼 수는 방법 sun.awt.windows.WTrayIconPeer.showPopupMenu(int, int)에 의해 가시화된다.

이 메서드는 EventQueue.invokeLater을 사용하여 실행 파일을 이벤트 발송 스레드에 전달합니다. 이 실행 가능 파일에서 메소드 sun.awt.windows.WPopupMenuPeer._show이 호출됩니다. 이 메서드는 native이므로이 메서드는 이벤트 처리 스레드가 완전히 채워지도록 busy wait 루프를 구현하는 것 같습니다. 다른 Ui 관련 작업은 팝업 메뉴가 표시된 동안에는 실행되지 않습니다.

누구든지 TrayIcon 팝업이 표시되는 동안 이벤트 발송 스레드가 반응하도록하는 해결 방법을 알고 있습니까?

답변

1

해결 방법으로 JPopupMenu을 사용하고 있지만 TrayIcon에 직접 JPopupMenu을 추가 할 수 없습니다.

트릭은 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); 
에 연결하는 것입니다