我正在使用连接了MouseAdapter的单行JTabel。表模型填充了一些随机值。右键单击表格后,将出现带有多个JMenuItems的JPopupMenu。视觉工件开始显示弹出窗口的一部分是否在某个点附着到它所连接的面板之外。有趣的是,如果弹出窗口没有附加很多项目,这似乎只会发生。任何超过七项的弹出窗口都一直在为我工作。
仅在使用Java 1.8.0_112-b15的Windows 10 64位上进行了测试。
为什么会发生这种情况并且有解决方法?
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);
}
}
}
答案 0 :(得分:1)
COM / DCOM层和Java Swing应用程序的延迟很小。
基本上,当Swing在Windows上运行时,它会注册本机操作系统事件监听器,因为操作系统的图形桌面环境是第一个检测鼠标移动和按钮按下的应用程序。
然后,Swing(实际上是AWT)为所报告的操作创建相应的Event对象,并将此对象放置在内部事件处理线程上。一旦事件被分派到线程,事件处理子系统就会对其进行处理,找到可能对事件做出反应的Java代码块并运行这些块(例如说放置弹出菜单的块)。弹出菜单再次没有直接绘制到屏幕上,而是被堆肥到像素缓冲区窗口(如果是JPopupMenu)中,并通过JNI转换为请求分发给操作系统图形库,或者(如果是AWT弹出菜单),COM / DCOM对象的Java端包装器直接分派了请求(没有Java端处理的其他图形)。
所有这些都意味着,操作系统提供的窗口系统与Java虚拟机(及其对与窗口项相对应的Java边对象的处理)之间存在通信循环。此循环意味着响应“有工作要做”和“等待另一个程序执行更多工作”,随着程序从内核中放入和删除,处理的延迟。
此外,由于性能原因,鼠标光标并非总是由操作系统的窗口环境处理。根据您计算机的配置详细信息,图形卡可能会在操作系统作为其桌面显示的图形之上绘制鼠标,而不总是通知操作系统鼠标的位置,只要它在其他事件(例如单击)之前报告鼠标的位置需要正确的位置。这项额外的优化措施可能意味着即使您的操作系统在短时间内“仅在屏幕上四处移动”也不知道鼠标的位置。
在某些操作系统上,可以通过编程方式调整这些项目。在某些情况下,它们无法调整,但可以(在程序外部)重新配置。在某些情况下,默认行为甚至是不可配置的。
Swing隐藏了许多细节,使图形编程比没有Swing时容易得多;并且,有许多技术可用于减少往返时间。首先,我会仔细检查您是否在事件处理程序中对EventDispatch线程进行了最少的工作。之后,如果您需要更高的速度,我建议使用this article。
如果您需要更快的速度,最终您的代码将看起来更像是其他绘图系统中围绕绘图代码的包装器,这就是SWT解决问题的方式;或者,您将放弃在Java程序中进行绘制,并以平台库的语言(通过JNI绑定到您的应用程序)进行绘制。如果您正在考虑“在EventDispatch线程中做最少的工作”之外的任何其他选择,请注意,您将花费越来越多的精力来获得很小的收获,而这种工作将导致代码从根本上变得更难维护,调试并迁移到“甚至略有不同”的操作系统(甚至同一操作系统的版本)。
很抱歉,答案很长,但是对于大多数人来说,JPopupMenu的实际绘制方式还是一个谜,在提出改进的方法之前必须解决这个问题。