关于EditText设置单击提示音方法的探讨

1 篇文章 0 订阅
1 篇文章 0 订阅

关于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就会一直响应,单击提示音也一直都有。

深究细查

  1. 为什么在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才不会出现按键提示音。
所以,默认不设置,就有单击提示音。

以上就是在开发对遇到的一个小问题的总结,比较浅显易懂,分享给大家,再碰到类似的问题就可以少走弯路,提升工作效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值