首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么小的JPopupMenu会产生视觉伪像,而大的不会?

为什么小的JPopupMenu会产生视觉伪像,而大的不会?
EN

Stack Overflow用户
提问于 2017-02-03 07:09:22
回答 1查看 723关注 0票数 9

我使用的是一个附加了MouseAdapter的单行JTabel。表格模型中填充了一些随机值。右键单击表时,将出现一个包含多个JMenuItems的JPopupMenu。可视化工件开始显示弹出窗口的一部分是否在面板外部绘制,它在某个时候附加到面板上。有趣的是,这似乎只有在弹出窗口没有附加很多项的情况下才会发生。任何超过七个项目的弹出窗口对我来说都是有效的。

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

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

代码语言:javascript
复制
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);
        }
    }
}
EN

回答 1

Stack Overflow用户

发布于 2020-02-14 00:01:07

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

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

然后,Swing (实际上是AWT)为报告的操作创建一个相应的事件对象,并将该对象放在内部事件处理线程上。一旦事件被分派到线程,它就会由事件处理子系统进行处理,找到可能对事件做出反应的Java代码块并运行这些块(就像定位弹出菜单的块一样)。弹出菜单同样不会直接绘制到屏幕上,它会被组合成一个像素缓冲区窗口(如果是JPopupMenu),并通过JNI的方式转换为对操作系统绘图库的请求分派,或者(如果是AWT弹出菜单) COM / DCOM对象的Java端包装器直接分派请求(不需要在Java端处理额外的绘制)。

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

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

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

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

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

抱歉,答案太长了,但对于大多数人来说,JPopupMenu是如何实际绘制的是一个谜,在提出改进方法之前,必须先讨论这一点。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42014033

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档