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

标签 java swing audio javasound

我的游戏对话框结束后,应该会显示一个可怕的弹出窗口,并伴有刺耳的噪音。当我单击按钮(BPanel)时,图片显示为“损坏”,直到声音文件播放完毕。尖叫声结束后,画面弹出。

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

主要方法:

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

    }

}

声音等级:

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 设计中的一种幻觉。影响组件个体尺寸的因素太多,您无法控制其中任何一个。 Swing 的设计初衷是与布局管理器一起工作,放弃它们将导致无穷无尽的问题,您将花费越来越多的时间来尝试纠正

您不应该使用带按钮的 MouseListener,而应使用 ActionListener,触发按钮的方法不止一种。请参阅How to Use Buttons, Check Boxes, and Radio ButtonsHow to Write an Action Listeners了解更多详情

如果您想知道音频何时结束,您应该使用 LineListenerfor example 。这样,您可以选择音频完成后要执行的操作。就个人而言,我会将 LineListener 的实例传递给您的 Sound 类,因为您的 Sound 类不应该关心播放之外的任何内容。声音

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 类并向其应用 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 的更复杂示例,请查看 Playing multiple sound clips using Clip objects

关于java - Java Swing 中面板无法与声音文件同时显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34758718/

相关文章:

Java代码编译错误

java - 当我单击 jList 中的项目时,背景颜色不会改变

java - GridBagLayout 不根据权重分配列

java语音聊天使用multicastsocket编程

ios - 音频AVAssetWriter静音

iOS 如何将 USB 设备的特定 channel 设置为音频播放器? AV基金会

java - 绕过警报 "Do you want to run this application"

java - Bitfinex API 错误消息 "Key symbol was not present."

java - Kafka Consumer Custom MetricReporter 未接收指标

java - 用 Java 绘制 Hello World 最快的方法是什么