如何通过鼠标点击事件停止或继续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 } }
代码说明
- volatile变量的作用:
shouldNext和shouldReplay用volatile修饰,确保UI线程修改后,播放线程能立刻看到最新值,避免线程缓存导致的不一致问题。 - 线程分离:播放逻辑放到独立线程中,点击按钮时UI不会卡顿,因为UI线程不会被播放逻辑阻塞。
- 标记重置:每次进入新声音播放或处理完标记后,重置标记状态,避免下一次循环受到上一次操作的影响。
- 修正原代码错误:修复了数组初始化、方法调用等语法问题(比如原代码中的
sonido.generateSound[opcNotes]和sonido[i]().play都是语法错误)。
内容的提问来源于stack exchange,提问作者Luis Enrique Bermúdez García
相关产品推荐
相关产品推荐

