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

如何通过鼠标点击事件停止或继续Java Applet中的循环?

解决Java Applet中跨类控制循环停止与继续的问题

我来帮你拆解这个问题,核心就是用线程安全的标记变量跨类传递控制信号,同时要把播放逻辑和UI线程分离,避免界面卡死。下面给你一步步讲清楚实现思路和完整代码:

核心思路

按钮点击事件在UI线程触发,而声音播放循环如果放在UI线程会导致界面卡顿,所以必须把播放逻辑放到独立的工作线程里。用volatile修饰的布尔变量作为控制标记最合适——它能保证不同线程之间的变量可见性,避免出现线程看不到变量更新的情况。

具体实现步骤

  • 定义全局控制标记:在主Applet类里声明两个volatile布尔变量,分别控制“跳转到下一个”和“重新播放当前”的逻辑。
  • 绑定按钮点击事件:在按钮的鼠标监听器里修改标记变量的值。
  • 循环内检查标记:在播放循环的每一次迭代中,检查标记状态,根据状态执行跳过/重新播放操作。
  • 线程分离:播放逻辑必须放在单独线程中,不能阻塞UI线程。

完整修正后的示例代码

主Applet类(包含按钮、控制逻辑和播放线程)

import java.applet.Applet;
import java.awt.Button;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class PM extends Applet {
    // 线程安全的控制标记,volatile保证跨线程可见性
    private volatile boolean shouldNext = false;
    private volatile boolean shouldReplay = false;
    private Button btnNext;
    private Button btnPlayAgain;
    private Sonido[] sonidoArray;
    private int numTries;

    @Override
    public void init() {
        // 初始化按钮
        btnNext = new Button("Next");
        btnPlayAgain = new Button("Play Again");
        add(btnNext);
        add(btnPlayAgain);

        // 绑定Next按钮点击事件
        btnNext.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                shouldNext = true; // 标记为跳转到下一个声音
            }
        });

        // 绑定Play Again按钮点击事件
        btnPlayAgain.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                shouldReplay = true; // 标记为重新播放当前声音
            }
        });
    }

    // Applet启动时触发播放逻辑
    @Override
    public void start() {
        // 示例参数,可根据实际情况调整
        boolean[] opcNotes = {true, false, true, true};
        numTries = 3;

        // 初始化声音数组(修正原代码语法错误)
        sonidoArray = new Sonido[opcNotes.length];
        for (int i = 0; i < sonidoArray.length; i++) {
            sonidoArray[i] = Sonido.generateSound(opcNotes[i]);
        }

        // 播放逻辑放到单独线程,避免阻塞UI
        new Thread(this::playSounds).start();
    }

    private void playSounds() {
        for (int i = 0; i < sonidoArray.length; i++) {
            // 每次进入新声音前重置标记,避免上一次操作影响
            shouldNext = false;
            shouldReplay = false;

            Sonido currentSonido = sonidoArray[i];
            currentSonido.play();

            for (int j = 0; j < numTries; j++) {
                // 检查是否需要跳转到下一个声音
                if (shouldNext) {
                    shouldNext = false;
                    break; // 跳出内部循环,进入下一个声音的播放
                }

                // 检查是否需要重新播放当前声音
                if (shouldReplay) {
                    shouldReplay = false;
                    currentSonido.play();
                    j--; // 抵消循环的j++,让当前尝试次数不增加
                    continue;
                }

                // 模拟播放间隔,可替换为你的业务逻辑
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
            }
        }
    }
}

模拟的Sonido类(可根据实际音频逻辑修改)

public class Sonido {
    // 模拟生成声音的静态方法
    public static Sonido generateSound(boolean note) {
        return new Sonido();
    }

    // 模拟播放声音的方法
    public void play() {
        System.out.println("Playing sound...");
        // 这里替换为实际的音频播放逻辑,比如调用Java音频API
    }
}

代码说明

  1. volatile变量的作用:shouldNext和shouldReplay用volatile修饰,确保UI线程修改后,播放线程能立刻看到最新值,避免线程缓存导致的不一致问题。
  2. 线程分离:播放逻辑放到独立线程中,点击按钮时UI不会卡顿,因为UI线程不会被播放逻辑阻塞。
  3. 标记重置:每次进入新声音播放或处理完标记后,重置标记状态,避免下一次循环受到上一次操作的影响。
  4. 修正原代码错误:修复了数组初始化、方法调用等语法问题(比如原代码中的sonido.generateSound[opcNotes]和sonido[i]().play都是语法错误)。

内容的提问来源于stack exchange,提问作者Luis Enrique Bermúdez García

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:47:59