Java实现F11切换全屏时报Component must have a valid peer错误原因
错误原因
- 有效性判断逻辑不匹配
你使用的JFrame.isValid()仅判断Swing容器的布局状态是否合法,和Canvas是否拥有有效的渲染peer没有直接关联。窗口布局合法的情况下,完全可能出现Canvas还未完成和新窗口的peer绑定、或是旧peer已经被销毁的情况,这时候操作BufferStrategy就会触发报错。 - 组件复用逻辑缺失步骤
你在销毁旧JFrame前没有将Canvas从旧容器中主动移除,旧窗口销毁时会连带处理关联组件的peer状态,直接将未脱离旧容器的Canvas添加到新窗口,会导致peer状态混乱。 - 窗口初始化顺序错误
你在createFrame方法中先执行jf.setVisible(true),再执行jf.add(canvas),窗口显示后才挂载Canvas,会导致Canvas的peer创建严重滞后,和渲染线程的执行时序发生随机冲突。 - 线程安全问题
如果你的窗口创建、销毁逻辑,以及渲染逻辑分别在不同线程执行(比如渲染跑在自定义的游戏循环线程,窗口操作跑在事件线程/其他线程),没有做同步处理的情况下,必然会出现状态判断和实际组件状态不一致的问题。
修复方案
- 调整窗口切换逻辑,销毁旧窗口前先移除Canvas,且所有UI操作放在Swing EDT调度线程执行:
public void setFullscreen(boolean bool) { fullscreen = bool; SwingUtilities.invokeLater(() -> { // 先将Canvas从旧容器移除 jf.getContentPane().remove(canvas); jf.dispose(); jf = createFrame(); }); }
- 修正
createFrame的执行顺序,所有配置完成后再显示窗口:
public JFrame createFrame() { JFrame jf = new JFrame(title); jf.getContentPane().setPreferredSize(new Dimension(width, height)); jf.setMaximumSize(new Dimension(width, height)); jf.setMinimumSize(new Dimension(width, height)); jf.setLocationRelativeTo(null); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setResizable(false); if(fullscreen) { jf.setExtendedState(JFrame.MAXIMIZED_BOTH); jf.setUndecorated(true); } // 先添加组件、打包,最后再显示窗口 jf.add(canvas); jf.pack(); jf.setIconImage(img); jf.setVisible(true); jf.requestFocus(); return jf; }
- 替换有效性判断逻辑,直接判断Canvas的可显示状态:
public void render() { // 仅当Canvas已经挂载到容器、有有效peer时才执行渲染 if(canvas.isDisplayable()) canvas.render(); }
内容的提问来源于stack exchange,提问作者Andreas Sandberg
相关产品推荐
相关产品推荐

