【问题标题】:Actions stop working in Java操作在 Java 中停止工作
【发布时间】:2017-02-11 02:05:09
【问题描述】:

所以这是我的第一篇文章,如果我的语法不好,我真的很抱歉(英语不是我的母语)。我最近开始用java编程,我对学习很感兴趣。所以我开始了一些小项目来帮助我了解更多的基本知识并改进我的编码。

最近我读到了 keyListeners、keyBindings 和所有这些东西。所以我想我编写了一个非常基本的程序(几乎没有 gui),它应该像一架简单的钢琴一样工作:

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import java.awt.*;
import java.io.File;
import javax.swing.*;

import java.awt.event.*;

public class pianl1 {


    public static void main (String[] args){
        int i= 0;

        File C7 = new File("res/39191__jobro__piano-ff-044.wav");
        File D7 = new File ("res/39194__jobro__piano-ff-046.wav");

        JLabel lab1 =new JLabel("Hallo");
        frame.add(lab1);


        AbstractAction keyBindingReactorC7 = new AbstractAction(){
            @Override
            public void actionPerformed(ActionEvent e){
                Playsound(C7);
            }
        };
        AbstractAction keyBindingReactorD7 = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Playsound(D7);
            }

        JPanel panel= new JPanel(new FlowLayout());
        frame.getContentPane().add(panel);
        frame.pack();

        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("C"),
                "cPressed");
        panel.getActionMap().put("cPressed", keyBindingReactorC7);

        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"),
                "dPressed");
        panel.getActionMap().put("dPressed", keyBindingReactorD7);


    public static void Playsound(File Sound){
        try{
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(Sound));
            clip.start();

        }catch(Exception e){}

    }

}

而且它工作正常。如果我按下琴键,它会播放声音,即使我敲击琴键,声音也几乎没有延迟。这正是我想要的。

但是有一个小错误。

如果我一直按一个键,或者我同时按两个键(有效,它同时播放两个键),有时会发生根本没有声音的情况。我无能为力让它再次工作,我所能做的就是重新启动整个程序。这是随机发生的,我不知道为什么。我知道有什么东西阻止了我的操作持续工作。

【问题讨论】:

  • 使用 clip.is active() 或 clip.is running() 如果它什么都不做!
  • @Gacci 因为它们是作为局部变量创建的,所以它不会起作用——如果它可以用作实例字段是个好主意
  • 不要使用空的 catch 块。可能正在生成错误。
  • 好吧,你需要一个变量来引用找出对象状态。根本没有别的办法!!
  • @camickr 谢谢,我会解决的!

标签: java swing action key-bindings keystroke


【解决方案1】:

您的问题的答案围绕您最终想要实现的目标,如果您只是想停止多个声音播放,那么您需要某种可以监控的条件,如果您想停止“特定”声音从玩,那么你需要维护某种List 的标志,你可以检查。

因此,根据您的代码,假设您正在制作钢琴,因此您希望声音重叠,在这种情况下,我们需要某种方法来确定是否播放了特定的声音。

有很多方法可以实现这一点,但让我们选择Set,它允许您维护唯一的List 项目(无重复项)。

每次调用playSound,我们检查这个列表并确定声音是否已经播放过,如果没有,我们将声音添加到列表中并播放它,当声音停止时,我们从列表中删除它

核心...

我对你的代码做了一些改动,稍后我会详细解释它,但本质上,这是这个想法的“核心”......

private Set<File> playing = new HashSet<File>(25);

public void playsound(File sound) {
    try {
        // Is the been played or not?
        if (!playing.contains(sound)) {
            // And the sound to prevent it from been played again
            playing.add(sound);
            // Set up a new clip
            Clip clip = AudioSystem.getClip();
            // Monitor the clip's status, we want to know when it ends
            clip.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    // Clip has stopped
                    if (event.getType() == LineEvent.Type.STOP) {
                        // Release the resources
                        clip.close();
                        // Remove the sound from our list
                        // so it can be played again
                        playing.remove(sound);
                    }
                }
            });
            // Play it again Sam
            clip.open(AudioSystem.getAudioInputStream(sound));
            clip.start();
        }
    } catch (Exception e) {
        // Remove the sound if something goes wrong
        playing.remove(sound);
        e.printStackTrace();
    }

}

可运行示例

好的,所以我已经“更新”了你的代码,这就是为什么...

  • Java 有一些既定的编码约定,鼓励所有开发人员遵循,它们使其他人更容易阅读您的代码,而您也可以阅读他们的代码,请查看Code Conventions for the Java TM Programming Language 了解更多详细信息。话虽如此,我让你所有的变量都以小写字符开头
  • A 创建了一个自定义面板。好的,这只是我,但它让生活变得更轻松,因为您可以更轻松地封装您尝试实现的逻辑。因为您正在使用Action API,所以另一种选择是创建一个SoundManager 类,其中包含管理和播放声音的逻辑并传递对每个Action 的引用,就我个人而言,我' d 可能最终都得到了
  • 我使用EventQueue.invokeLater 来确保在事件调度线程的上下文中创建 UI,从而降低违反 Swing 单线程特性的任何可能风险,这总是一个好主意 ;)

您的尝试似乎有了一个良好的开端,做得很好,继续努力!

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {

            File c7 = new File("res/res39191__jobro__piano-ff-044.wav");
            File d7 = new File("res/39194__jobro__piano-ff-046.wav");

            add(new JLabel("Hello"));

            AbstractAction keyBindingReactorC7 = new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    playsound(c7);
                }
            };
            AbstractAction keyBindingReactorD7 = new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    playsound(d7);
                }
            };
            getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("C"),
                                                                                                                             "cPressed");
            getActionMap().put("cPressed", keyBindingReactorC7);

            getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"),
                                                                                                                             "dPressed");
            getActionMap().put("dPressed", keyBindingReactorD7);
        }

        private Set<File> playing = new HashSet<File>(25);

        public void playsound(File sound) {
            try {
                if (!playing.contains(sound)) {
                    playing.add(sound);
                    Clip clip = AudioSystem.getClip();
                    clip.addLineListener(new LineListener() {
                        @Override
                        public void update(LineEvent event) {
                            if (event.getType() == LineEvent.Type.STOP) {
                                clip.close();
                                playing.remove(sound);
                            }
                        }
                    });
                    clip.open(AudioSystem.getAudioInputStream(sound));
                    clip.start();
                }
            } catch (Exception e) {
                playing.remove(sound);
                e.printStackTrace();
            }

        }
    }
}

好的,但是我怎样才能让它一次只能播放一种声音呢?

啊,好问题……

基本上,我们使用单个剪辑并检查它的状态...

private Clip clip;

public void playsound(File sound) {
    try {
        if (clip == null) {
            clip = AudioSystem.getClip();
            clip.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType() == LineEvent.Type.STOP) {
                        clip.close();
                    }
                }
            });
        }
        // Is the clip active or running?
        if (!clip.isActive() && !clip.isRunning()) {
            if (clip.isOpen()) {
                clip.close();
            }
            clip.open(AudioSystem.getAudioInputStream(sound));
            clip.start();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

现在,我几乎没有使用 Java 的音频体验,但有些事情我可能会考虑尝试...

  • 创建一个AudioManagerSoundManager 类,用于管理声音并提供简单的播放界面(如playCplayD)。为每个声音预加载剪辑。这将需要一些额外的管理,因为当剪辑结束时,您需要将其“重置”回开始。这个“应该”让播放剪辑更快,但它可能会消耗更多资源,哪个权衡对你来说更重要?您还可以创建某种缓存,如果某个剪辑在一段时间内没有播放,它就会被关闭并丢弃。
  • 继续播放声音,直到松开按键...对你来说是个不错的挑战;)

【讨论】:

  • (1+) 好例子。 because when the clip ends, you will need to "reset" it back to the start. - 这可以通过在 clip.start() 之前使用 clip.setFramePosition(0) 来实现
  • @camickr 是的,我也想过,当时关闭似乎更简单:P,但如果你要重用剪辑(努力提高速度),那么 setFramePosition 绝对是要走的路!
  • 谢谢!这真的很有帮助,我很感激你付出了这么多努力来帮助我。
  • 因此,由于该问题没有新的反对票或接近投票,我可以假设问题不在于问题质量低下。由于没有其他答案没有被否决,我只能假设这个问题有问题。它在哪些方面不回答 OP 的问题?在哪些方面可以改进?由于您没有提供任何形式的反馈,在这一点上,我只能假设您个人不喜欢我试图帮助别人
  • 致反对者:由于您也未能提供额外的答案,我只能假设您也不知道如何解决问题,这就引出了一个问题,您为什么反对回答?
【解决方案2】:

除了@MadProgrammer 的大力帮助之外,我还注意到了其他一些事情。我在 Mac 上工作,但我也拥有 Windows 机器。我最近刚切换到 Mac,但我没有注意到(直到现在),如果你按住一个键足够长的时间,OSX 会自动显示更多的键选项(诸如û、ć 之类的东西)。 所以我把所有东西都切换到我的 Windows 机器上,瞧,它可以工作了。当然有一些小错误,但最后我对结果很满意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-28
    • 2020-02-06
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多