关于EditText设置单击提示音方法的探讨
问题来源
在我们手机中,有很多编辑框,但却存在这样一个现象,就是有一些编辑框点击时有点击提示音,但有些却没有。为什么会出现这种情况呢?难道那些有点击提示音的EditText都是自己设置的音效吗?
EditText单击提示音
实验一下,发现当EditText的onClickListener不为空时,即
editText..setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
此时,点击EdiiText是有单击提示音的。
既然如此,那不是很简单,设置editText的onClickListener不就解决问题了?
事实并非如此
仔细测试加验证,会发现一个新的问题,当光标在当前的EditText上时,点击当前EditText,确实有单击提示音,但是,当光标不在当前Editext上时,单击却没有提示音,这又是什么原因呢?
在onClick中打印一行log,发现这样的现象: EditText setOnClickListener事件响应中,只有获取焦点的时候才会响应,
当焦点在别的控件上时,只能先点击获取焦点,第二次点击才会响应。所以才会出现,光标不在eidtText上时,不响应onClick,也就没有单击提示音。
解决方法
既然未获取焦点时,点击不响应onClick,导致没有单击提示音,那么我们可以在相应onClick之前让它取得焦点就可以解决此问题。
eidtText.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
editText.requestFocus();
return false;
}
});
这样设置之后,再次点击,onClick就会一直响应,单击提示音也一直都有。
深究细查
为什么在setOnClickListener之后,EditText就会有单击提示音呢?2. 为什么EditText在没有获取焦点时,不响应onClick呢,没有单击提示音?
带着这样的两个问题,我们来查阅View.java中的代码,你会发现:/** * Call this view's OnClickListener, if it is defined. Performs all normal * actions associated with clicking: reporting accessibility event, playing * a sound, etc. * * @return True there was an assigned OnClickListener that was called, false * otherwise is returned. */ public boolean performClick() { final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result; }
看完这段代码,第一个问题的答案便是一目了然了。
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
}
只有当当前View的mOnClickListener
不为空时,即view.setOnClickListener()
,才会执行 playSoundEffect(SoundEffectConstants.CLICK)
;
这一行就是单击发声的根源。
我们再来看看View.java中onTouchEvent(MotionEvent event)
方法中的一段代码:
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_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 (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
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();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
从这段代码中容易看出,当View未获取焦点时,isFocused()==false
, 点击View时,就会去requesetFocus()
,
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
这样,focusTaken = true
,
此时:
// 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();
}
}
focusTaken = true
,就不会执行performClick()
playSoundEffect和onClick都是在performClick()
中执行的,此时,就不会有单击音效,也不会响应view的onClick方法。
以上两个问题就迎刃而解
总结
通过以上的分析可知,要使EditText点击有提示音,需要这样设置:
editText..setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
eidtText.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
editText.requestFocus();
}
return false;
}
});
既然我们已经知道单击提示音来源是调用了View.java中的方法playSoundEffect(SoundEffectConstants.CLICK);
其在view类中的定义为:
/**
* Play a sound effect for this view.
*
* <p>The framework will play sound effects for some built in actions, such as
* clicking, but you may wish to play these effects in your widget,
* for instance, for internal navigation.
*
* <p>The sound effect will only be played if sound effects are enabled by the user, and
* {@link #isSoundEffectsEnabled()} is true.
*
* @param soundConstant One of the constants defined in {@link SoundEffectConstants}
*/
public void playSoundEffect(int soundConstant) {
if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
return;
}
mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
}
其方法是公开的,因此我们也可以这样设置EditText的单击提示音:
editText.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
editText.playSoundEffect(SoundEffectConstants.CLICK);
}
return false;
}
});
执行mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
的前提是isSoundEffectsEnabled() = true
,那么它何时是false呢
查阅相关资料得知:
系统设置中若关闭了选择操作音(选取控件时播放音效),则android:soundEffectsEnabled属性无意义,均不发声;
而如果开启了设置中的选择操作音,则即使没有设置view的该属性,view.playSoundEffect(soundEffectConstants)依然会有单击提示音,除非设置该属性为false时,或者在代码中设view.setSoundEffectsEnabled(false)
,
view才不会出现按键提示音。
所以,默认不设置,就有单击提示音。
以上就是在开发对遇到的一个小问题的总结,比较浅显易懂,分享给大家,再碰到类似的问题就可以少走弯路,提升工作效率。