在 Java Swing 中面板无法与声音文件同时显示

Panel Fails to Show Up Concurrently with Sound File In Java Swing

我的游戏对话框结束后,会出现一个可怕的弹出窗口,并伴有刺耳的噪音。当我点击按钮 (BPanel) 时,图片出现 "corrupted" 直到声音文件播放完毕。尖叫结束后,画面弹出。

是否可以将两者简单地同步在一起? 请注意,问题发生在 Woc class 中,其中使用了 Scaryface.png 和声音 class。

主要方法:

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

public class Game {
    public static void main(String args[]) {
        Woc WineOrCheese = new Woc("Wine or Cheese");
        WineOrCheese.applyBackground("TitleBackground.png");
        WineOrCheese.applyButton("Play.png", 250, 200, 400, 200);
    }
}

woc 是 JFrame

import static java.lang.System.out;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


public class Woc extends JFrame {

private JFrame window;
private Woc.BPanel background;

private BufferedImage backgroundImg;
final int HEIGHT = 600;
final int WIDTH = 900;
private BufferedImage scaryImg;

public Woc(String text) {
    /*********************************
     * Sets up window. *
     ********************************/
    window = new JFrame(text);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setLayout(null);

    window.setVisible(true);
    window.setSize(WIDTH, HEIGHT);
    window.setLocationRelativeTo(null);
}

public void applyBackground(String ImgName) {
    try {
        backgroundImg = ImageIO.read(getClass().getResource(ImgName));
    } catch (IOException e) {
        out.println("No image detected");
    }

    background = new Woc.BPanel(backgroundImg, 0, 0, WIDTH, HEIGHT);

    window.add(background);
    background.setBounds(0, 0, WIDTH, HEIGHT);
}

public void applyButton(String ImgName, int x1, int y1, int width,
        int height) {
    BufferedImage buttonImg = null;
    try {
        buttonImg = ImageIO.read(getClass().getResource(ImgName));
    } catch (IOException e) {
    }

    Woc.BPanel button = new Woc.BPanel(buttonImg, x1, y1, width, height);

    window.add(button);
    button.setBounds(0, 0, WIDTH, HEIGHT);
    button.addMouseListener(new Clicker());

}

public static void play(String filename) {
    try {
        Clip clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(new File(filename)));
        clip.start();
    } catch (Exception exc) {
        exc.printStackTrace(System.out);
    }
}

private class BPanel extends JPanel {
    public BufferedImage img;
    public int x1, y1, width, height;

    public BPanel(BufferedImage img, int x1, int y1, int width, int height) {
        super();
        this.img = img;
        this.x1 = x1;
        this.y1 = y1;
        this.width = width;
        this.height = height;
        // this.setOpaque(false);
        this.setBackground(new Color(0, 0, 0, 0));
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, x1, y1, width, height, null);
    }
}

private class Clicker implements MouseListener {

    @Override
    public void mouseClicked(MouseEvent arg0) {

        //Dialog of game is here in the form of JOptionPane.

        applyBackground("Scaryface.png");
        for (int k = 0; k < 10; k++) {
            for (int z = 0; z < 10; z++) {
                out.println(".");
            }
        }

        Sound scary = null;
        try {
            scary = new Sound("scary.wav", window);
        } catch (Exception e) {
        }

    }

    @Override
    public void mouseEntered(MouseEvent arg0) {

    }

    @Override
    public void mouseExited(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseReleased(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

}

声音class:

import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.*;

public class Sound {

    public Sound(String s, JFrame win) throws InterruptedException {
        Clip play = null;
        try {
            File in = new File(s);
            AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(in);
            play = AudioSystem.getClip();
            play.open(audioInputStream);
            FloatControl volume = (FloatControl) play.getControl(FloatControl.Type.MASTER_GAIN);
            volume.setValue(1.0f); // Reduce volume by 10 decibels.
            play.start();
            // Loop until the Clip is not longer running.
            // We loop this way to allow the line to fill, otherwise isRunning will
            // return false
            do {
                Thread.sleep(10);
            } while (play.isRunning());
            play.drain();
        } catch (UnsupportedAudioFileException | IOException | LineUnavailableException ex) {
            ex.printStackTrace();
        } finally {
            try {
                play.close();
            } catch (Exception exp) {
            }
        }
    }
}

顺便问一下,有什么技巧可以让我的游戏更容易编写吗?有什么方法或 class 可以改善和减轻我必须经历的痛苦吗? (好吧,不是真正的疼痛,但仍然很痛)

do { 
    Thread.sleep(10); 
} while (play.isRunning());

play.drain();

正在使用上述代码阻止事件调度线程,从而阻止 UI 更新。有关详细信息,请参阅 Concurrency in Swing

避免使用 null 布局,像素完美布局是现代 ui 设计中的一种错觉。影响组件个体大小的因素太多,none 是您可以控制的。 Swing 旨在与核心的布局管理器一起工作,丢弃这些将导致无穷无尽的问题和问题,您将花费越来越多的时间来尝试纠正

您不应使用带按钮的 MouseListener,而应使用 ActionListener,触发按钮的方法不止一种。有关详细信息,请参阅 How to Use Buttons, Check Boxes, and Radio Buttons and How to Write an Action Listeners

如果您想知道音频何时结束,您应该使用 LineListener。有了这个,您可以选择音频完成后要做什么。就个人而言,我会将 LineListener 的实例传递给您的 Sound class,因为您的 Sound class 除了播放声音之外不应该关心任何其他事情

Without the while loop, the sound is unable to play. How do I resolve this issue because this was the only way I was able to play the sound

在音频完成之前不要关闭 Clip,为此我会使用(另一个)LineListener

下面的示例基本上采用您的 Sound class 并对其应用 LineListener。一种用于通知相关方(启动声音的人)有关线路事件,一种用于内部监控剪辑并在它停止时关闭它。

该示例使用 ReentrantLockCondition 来停止代码执行,直到剪辑完成,在此示例中,这将阻止 JVM 终止,但您不需要,我只是用来提供一个基本的演示

import java.io.File;
import java.io.IOException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class TestAudio {

    public static void main(String[] args) {
        ReentrantLock lockWait = new ReentrantLock();
        Condition conWait = lockWait.newCondition();
        try {
            new Sound("...", new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType().equals(LineEvent.Type.STOP)) {
                        System.out.println("Line has stopped");
                        lockWait.lock();
                        try {
                            conWait.signal();
                        } finally {
                            lockWait.unlock();
                        }
                    }
                }
            });
            System.out.println("Waiting for audio to finish");
            lockWait.lock();
            try {
                conWait.await();
            } finally {
                lockWait.unlock();
            }
            System.out.println("Audio has finished");
        } catch (InterruptedException | LineUnavailableException | IOException | UnsupportedAudioFileException exp) {
            exp.printStackTrace();
        }
    }

    public static class Sound {

        private Clip play;

        public Sound(String s, LineListener listener) throws InterruptedException, LineUnavailableException, IOException, UnsupportedAudioFileException {
            play = null;
            File in = new File(s);
            AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(in);
            play = AudioSystem.getClip();
            play.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType().equals(LineEvent.Type.STOP)) {
                        System.out.println("Audio stopped, closing clip");
                        play.close();
                    }
                }
            });
            play.addLineListener(listener);
            play.open(audioInputStream);
            FloatControl volume = (FloatControl) play.getControl(FloatControl.Type.MASTER_GAIN);
            volume.setValue(1.0f); // Reduce volume by 10 decibels.
            play.start();
        }
    }

}

有关更复杂的示例,使用 Swing 和 Clip,请查看