为什么JPopupMenu在渲染之前需要2次UIThread传递?

时间:2017-07-18 01:01:12

标签: java jpopupmenu

第一遍似乎使可绘制区域无效或绘制背景。第二遍传递菜单。如果有任何延迟(如下面的exaserbates示例),那么您将获得灰色方块闪烁效果。

这是Linux上的JDK8。

enter image description here

如何阻止这种闪烁效应?

public class MenuTester {

  public static void main(String[] args) {
    final JFrame frame = new JFrame();
    frame.setBounds(100,  100,  300,  200);


    final JButton button = new JButton("Show Menu");
    button.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            final JPopupMenu popupMenu = new JPopupMenu();
            popupMenu.add(new JMenuItem("aaaa"));
            popupMenu.add(new JMenuItem("bbbb"));
            popupMenu.add(new JMenuItem("cccc"));
            popupMenu.setLocation(100, 100);
            popupMenu.setVisible(true);
            try {
                Thread.sleep(2000); // Leave enough time to clearly see the ?invalidated/background? area.
            } catch (InterruptedException ex) {
                // Nothing to do
            }
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    // Hide after 1 second
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        // Nothing to do
                    }
                    popupMenu.setVisible(false);
                }
             });
         }
    });

    frame.add(button);
    frame.setVisible(true);
  }
}

1 个答案:

答案 0 :(得分:1)

Swing已经,只要我记得它,在显示窗口时有“延迟”,这可能与操作系统实现的帧之间的时间以及本机消息和事件队列的连接有关,但是这个是纯粹的观察

我带了你的代码,只需将框架的创建包装成EventQueue.invokeLater就能得到类似的行为

Delayed Startup

根据系统配置和配置,您将在不同的系统上获得不同的结果

  

导致窗口在第一次传递中呈现的事件是什么?

我所做的就是拿走你的代码并将创建包裹在EventQueue.invokeLater中,例如......

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class MenuTester {

    public static void main(String[] args) {
        new MenuTester();
    }

    public MenuTester() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                final JFrame frame = new JFrame();
                frame.setBounds(100, 100, 300, 200);

                final JButton button = new JButton("Show Menu");
                button.addActionListener(new ActionListener() {

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        final JPopupMenu popupMenu = new JPopupMenu();
                        popupMenu.add(new JMenuItem("aaaa"));
                        popupMenu.add(new JMenuItem("bbbb"));
                        popupMenu.add(new JMenuItem("cccc"));
                        popupMenu.setLocation(100, 100);
                        popupMenu.setVisible(true);
//                        try {
//                            Thread.sleep(2000); // Leave enough time to clearly see the ?invalidated/background? area.
//                        } catch (InterruptedException ex) {
//                            // Nothing to do
//                        }
//                        SwingUtilities.invokeLater(new Runnable() {
//                            @Override
//                            public void run() {
//                                // Hide after 1 second
//                                try {
//                                    Thread.sleep(1000);
//                                } catch (InterruptedException ex) {
//                                    // Nothing to do
//                                }
//                                popupMenu.setVisible(false);
//                            }
//                        });
                    }
                });
                frame.add(button);
                frame.setVisible(true);
            }
        });
    }
}
  

有没有办法在第一次通过时将窗口渲染为透明,在第二次曝光期间不透明?

这不是一个新问题,自从我在Java 1.3开始使用Swing以来,这一直是一种事态。你问的是什么意思你知道什么时候油漆通过并且完成了。 Swing并不是完全愚蠢的,它可以做出一些聪明的决定来优化渲染过程(比如不绘制可见的组件)

另一个问题是,使用JPopupMenu,您实际上并不知道它是否已在窗口中显示(或者仅在玻璃窗格中显示为组件),所以整个事情是非常复杂的