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

Java Stop Sound Button Not Working Correctly

所以我最近一直在做一个停止按钮,一直想知道如何在播放时立即停止按钮声音。

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

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

这是主按钮 class 声音:

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,locY,100,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,100,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。此外,因为遍历集合和 adding/removing 可能会同时发生,所以 CopyOnWriteArrayList 之类的线程安全集合可能是比 ArrayList.

更好的选择。

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

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

MusicController 独立于 UI:

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, oldState, 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);
    }

}

View 管理 UI 并且只监听 MusicController 状态。

class View implements PropertyChangeListener
{

    JButton playButton, stopButton;
    MusicController controller = new MusicController();

    public View()
    {
        // Listen to music controller state changes
        controller.addPropertyChangeListener(this);

        // Create UI
        Action playAction = new AbstractAction("Play")
        {
            public void actionPerformed(ActionEvent ae)
            {
                controller.play();
            }
        };
        playButton = new JButton(playAction);
        
        // Same for stopButton with controller.stop();
        ...
                
        // Add buttons to UI etc.
        ...

        updateUI(controller.getState());
    }

    /**
     * Update the user interface depending on the music controller state.
     *
     * @param state
     */
    private void updateUI(State state)
    {
        switch (state)
        {
            case NOT_READY:
                playButton.setEnabled(false);
                stopButton.setEnabled(false);
                break;
            case STOPPED:
                playButton.setEnabled(true);
                stopButton.setEnabled(false);
                break;
            case PLAYING:
                playButton.setEnabled(false);
                stopButton.setEnabled(true);
                break;
            default:
                throw new AssertionError(state.name());
        }
    }

    /**
     * Called when a MusicController property has changed.
     */
    public void propertyChange(PropertyChangeEvent evt)
    {
        if (evt.getSource() == controller && MusicController.PROP_STATE.equals(evt.getPropertyName()))
        {
            // State has changed, update UI accordingly
            State state = (State) evt.getNewValue();
            updateUI(state);
        }
    }

}