Java 停止声音按钮无法正常工作

问题描述

所以我最近一直在制作一个停止按钮,并且一直想知道如何在已经播放时立即停止按钮声音。

问题:当您单击停止声音按钮时,它只会停止您按下的下一个按钮。

我想要实现的目标:当您单击停止声音按钮时,它会停止所有播放的声音。

这是主要的按钮类声音:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

//JButtons Class
public class Buttons extends JButton implements ActionListener{
  private int locX = 0;
  private int locY = 0;

  //Sets the basic features of the buttons and adds an action listener
  public Buttons(String title){
    super(title);
    setBounds(locX,locY,100,100);
    setopaque(true);
    setBorderPainted(false);
    setBorder(BorderFactory.createLineBorder(Color.WHITE));
    addActionListener(this);
  }

  //Sets the dimentions of the buttons
  public void setDimentions(int x,int y){
    this.locX = x;
    this.locY = y;
    setBounds(locX,100);
  }

  //Maps button colors to sting values
  static Map<String,Color> colorMap = Map.ofEntries(Map.entry("WHITE",Color.WHITE),Map.entry("GRAY",Color.GRAY),Map.entry( "BLACK",Color.BLACK),Map.entry( "RED",Color.RED),Map.entry( "ORANGE",new Color(255,121,0)),Map.entry( "YELLOW",Color.YELLOW),Map.entry( "GREEN",Color.GREEN),Map.entry( "BLUE",Color.BLUE),Map.entry( "magenta",Color.magenta),Map.entry( "PINK",Color.PINK),Map.entry( "CYAN",Color.CYAN));

  //Gets the color from the map and returns it
  static Color getColor(String col){
    return colorMap.get(col.toupperCase());
  }

  //Sets the color of the button and repaints it
  public void setColors(String colorBack,String colorFront){
    setBackground(getColor(colorBack));
    setForeground(getColor(colorFront));
    repaint();
  }

  public String[] listFilesForFolder(final File folder) {
    String[] f = new String[25];
    int count = 0;
    for(int i = 0; i < 25; i++){
      f[i] = "";
    }
    for (final File fileEntry : folder.listFiles()) {
        if (fileEntry.isDirectory()) {
            listFilesForFolder(fileEntry);
        } else {
            if(fileEntry.getName().equals(".DS_Store")){

            }else{
              f[count] = fileEntry.getName();
              count++;
            }
        }
    }
    return f;
  }

  public void playSound(String url,boolean loop,boolean stop){
    try{
      AudioInputStream audioIn = AudioSystem.getAudioInputStream(Launchpad.class.getResource("soundFiles/" + url));
      Clip clip = AudioSystem.getClip();
      clip.open(audioIn);
      clip.start();
      if(loop == true){
        clip.loop(Clip.LOOP_CONTINUOUSLY);
      }
      if(stop == true){
        stopSound(clip);
      }
    }
    catch(Exception e){
      System.out.println("Error");
    }
  }

  public void stopSound(Clip clip){
    if(clip.isActive()){
      clip.stop();
      clip.flush();
      clip.close();
    }
  }

  //Event Handler / Action Listener
  @Override
  public void actionPerformed(ActionEvent e){
    if(e.getSource() == this){
      String sNum = this.getText();
      int num = Integer.parseInt(sNum);
      final File folder = new File("/Users/ethanbowles/Desktop/idk/programing/java/Launchpad/soundFiles");
      String[] names =listFilesForFolder(folder);
      System.out.println(names[num - 1]);
      System.out.println(num);
      boolean fullStop = StopButton.stop;
      playSound(names[num - 1],LoopButton.loop,fullStop);
      StopButton.stop = false;
      LoopButton.loop = false;
    }
  }
}

这是主要的声音停止按钮:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;

public class StopButton extends JButton implements ActionListener{
  public static boolean stop = false;
  public StopButton(){
    super("Stop");
    setBounds(10,10,50);
    addActionListener(this);
  }

  @Override
  public void actionPerformed(ActionEvent e){
    if(e.getSource() == this){
      if(stop == true){
        stop = false;
      }else{
        stop = true;
      }
      super.repaint();

    }
  }
}

解决方法

我认为您需要更改 Clip 的范围。目前它们是局部变量。它们需要是实例变量。然后,将所有符合“停止”条件的 Clip 放入一个集合中。如果您想一次停止它们,您可以遍历该集合。

拥有 Clip 的集合需要更多的开销。例如,您必须确保在完成集合后从集合中删除 Clip。此外,由于遍历集合和添加/删除可能同时发生,因此线程安全集合(例如 CopyOnWriteArrayList)可能比 ArrayList 更好。

,

问题是由于一个 JButton 中的 .stop 字段被另一个 JButton 修改的逻辑。重用 playSound() 来播放或停止也不是一个好的设计。

这是一种设计更简洁的解决方案,带有 2 个按钮,一个用于播放,一个用于停止。

MusicController 独立于 UI:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Database Project</title>
  <script src="https://kit.fontawesome.com/d6307e6979.js" crossorigin="anonymous"></script>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div class="container">
    <h2>Sales</h2>

    <div class="select-box">
      <div class="options-container">

      </div>
      <div class="selected">
        <p>Select Item</p>
      </div>
    </div>
  </div>

  <script src="main.js"></script>
</body>

</html>

View 管理 UI,只监听 MusicController 状态。

class MusicController
{
    // A property for the state of the controller
    public final static String PROP_STATE = "StateProperty";

    enum State
    {
        NOT_READY,STOPPED,PLAYING
    };
    State state = State.NOT_READY;
    boolean loop;
    
    // Manage property change listeners
    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 

    public void loadSound()
    {
        // Initialize music data,load clip from file etc.
        ...
        State oldState = state;        
        state = State.STOPPED;  // We can actually play a sound only from the STOPPED state
        pcs.firePropertyChange(PROP_STATE,oldState,state);
    }

    public State getState()
    {
        return state;
    }

    public void play()
    {
        switch (state)
        {
            case NOT_READY:
                // Error "Not ready"
                ...
                break;
            case STOPPED:
                // Start playback (looped if loop is true)
                ...
                State oldState = state;                
                state = State.PLAYING;
                pcs.firePropertyChange(PROP_STATE,state);   // Notify listeners
                break;
            case PLAYING:
                // Already playing,do nothing
                break;
            default:
                throw new AssertionError(state.name());
        }
    }

    public void stop()
    {
        // Same code structure than play(),but adapted to stop playback if current state is PLAYING.
        ...
    }

    public void addPropertyChangeListener(PropertyChangeListener listener)
    {
        pcs.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener)
    {
        pcs.removePropertyChangeListener(listener);
    }

}