android 按键声音
2012-04-27 11:17
686 查看
1.Android的audio流的类型有以下12种:
/* The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = 0;//通话连接时的音频流(通话声)
/* The audio stream for system sounds */
public static final int STREAM_SYSTEM = 1;//系统音频流
/* The audio stream for the phone ring and message alerts */
public static final int STREAM_RING = 2;//来电铃声
/* The audio stream for music playback */
public static final int STREAM_MUSIC = 3;//媒体音频流
/* The audio stream for alarms */
public static final int STREAM_ALARM = 4;//闹钟音频流
/* The audio stream for notifications */
public static final int STREAM_NOTIFICATION = 5;//通知音频流
/* @hide The audio stream for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;//从注释上看时使用蓝牙耳机通话的音频流
/* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = 7;//一些国家强制使用的音频流??不太明白
/* @hide The audio stream for DTMF tones */
public static final int STREAM_DTMF = 8;//DTMF音频流
/* @hide The audio stream for text to speech (TTS) */
public static final int STREAM_TTS = 9;//TTS: Text to Speech:文件到语言的音频流,即机器说话
/* @hide The audio stream for Fm */
public static final int STREAM_FM = 10;//FM的音频流
/* @hide The audio stream for MATV */
public static final int STREAM_MATV = 11;//TV的音频流
每种音频流所规定的最大值:
/** @hide Maximum volume index values for audio streams */
private int[] MAX_STREAM_VOLUME = new int[] {
6, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
12, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15, // STREAM_TTS
13, //STREAM_FM
13 //stream_MATV
};
2.所有的按键事件都是touch事件,这部分我会另外开篇博文介绍。
开始本文正文,Anndroid系统中所有View带有按键音,用户可以通过Settings>Sound>勾选Audible Selection即可开启按键音。但是有个奇怪的地方:此按键音是与媒体音量(即STREAM_MUSIC)绑定的,难道按键音的STREAM TYPE就是STREAM_MUSIC吗?我们从代码中寻找一下。
首先所有的View点击的时候都有按键音,我们从View.java的点击事件找起,在view的响应的onTouchEvent()方法中有如下代码:
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();//这里响应click事件
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
mPrivateFlags |= PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
从这里可以看到与用户接口onClickListener结合起来了,当用户注册了clickListener,则调用发出按键音函数playSoundEffect ()和响应用户写好的clickListener的onClick()方法。这里playSoundEffect函数传的参数SoundEffectContants.CLICK为多少呢,从SoundEffectConstants.java可知SoundEffectConstants.CLICK
= 0:
public class SoundEffectConstants
{
SoundEffectConstants() { throw new RuntimeException("Stub!"); }
public static int getContantForFocusDirection(int direction) { throw new RuntimeException("Stub!"); }
public static final int CLICK = 0;
public static final int NAVIGATION_LEFT = 1;
public static final int NAVIGATION_UP = 2;
public static final int NAVIGATION_RIGHT = 3;
public static final int NAVIGATION_DOWN = 4;
}
playSoundEffect
()的具体内容如下:
public void playSoundEffect(int soundConstant) {
if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
return;
}
mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
}
真正调用的是AttachInfo
Callbacks接口的playSoundEffect()函数:
/**
* A set of information given to a view when it is attached to its parent
* window.
*/
static class AttachInfo {
interface Callbacks {
void playSoundEffect(int effectId);
boolean performHapticFeedback(int effectId, boolean always);
}
看注释可知其真正的方法写在parent
window中,那parent window是哪个呢?ViewRoot的实现该回调接口:
public final class ViewRoot extends Handler implements ViewParent,
View.AttachInfo.Callbacks {
具体的playSoundEffect()函数内容:
public void playSoundEffect(int effectId) {
checkThread();
try {
final AudioManager audioManager = getAudioManager();
switch (effectId) {
case SoundEffectConstants.CLICK:
audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
return;
case SoundEffectConstants.NAVIGATION_DOWN:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
return;
case SoundEffectConstants.NAVIGATION_LEFT:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
return;
case SoundEffectConstants.NAVIGATION_RIGHT:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
return;
case SoundEffectConstants.NAVIGATION_UP:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
return;
default:
throw new IllegalArgumentException("unknown effect id " + effectId +
" not defined in " + SoundEffectConstants.class.getCanonicalName());
}
} catch (IllegalStateException e) {
// Exception thrown by getAudioManager() when mView is null
Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
e.printStackTrace();
}
}
我们传入的参数为SoundEffectContants.CLICK,调用AudioManager的playSoundEffect()方法,参数为AudioManger.FX_KEY_CLICK,继续往下看,在AudioManager.java中playSoundEffect()方法:
public void playSoundEffect(int effectType) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
if (!querySoundEffectsEnabled()) {
return;
}
IAudioService service = getService();
try {
service.playSoundEffect(effectType);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in playSoundEffect"+e);
}
}
调用了IAudioService的playSoundEffect() 方法,IAudioService方法是使用aidl生成的接口,aidl源文件:frameworks/base/media/java/android/media/IAudioService.aidl,真正响应的地方在AudioService.java中:
/** @see AudioManager#playSoundEffect(int) */
public void playSoundEffect(int effectType) {
sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
effectType, -1, null, 0);
}
该方法调用sendMsg()方法,传入一下参数:(mAudioHandler, 7, -1, 1, 0, -1, null, 0),sendMsg()方法如下:
private static void sendMsg(Handler handler, int baseMsg, int streamType,
int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
if (existingMsgPolicy == SENDMSG_REPLACE) {
handler.removeMessages(msg);
} else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
Log.d(TAG, "sendMsg: Msg " + msg + " existed!");
return;
}
handler
.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
}
该方法就是将传入的参数经过计算,obtain一个message,message的what
= 7 arg1 = 0 arg2 = -1 object = null; 处理该消息的地方handleMessage ():
@Override
public void handleMessage(Message msg) {
int baseMsgWhat = getMsgBase(msg.what);
switch (baseMsgWhat) {
...
case MSG_PLAY_SOUND_EFFECT:
playSoundEffect(msg.arg1, msg.arg2);
break;
...
}
调用了带两个参数的playSoundEffect()函数,传入参数
0,-1:
private void playSoundEffect(int effectType, int volume) {
synchronized (mSoundEffectsLock) {
if (mSoundPool == null) {
return;
}
float volFloat;
// use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller
if (volume < 0) {
//以下计算播放的音量大小:
// Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp)
float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10;
float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
volFloat = (float)Math.pow(10, (musicVoldB - 3)/20);
} else {
volFloat = (float) volume / 1000.0f;
}
if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);//调用该函数播放按键音
} else {
MediaPlayer mediaPlayer = new MediaPlayer();
if (mediaPlayer != null) {
try {
String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
mediaPlayer.setDataSource(filePath);
mediaPlayer.setAudioStreamType(AudioSystem.STREAM_RING);
mediaPlayer.prepare();
mediaPlayer.setVolume(volFloat, volFloat);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
cleanupPlayer(mp);
}
});
mediaPlayer.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
cleanupPlayer(mp);
return true;
}
});
mediaPlayer.start();
} catch (IOException ex) {
Log.w(TAG, "MediaPlayer IOException: "+ex);
} catch (IllegalArgumentException ex) {
Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
} catch (IllegalStateException ex) {
Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
}
}
}
}
}
因为传入的参数volume为-1,按键音大小的值走if(volume
< 0)内,函数中此部分计算的是按键音的大小volFloat,可以看出,整个计算过程都跟媒体音量STREAM_MUSIC有关,这里就看出,按键音的音量大小是与STREAM_MUSIC绑定的,那按键音的类型呢?继续看下去,函数 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);中使用了SoundPool来播放按键音,我们看看该SoundPool初始化步骤,在AudioService初始化时,调用了loadSoundEffects()函数:
public AudioService(Context context) {
......
loadSoundEffects();
.......
}
loadSoundEffects()函数的具体实现如下:
public boolean loadSoundEffects() {
synchronized (mSoundEffectsLock) {
if (mSoundPool != null) {
return true;
}
mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
......
return true;
}
在此函数中,初始化了该SoundPool,类型为STREAM_SYSTEM。
到这里,结果出来了,android中,view的按键音类型为系统音频(STREAM_SYSTEM),而音量的大小与媒体音量(STREAM_MUSIC)绑定了起来。
/* The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = 0;//通话连接时的音频流(通话声)
/* The audio stream for system sounds */
public static final int STREAM_SYSTEM = 1;//系统音频流
/* The audio stream for the phone ring and message alerts */
public static final int STREAM_RING = 2;//来电铃声
/* The audio stream for music playback */
public static final int STREAM_MUSIC = 3;//媒体音频流
/* The audio stream for alarms */
public static final int STREAM_ALARM = 4;//闹钟音频流
/* The audio stream for notifications */
public static final int STREAM_NOTIFICATION = 5;//通知音频流
/* @hide The audio stream for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;//从注释上看时使用蓝牙耳机通话的音频流
/* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = 7;//一些国家强制使用的音频流??不太明白
/* @hide The audio stream for DTMF tones */
public static final int STREAM_DTMF = 8;//DTMF音频流
/* @hide The audio stream for text to speech (TTS) */
public static final int STREAM_TTS = 9;//TTS: Text to Speech:文件到语言的音频流,即机器说话
/* @hide The audio stream for Fm */
public static final int STREAM_FM = 10;//FM的音频流
/* @hide The audio stream for MATV */
public static final int STREAM_MATV = 11;//TV的音频流
每种音频流所规定的最大值:
/** @hide Maximum volume index values for audio streams */
private int[] MAX_STREAM_VOLUME = new int[] {
6, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
12, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15, // STREAM_TTS
13, //STREAM_FM
13 //stream_MATV
};
2.所有的按键事件都是touch事件,这部分我会另外开篇博文介绍。
开始本文正文,Anndroid系统中所有View带有按键音,用户可以通过Settings>Sound>勾选Audible Selection即可开启按键音。但是有个奇怪的地方:此按键音是与媒体音量(即STREAM_MUSIC)绑定的,难道按键音的STREAM TYPE就是STREAM_MUSIC吗?我们从代码中寻找一下。
首先所有的View点击的时候都有按键音,我们从View.java的点击事件找起,在view的响应的onTouchEvent()方法中有如下代码:
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();//这里响应click事件
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
mPrivateFlags |= PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
从这里可以看到与用户接口onClickListener结合起来了,当用户注册了clickListener,则调用发出按键音函数playSoundEffect ()和响应用户写好的clickListener的onClick()方法。这里playSoundEffect函数传的参数SoundEffectContants.CLICK为多少呢,从SoundEffectConstants.java可知SoundEffectConstants.CLICK
= 0:
public class SoundEffectConstants
{
SoundEffectConstants() { throw new RuntimeException("Stub!"); }
public static int getContantForFocusDirection(int direction) { throw new RuntimeException("Stub!"); }
public static final int CLICK = 0;
public static final int NAVIGATION_LEFT = 1;
public static final int NAVIGATION_UP = 2;
public static final int NAVIGATION_RIGHT = 3;
public static final int NAVIGATION_DOWN = 4;
}
playSoundEffect
()的具体内容如下:
public void playSoundEffect(int soundConstant) {
if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
return;
}
mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
}
真正调用的是AttachInfo
Callbacks接口的playSoundEffect()函数:
/**
* A set of information given to a view when it is attached to its parent
* window.
*/
static class AttachInfo {
interface Callbacks {
void playSoundEffect(int effectId);
boolean performHapticFeedback(int effectId, boolean always);
}
看注释可知其真正的方法写在parent
window中,那parent window是哪个呢?ViewRoot的实现该回调接口:
public final class ViewRoot extends Handler implements ViewParent,
View.AttachInfo.Callbacks {
具体的playSoundEffect()函数内容:
public void playSoundEffect(int effectId) {
checkThread();
try {
final AudioManager audioManager = getAudioManager();
switch (effectId) {
case SoundEffectConstants.CLICK:
audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
return;
case SoundEffectConstants.NAVIGATION_DOWN:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
return;
case SoundEffectConstants.NAVIGATION_LEFT:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
return;
case SoundEffectConstants.NAVIGATION_RIGHT:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
return;
case SoundEffectConstants.NAVIGATION_UP:
audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
return;
default:
throw new IllegalArgumentException("unknown effect id " + effectId +
" not defined in " + SoundEffectConstants.class.getCanonicalName());
}
} catch (IllegalStateException e) {
// Exception thrown by getAudioManager() when mView is null
Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);
e.printStackTrace();
}
}
我们传入的参数为SoundEffectContants.CLICK,调用AudioManager的playSoundEffect()方法,参数为AudioManger.FX_KEY_CLICK,继续往下看,在AudioManager.java中playSoundEffect()方法:
public void playSoundEffect(int effectType) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
if (!querySoundEffectsEnabled()) {
return;
}
IAudioService service = getService();
try {
service.playSoundEffect(effectType);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in playSoundEffect"+e);
}
}
调用了IAudioService的playSoundEffect() 方法,IAudioService方法是使用aidl生成的接口,aidl源文件:frameworks/base/media/java/android/media/IAudioService.aidl,真正响应的地方在AudioService.java中:
/** @see AudioManager#playSoundEffect(int) */
public void playSoundEffect(int effectType) {
sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
effectType, -1, null, 0);
}
该方法调用sendMsg()方法,传入一下参数:(mAudioHandler, 7, -1, 1, 0, -1, null, 0),sendMsg()方法如下:
private static void sendMsg(Handler handler, int baseMsg, int streamType,
int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
if (existingMsgPolicy == SENDMSG_REPLACE) {
handler.removeMessages(msg);
} else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
Log.d(TAG, "sendMsg: Msg " + msg + " existed!");
return;
}
handler
.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
}
该方法就是将传入的参数经过计算,obtain一个message,message的what
= 7 arg1 = 0 arg2 = -1 object = null; 处理该消息的地方handleMessage ():
@Override
public void handleMessage(Message msg) {
int baseMsgWhat = getMsgBase(msg.what);
switch (baseMsgWhat) {
...
case MSG_PLAY_SOUND_EFFECT:
playSoundEffect(msg.arg1, msg.arg2);
break;
...
}
调用了带两个参数的playSoundEffect()函数,传入参数
0,-1:
private void playSoundEffect(int effectType, int volume) {
synchronized (mSoundEffectsLock) {
if (mSoundPool == null) {
return;
}
float volFloat;
// use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller
if (volume < 0) {
//以下计算播放的音量大小:
// Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp)
float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10;
float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
volFloat = (float)Math.pow(10, (musicVoldB - 3)/20);
} else {
volFloat = (float) volume / 1000.0f;
}
if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);//调用该函数播放按键音
} else {
MediaPlayer mediaPlayer = new MediaPlayer();
if (mediaPlayer != null) {
try {
String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
mediaPlayer.setDataSource(filePath);
mediaPlayer.setAudioStreamType(AudioSystem.STREAM_RING);
mediaPlayer.prepare();
mediaPlayer.setVolume(volFloat, volFloat);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
cleanupPlayer(mp);
}
});
mediaPlayer.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
cleanupPlayer(mp);
return true;
}
});
mediaPlayer.start();
} catch (IOException ex) {
Log.w(TAG, "MediaPlayer IOException: "+ex);
} catch (IllegalArgumentException ex) {
Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
} catch (IllegalStateException ex) {
Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
}
}
}
}
}
因为传入的参数volume为-1,按键音大小的值走if(volume
< 0)内,函数中此部分计算的是按键音的大小volFloat,可以看出,整个计算过程都跟媒体音量STREAM_MUSIC有关,这里就看出,按键音的音量大小是与STREAM_MUSIC绑定的,那按键音的类型呢?继续看下去,函数 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);中使用了SoundPool来播放按键音,我们看看该SoundPool初始化步骤,在AudioService初始化时,调用了loadSoundEffects()函数:
public AudioService(Context context) {
......
loadSoundEffects();
.......
}
loadSoundEffects()函数的具体实现如下:
public boolean loadSoundEffects() {
synchronized (mSoundEffectsLock) {
if (mSoundPool != null) {
return true;
}
mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
......
return true;
}
在此函数中,初始化了该SoundPool,类型为STREAM_SYSTEM。
到这里,结果出来了,android中,view的按键音类型为系统音频(STREAM_SYSTEM),而音量的大小与媒体音量(STREAM_MUSIC)绑定了起来。
相关文章推荐
- android学习日记:拨号按键声音自定义
- 实现android按键震动按键声音的机制
- Android如何为按键添加声音
- Android学习:为按键添加声音
- 一看就会Android之使用SoundPool播放多个短小的音频,如按键提示音,消息提示音,游戏声音等
- android应用中为按键添加声音
- Android 设置声音时出现按键音
- android 按键声音
- 简单的Android音乐播放,解决incall类型不能通过音量按键控制声音大小的问题
- android上播放按键声音方法
- android 按键声音
- Android学习:为按键添加声音
- android 按键声音功能实现介绍
- android 按键声音
- Android学习:为按键添加声音
- Android获取长按按键响应
- Android 设备关闭实体按键
- Android 判断是否有声音在播放
- android4.0.3 修改启动动画和开机声音
- Android 按键防止过快点击(防抖),在ButterKnife中的简单解决方案。