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

JDialogue组件在while循环结束前不显示的问题求助

Java Swing战斗场景JDialog实时更新问题解决

问题根源

你遇到的核心问题是Swing事件调度线程(EDT)被阻塞。Swing的所有UI更新、事件处理都必须在EDT中执行,而你的fight()方法是由UI触发的事件(比如按钮点击)调用的,属于EDT线程。里面的while循环加上Thread.sleep()会完全占用EDT,导致UI无法进行实时重绘,直到整个循环执行完毕,才会一次性刷新所有组件的最终状态。


解决方案1:使用Swing Timer替代while循环(推荐)

Swing Timer是专为UI定时任务设计的,它会自动在EDT中调度任务,不会阻塞UI线程。我们可以用它来拆分战斗循环的每一步:

public void setupFight(Character c) {
    fightPopup.setLocationRelativeTo(this);
    fightPopup.setVisible(true);
    mcBar.setMinimum(0);
    mcBar.setMaximum(mc.getAtkspd());
    enemyBar.setMinimum(0);
    enemyBar.setMaximum(c.getAtkspd());
    enemyDm.setVisible(false);
    enemyDmType.setVisible(false);
    mcDm.setVisible(false);
    mcFight.setVisible(true);
    enemyFight.setVisible(true);
    
    // 初始化战斗状态
    mc.setAtkbr(mc.getAtkspd());
    c.setAtkbr(c.getAtkspd());
    startFightLoop(c);
}

private void startFightLoop(Character c) {
    // 每10ms触发一次战斗步骤
    Timer fightTimer = new Timer(10, e -> {
        if (mc.getHP() > 0 && c.getHP() > 0) {
            mc.setAtkbr(mc.getAtkbr() - 10);
            c.setAtkbr(c.getAtkbr() - 10);
            
            // 实时更新UI组件
            mcBar.setValue(mcBar.getMaximum() - mc.getAtkbr());
            enemyBar.setValue(enemyBar.getMaximum() - c.getAtkbr());
            enemyDmType.setText("hola");
            
            int[] damage1;
            int[] damage2;
            if (mc.getAtkbr() == 0 && c.getAtkbr() == 0) {
                damage1 = mc.attack(c);
                damage2 = c.attack(mc);
                mc.setHP(mc.getHP() - damage2[1]); // mc承受敌人攻击伤害
                c.setHP(c.getHP() - damage1[1]); // 敌人承受mc攻击伤害
                mc.setAtkbr(mc.getAtkspd());
                c.setAtkbr(c.getAtkspd());
            } else if (mc.getAtkbr() == 0) {
                damage1 = mc.attack(c);
                c.setHP(c.getHP() - damage1[1]);
                mc.setAtkbr(mc.getAtkspd());
            } else if (c.getAtkbr() == 0) {
                damage2 = c.attack(mc);
                mc.setHP(mc.getHP() - damage2[1]);
                c.setAtkbr(c.getAtkspd());
            }
        } else {
            // 战斗结束,停止定时器
            ((Timer)e.getSource()).stop();
            // 处理战斗结果
            if (mc.getHP() <= 0) {
                ((MC) mc).defeat();
            } else {
                ((MC) mc).pickUp(((Enemy) c).defeat());
            }
        }
    });
    fightTimer.start();
}

解决方案2:后台线程+SwingUtilities.invokeLater更新UI

如果坚持使用while循环,需要把战斗逻辑放到后台线程中,所有UI更新操作必须通过SwingUtilities.invokeLater()提交到EDT执行:

public void setupFight(Character c) {
    fightPopup.setLocationRelativeTo(this);
    fightPopup.setVisible(true);
    mcBar.setMinimum(0);
    mcBar.setMaximum(mc.getAtkspd());
    enemyBar.setMinimum(0);
    enemyBar.setMaximum(c.getAtkspd());
    enemyDm.setVisible(false);
    enemyDmType.setVisible(false);
    mcDm.setVisible(false);
    mcFight.setVisible(true);
    enemyFight.setVisible(true);
    
    // 启动后台线程执行战斗逻辑
    new Thread(() -> fight(c)).start();
}

public void fight(Character c) {
    mc.setAtkbr(mc.getAtkspd());
    c.setAtkbr(c.getAtkspd());
    int[] damage1 = new int[2];
    int[] damage2 = new int[2];
    System.out.println(mc.getAtk());
    
    while (mc.getHP() > 0 && c.getHP() > 0) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException ex) {
            Logger.getLogger(Character.class.getName()).log(Level.SEVERE, null, ex);
            Thread.currentThread().interrupt();
            break;
        }
        
        mc.setAtkbr(mc.getAtkbr() - 10);
        c.setAtkbr(c.getAtkbr() - 10);
        
        // UI更新操作必须提交到EDT执行
        SwingUtilities.invokeLater(() -> {
            mcBar.setValue(mcBar.getMaximum() - mc.getAtkbr());
            enemyBar.setValue(enemyBar.getMaximum() - c.getAtkbr());
            enemyDmType.setText("hola");
        });
        
        if (mc.getAtkbr() == 0 && c.getAtkbr() == 0) {
            damage1 = mc.attack(c);
            damage2 = c.attack(mc);
            mc.setHP(mc.getHP() - damage2[1]);
            c.setHP(c.getHP() - damage1[1]);
            mc.setAtkbr(mc.getAtkspd());
            c.setAtkbr(c.getAtkspd());
        } else if (mc.getAtkbr() == 0) {
            damage1 = mc.attack(c);
            c.setHP(c.getHP() - damage1[1]);
            mc.setAtkbr(mc.getAtkspd());
        } else if (c.getAtkbr() == 0) {
            damage2 = c.attack(mc);
            mc.setHP(mc.getHP() - damage2[1]);
            c.setAtkbr(c.getAtkspd());
        }
    }
    
    // 战斗结束后的UI操作同样要提交到EDT
    SwingUtilities.invokeLater(() -> {
        if (mc.getHP() <= 0) {
            ((MC) mc).defeat();
        } else {
            ((MC) mc).pickUp(((Enemy) c).defeat());
        }
    });
}

重要提示

  1. 原代码逻辑修正:你原代码中HP更新的逻辑完全写反了(比如mc攻击后反而自己减血),上面的方案已经修正了这个问题,必须同步修改才能保证战斗逻辑正确。
  2. EDT线程规则:Swing是单线程模型,任何UI相关操作都不能在后台线程直接执行,否则会导致UI异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 05:22:51