You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按键触发循环更新JFrame组件失效(重绘异常问题)

问题分析

你的问题核心是事件调度线程(EDT)被阻塞了。Swing的UI更新、事件处理都依赖EDT执行,而你的move()方法是在按键事件回调中调用的——按键事件本身就在EDT里运行。循环里的Thread.sleep(100)会直接卡住EDT,导致UI无法及时响应重绘请求,直到整个循环跑完,EDT才有空闲处理界面刷新。

解决方案

以下是两种可行的修复方式:

方式一:使用Swing Timer(推荐)

Swing Timer专门为EDT设计,用于执行延迟/重复任务,不会阻塞UI线程,是处理这类动画或定时更新场景的最优选择。

修改后的完整代码:

import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import javax.swing.*;

public class Show {
    
    static JFrame frame = new JFrame();
    static JPanel panel = new JPanel();
    static Timer moveTimer;
    static int moveCount = 0;

    public static void main(String[] args) throws IOException, InterruptedException {
        // 初始化窗口
        frame.setVisible(true);
        frame.setSize(1500, 500);
        frame.setLayout(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.getContentPane().setBackground(new Color(0,120,240));
        frame.addKeyListener(new KeyListener() {

            @Override
            public void keyPressed(KeyEvent e) {
                // 避免重复触发定时器
                if(e.getKeyCode() == KeyEvent.VK_SPACE && moveTimer == null) {
                    moveCount = 0;
                    // 创建定时器,每100ms执行一次更新逻辑
                    moveTimer = new Timer(100, event -> {
                        if(moveCount < 20) {
                            panel.setLocation(panel.getLocation().x+20, panel.getLocation().y+20);
                            moveCount++;
                        } else {
                            moveTimer.stop();
                            moveTimer = null;
                        }
                    });
                    moveTimer.start();
                }
            }

            @Override
            public void keyTyped(KeyEvent e) {}

            @Override
            public void keyReleased(KeyEvent e) {}
        });

        // 初始化面板
        panel.setVisible(true);
        panel.setLocation(150,150);
        panel.setSize(150, 50);
        panel.setBackground(new Color(0,0,0));
        frame.add(panel);

        frame.repaint();
    }
}

方式二:单独线程+EDT更新UI

如果必须使用线程,要确保所有UI修改操作通过SwingUtilities.invokeLater放到EDT中执行,避免线程安全问题:

修改原代码中的move()方法:

public static void move() {
    new Thread(() -> {
        for(int i=0;i<20;i++) {
            // 将UI更新逻辑提交到EDT执行
            SwingUtilities.invokeLater(() -> {
                panel.setLocation(panel.getLocation().x+20, panel.getLocation().y+20);
            });
            try {
                Thread.sleep(100);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
    }).start();
}
关键注意事项
  • 绝对不要在EDT中执行耗时操作(比如Thread.sleep、长循环),否则会直接导致UI卡顿、无响应。
  • 所有Swing组件的修改必须在EDT中完成,Swing是非线程安全的,跨线程直接操作UI可能引发渲染异常或崩溃。

内容的提问来源于stack exchange,提问作者soxs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:30:40