这两天在开发一个功能的时候,发现项目的UX要求EditText点击出来一个AlterDialog。在一顿setOnClickListener操作之后,我发现了一个小问题。在点击的时候,需要点第二次才会show出来这个Dialog。原因是EditText这个控件需要在第一次点击的时候获取焦点,第二次点击才触发OnClick事件。非常合理,因为EditText的作用是为了输入文字,所以需要先获取焦点。

查了下资料,发现改为onTouchListener就可以解决问题,只不过这里需要注意下OnTouch会调用两次,在手指按下和释放的时候。因此,需要添加一个flag来消除这个操作。

        view.setOnTouchListener(new View.OnTouchListener() {
            int touch_flag = 0;

            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                touch_flag++;
                if (touch_flag == 2) {
                    touch_flag = 0;

                    final Dialog dialog = new Dialog();
                    dialog.show();
                }
                return false;
            }
        });

看似没有问题,实际上在编码完成后,我在测试的时候发现,有一个小毛病。如果你的EditText在一个可以滑动的RecylerView里面,那么当你滑动的时候,仍然会触发它的OnTouch事件。这当然是不对的,所以搜索了一下,找到了EditText有这样一个属性。

android:focusableInTouchMode="false"

添加完这个属性,直接就可以和TextView一样,调用onClickListener就能达到预期了。从字面上面就知道
在TouchModel中关闭掉焦点。那么TouchModel又是什么呢?参照Google Android Blog的这篇文章

这就要说起来Android的起源了,因为是移动端的平台,不像PC端,所有的IO就是键盘和鼠标(泛指以前的非触摸的操作系统)。在Android第一个量产机G1上面,就同时存在了三种点击途径,轨迹球,实体按键和用户手指点击。那么就会存在这样一个问题:如果我用手指点击了一个List中的某一个项目,接着我使用轨迹球选择另外一个,这样就会有冲突

为了解决这个问题,Google引入了TouchModel。在用户点击屏幕的时候,系统会切换到TouchModel上面,同时保存轨迹球,实体按键选择的item的位置。当用户再次使用键盘操作的时候,系统会退出TouchModel,并将上一次记录的位置展现并定位下来。

同时,在移动平台的情况下,用户通过手指点击控件,其实是少了一个选中的步骤的。举个例子,想一想PC机在没有鼠标点击操作的情况下面,我们需要通过Tab和上下左右去选中一个控件,然后点击Enter去触发这个控件的事件。想一想,如果在Android上面,需要用户先点击控件,获取焦点之后,在进行相应的Action,是非常不合理的。因此,在Android上面,你点击Button的时候,默认其实人眼已经做了焦点的聚焦,真正需要做的就是执行点击事件的内容。

但是仍然是需要有例外的,例如EditText,用户点击到EditText首先是需要获取到焦点,完了之后才能弹出键盘,然后让用户输入文字。在这样的情况下面,Android给View的focusableInTouchMode属性就立功了。

回到上面的问题,当我把focusableInTouchMode设置成false后,那么用户在点击EditText的时候,自然不会去获取焦点,从而直接可以执行我们的onClick里面的事件了。