java - 为什么小的 JPopupMenu 会导致视觉伪影,而较大的则不会?

标签 java swing jmenuitem jpopupmenu

我正在使用附有 MouseAdapter 的单行 JTabel。表模型填充了一些随机值。右键单击表格后,将出现一个带有多个 JMenuItems 的 JPopupMenu。如果弹出窗口的一部分在某个点被绘制在它所连接的面板之外,则视觉工件开始显示。有趣的是,这似乎只有在弹出窗口没有附加很多项目时才会发生。任何超过七个项目的弹出窗口一直为我工作。

仅在带有 Java 1.8.0_112-b15 的 Windows 10 64 位上进行了测试。

为什么会发生这种情况,是否有解决方法?

JPopupMenu artifact

public class PopupTest {

    private static final int NUM_POPUP_ITEMS = 3;

    private JFrame frame = new JFrame();
    private JPanel panel = new JPanel();
    private TableModel model = new TableModel();
    private JTable table = new JTable();

    public static void main(String[] a) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new PopupTest();
            }
        });
    }

    public PopupTest() {
        panel.setLayout(new BorderLayout());
        panel.add(table, BorderLayout.CENTER);
        panel.setPreferredSize(new Dimension(400, 500));
        table.setModel(model);
        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent event) {
                popup(event);
            }
        });
        frame.setLocation(150, 150);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(panel);
        frame.pack();
        frame.setVisible(true);
    }

    private void popup(MouseEvent e) {
        if (SwingUtilities.isRightMouseButton(e)) {
            JPopupMenu menu = new JPopupMenu();
            for (int i = 0; i < NUM_POPUP_ITEMS; i++) {
                menu.add(new JMenuItem(String.valueOf(i)));
            }
            menu.show(panel, e.getX(), e.getY());
        }
    }

    private class TableModel extends AbstractTableModel {

        private List<Double> dataList = new ArrayList<>();

        public TableModel() {
            for (int i = 0; i < 40; i++) {
                dataList.add(Math.random());
            }
        }

        @Override
        public int getRowCount() {
            return dataList.size();
        }

        @Override
        public int getColumnCount() {
            return 1;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return dataList.get(rowIndex);
        }
    }
}

最佳答案

COM/DCOM 层和 Java Swing 应用程序有一个小的延迟。

基本上,当 Swing 在 Windows 上运行时,它会为事件注册一个本地操作系统监听器,因为操作系统的图形桌面环境是第一个检测到鼠标移动和按钮按下的应用程序。

然后 Swing(实际上是 AWT)为报告的 Action 创建一个对应的 Event 对象,并将这个对象放在内部事件处理线程上。一旦事件被分派(dispatch)给线程,它就会被事件处理子系统处理,找到可能对事件使用react的 Java 代码块并运行这些 block (就像说定位弹出菜单的那个 block )。弹出菜单再次不直接绘制到屏幕上,它被堆肥到像素缓冲区窗口(如果是 JPopupMenu)并通过 JNI 转换为请求调度到操作系统绘图库,或者(如果 AWT弹出菜单)COM/DCOM 对象的 Java 端包装器直接分派(dispatch)请求(无需在 Java 端处理额外的绘图)。

所有这些都意味着在您的操作系统提供的窗口系统和 Java 虚拟机之间存在一个通信循环(以及它处理与窗口项对应的 Java 端对象)。这个循环意味着处理延迟,因为程序被放置和从内核中删除以响应“有工作要做”和“等待另一个程序做更多的工作”。

此外,鼠标光标(出于性能原因)并不总是由操作系统的窗口环境处理。根据您计算机的配置详细信息,图形卡可能会将鼠标绘制在操作系统呈现为桌面的顶部,而不会始终通知操作系统鼠标位置,只要它在其他事件(如点击)之前报告鼠标位置即可需要位置正确。这种额外的优化可能意味着即使您的操作系统在只是“在屏幕周围移动”的短时间内也不知道鼠标位置。

在某些操作系统上,可以通过编程方式调整这些项目。在某些情况下,它们无法调整,但可以(在程序之外)重新配置。在少数情况下,默认行为甚至不可配置。

Swing 隐藏了很多细节,使得图形编程比没有 Swing 时容易得多;并且,有许多技术可用于减少往返时间。首先,我会仔细检查您在事件处理程序中对 EventDispatch 线程所做的工作量是否最少。之后,如果您需要更快的速度,我会推荐 this article .

如果您需要比这更快,最终您的代码看起来更像是其他绘图系统中绘图代码的包装器,这就是 SWT 解决问题的方式;或者,您将放弃在 Java 程序中进行绘图并使用平台库的语言进行绘图,该语言将通过 JNI 绑定(bind)到您的应用程序。如果您正在考虑“在您的 EventDispatch 线程中做最少的工作”之外的任何选项,请注意您将花费越来越多的努力来获得非常小的 yield ,并且努力将导致代码从根本上更难维护、调试和迁移到“甚至有一点不同”的操作系统(甚至是相同操作系统的版本)。

抱歉,答案太长了,但对于大多数人来说,JPopupMenu 的实际绘制方式是个谜,在提出改进方法之前必须先解决这个问题。

关于java - 为什么小的 JPopupMenu 会导致视觉伪影,而较大的则不会?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42014033/

相关文章:

java - 有没有在新打开的框架内调用子例程或函数的方法? java

java - 如何将菜单项保持在文本区域的顶部?

java - 如何防止禁用的 JMenuItem 在被单击时隐藏菜单?

java - 使用 RetroFit2 和 Android/Java 的首次 API 调用

java - java中如何从请求中获取ip?

java - 检测鼠标悬停在 JPanel 子级上

java - Swing 部件重量轻?

java - 我们可以使用类型名称作为变量吗

java - 在 java REST 中解析 JSON 参数时出错

java - ActionListeners JMenuItems - 跨类