含可聚焦组件的JPopupMenu导致窗口边框闪烁的解决方法
我在JPopupMenu中嵌入了JTextField组件,当菜单部分超出主窗口显示时,窗口边框会出现闪烁;若菜单完全在窗口内则无此现象。排查发现将JTextField设置为不可聚焦可消除闪烁,但这会导致无法在文本框中输入内容,并非可行方案。
示例中,左键点击窗口时,弹出的菜单中文本框不可聚焦,显示正常;右键点击时,文本框可聚焦,弹出菜单前应用窗口边框会闪烁。若将窗口尺寸调至大于菜单,两种情况均无闪烁。
可行解决方案
方案1:延迟请求文本框焦点
问题根源在于弹出菜单前设置JTextField可聚焦会触发窗口不必要的重绘逻辑,导致边框闪烁。我们可以先保持文本框可聚焦,在菜单弹出后再延迟请求焦点:
修改鼠标点击事件的逻辑:
@Override public void mouseClicked(MouseEvent e) { textField.setFocusable(true); popupMenu.show(header, e.getX(), e.getY()); // 右键点击时延迟请求焦点 if (SwingUtilities.isRightMouseButton(e)) { SwingUtilities.invokeLater(() -> { textField.requestFocusInWindow(); }); } else { textField.setFocusable(false); } }
通过SwingUtilities.invokeLater把焦点请求放到事件队列末尾,等菜单完全弹出后再执行,避免弹出前的重绘冲突,既保留输入功能,又消除闪烁。
方案2:自动调整菜单弹出位置
重写JPopupMenu的show方法,让菜单自动调整位置,尽可能在主窗口范围内显示,从根源上避免触发闪烁的场景:
JPopupMenu popupMenu = new JPopupMenu() { @Override public void show(Component invoker, int x, int y) { Window topWindow = SwingUtilities.getWindowAncestor(invoker); if (topWindow != null) { Rectangle windowBounds = topWindow.getBounds(); Dimension popupSize = getPreferredSize(); // 计算不超出窗口的坐标 int adjustX = x + popupSize.width > windowBounds.width ? windowBounds.width - popupSize.width : x; int adjustY = y + popupSize.height > windowBounds.height ? windowBounds.height - popupSize.height : y; // 确保坐标非负 adjustX = Math.max(adjustX, 0); adjustY = Math.max(adjustY, 0); super.show(invoker, adjustX, adjustY); } else { super.show(invoker, x, y); } } };
这样菜单会自动避开窗口边界,始终在可视范围内显示,自然不会出现边框闪烁问题,同时JTextField保持正常输入功能。
修改后的完整代码(方案1)
import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; public class PopupTesting { public static void main( String[] args ) { SwingUtilities.invokeLater( () -> { JPopupMenu popupMenu = new JPopupMenu(); for( int i = 0; i < 10; i++ ) { popupMenu.add( new JCheckBoxMenuItem( "A Menu " + i ) ); } JTextField textField = new JTextField( "" ); popupMenu.add( textField ); JFrame frame = new JFrame( "Popup Testing" ); frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); JLabel header = new JLabel( "Header" ); header.setPreferredSize( new Dimension( 200, 100 ) ); header.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { textField.setFocusable(true); popupMenu.show( header, e.getX(), e.getY() ); if (SwingUtilities.isRightMouseButton(e)) { SwingUtilities.invokeLater(() -> textField.requestFocusInWindow()); } else { textField.setFocusable(false); } } } ); frame.add( header ); frame.pack(); frame.setVisible( true ); } ); } }
内容的提问来源于stack exchange,提问作者colomb
相关产品推荐
相关产品推荐

