例如,当我显示时,我将如何播放用户设置的感叹JOptionPane.WARNING_MESSAGE
声或显示时的错误声音JOptionPane.ERROR_MESSAGE
?
3 回答
我的假设——没有什么特别需要做的,JOptionPane 就是这么做的——基于浏览 BasicOptionPaneUI 代码并检查是否安装了 optionPane 的 audioActionMap。
播放音频的位置在 ui 的 propertyChangeListener 中对其祖先属性的更改:
if ("ancestor" == e.getPropertyName()) {
JOptionPane op = (JOptionPane)e.getSource();
boolean isComingUp;
// if the old value is null, then the JOptionPane is being
// created since it didn't previously have an ancestor.
if (e.getOldValue() == null) {
isComingUp = true;
} else {
isComingUp = false;
}
// figure out what to do based on the message type
switch (op.getMessageType()) {
case JOptionPane.PLAIN_MESSAGE:
if (isComingUp) {
BasicLookAndFeel.playSound(optionPane,
"OptionPane.informationSound");
}
break;
// all other message types handled as well
}
共享的 actionMap 已安装(懒惰,所以 optionPane 必须已经可见一次)
assertTrue(UIManager.get("AuditoryCues.actionMap") instanceof ActionMap);
ActionMap map = (ActionMap) UIManager.get("AuditoryCues.actionMap");
assertNotNull(map.get("OptionPane.errorSound"));
在操作系统(win 7)级别启用声音并打开硬件声音(仅用于测试)... WTF:但没有任何反应(并且假设被证明是错误的;-)
调试会话(我讨厌它......但偶尔......)结果表明执行 audioAction 不会发生,这里是涉及的方法:
static void playSound(JComponent c, Object actionKey) {
LookAndFeel laf = UIManager.getLookAndFeel();
if (laf instanceof BasicLookAndFeel) {
ActionMap map = c.getActionMap();
if (map != null) {
Action audioAction = map.get(actionKey);
if (audioAction != null) {
// pass off firing the Action to a utility method
// JW: we have an audioAction, so on to the next method
((BasicLookAndFeel)laf).playSound(audioAction);
}
}
}
}
protected void playSound(Action audioAction) {
if (audioAction != null) {
Object[] audioStrings = (Object[])
UIManager.get("AuditoryCues.playList");
if (audioStrings != null) {
// JW: here the action is performed ... except we don't reach this
....
}
}
这相当惊人,不是吗?毕竟,动作是创建的,如果没有播放列表,为什么要创建它们?
问题来了:用于创建操作的列表是不同的列表
// in BasicLookAndFeel
protected ActionMap getAudioActionMap() {
ActionMap audioActionMap = (ActionMap)UIManager.get(
"AuditoryCues.actionMap");
if (audioActionMap == null) {
// here it's named cueList
Object[] acList = (Object[])UIManager.get("AuditoryCues.cueList");
}
不同列表的原因是......允许 LAF 自定义实际要播放的声音
// BasicLookAndFeel
// *** Auditory Feedback
"AuditoryCues.cueList", allAuditoryCues,
// this key defines which of the various cues to render.
// L&Fs that want auditory feedback NEED to override playList.
"AuditoryCues.playList", null,
Ooookaaayy .. 所以让我们看看具体的 LAF 在做什么,fi Win:
// *** Auditory Feedback
// this key defines which of the various cues to render
// Overridden from BasicL&F. This L&F should play all sounds
// all the time. The infrastructure decides what to play.
// This is disabled until sound bugs can be resolved.
"AuditoryCues.playList", null, // table.get("AuditoryCues.cueList"),
停产。
不完全是:-) 这个评论暗示了什么是可行的:
Object[] cueList = (Object[]) UIManager.get("AuditoryCues.cueList");
UIManager.put("AuditoryCues.playList", cueList);
这实际上适用于 WindowsLAF(即使尊重操作系统声音模式,并且 - 最重要的是 -如果禁用则不会播放),但不适用于任何其他核心 LAF。
使用 MadProgrammer 提供的链接(最后重新发布)作为起点,这就是我想出的:
import java.awt.*;
import javax.swing.JOptionPane;
//retrieve the default sound from windows system sounds
//for another sound replace "default" accordingly
final Runnable SOUND = (Runnable)Toolkit.getDefaultToolkit().getDesktopProperty
("win.sound.default");
然后在显示 JOptionPane 之前:
if(SOUND != null)SOUND.run();
注意某些声音事件(例如 Program Error)无法通过这种方式访问。Oracle的Windows 桌面属性支持页面上的音频反馈标题下提供了可访问的声音事件列表
虽然这在非 Windows 操作系统上根本不起作用,但根据博客,它不会导致程序在另一个操作系统上崩溃。我的JDK
Linux 分区还没有,所以我目前无法测试这个。