Android 软键盘事件imeOptions响应

在android发开过程中,有时候需要对EditText的软键盘进行监听。 
当点击软键盘回车位置按键的时候,需要实现 完成、前进、下一项、搜索、发送或其他功能,这就需要开发者对软键盘回车的点击事件进行捕捉。 
比如在登录界面,需要用户在输入密码之后点击软键盘回车直接登录,不必再去点击屏幕上的登录按钮。我们就可以在密码使用的EditText设置 android:imeOptions=”actionDone”,然后对EditText设置OnEditorActionListener监听,当捕捉到用户点击完成时,调用登录方法即可。(IME英文全称Input Method Editors,中文名称输入法编辑器

先看一个demo 
布局文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical">


            <EditText
                android:id="@+id/actionDoneEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionDone"
                android:hint="actionDone" />

            <EditText
                android:id="@+id/actionGoEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionGo"
                android:hint="actionGo" />

            <EditText
                android:id="@+id/actionNextEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionNext"
                android:hint="actionNext" />

            <EditText
                android:id="@+id/actionNoneEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionNone"
                android:hint="actionNone" />

            <EditText
                android:id="@+id/actionPreviousEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionPrevious"
                android:hint="actionPrevious" />

            <EditText
                android:id="@+id/actionSearchEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionSearch"
                android:hint="actionSearch" />

            <EditText
                android:id="@+id/actionUnspecifiedEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionUnspecified"
                android:hint="actionUnspecified" />

            <EditText
                android:id="@+id/actionSendEditText"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
                android:inputType="text"
                android:imeOptions="actionSend"
                android:hint="actionSend" />


        </LinearLayout>
    </ScrollView>
</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

Attention One: 
使用android:imeOptions属性的时候,一定要对EditText设置 android:inputType 或者 设置 android:singleline=”true”。

在activity_main.xml文件中,定义了8个EditText,imeOptions分别是: 
actionDone 完成 对应 EditorInfo.IME_ACTION_DONE 
actionGo 前进 对应 EditorInfo.IME_ACTION_GO 
actionNext 下一项 对应 EditorInfo.IME_ACTION_NEXT 
actionNone 无动作 对应 EditorInfo.IME_ACTION_NONE 
actionPrevious 上一项 对应 EditorInfo.IME_ACTION_PREVIOUS 
actionSearch 搜索 对应 EditorInfo.IME_ACTION_SEARCH 
actionUnspecified 未指定 对应 EditorInfo.IME_ACTION_UNSPECIFIED 
actionSend 发送 对应 EditorInfo.IME_ACTION_SEND

在MainActivity中:

public class MainActivity extends Activity implements TextView.OnEditorActionListener {

    private EditText mActionDoneEditText;
    private EditText mActionGoEditText;
    private EditText mActionNextEditText;
    private EditText mActionNoneEditText;
    private EditText mActionPreviousEditText;
    private EditText mActionSearchEditText;
    private EditText mActionSendEditText;
    private EditText mActionUnspecifiedEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        mActionDoneEditText = (EditText) findViewById(R.id.actionDoneEditText);
        mActionGoEditText = (EditText) findViewById(R.id.actionGoEditText);
        mActionNextEditText = (EditText) findViewById(R.id.actionNextEditText);
        mActionNoneEditText = (EditText) findViewById(R.id.actionNoneEditText);
        mActionPreviousEditText = (EditText) findViewById(R.id.actionPreviousEditText);
        mActionSearchEditText = (EditText) findViewById(R.id.actionSearchEditText);
        mActionSendEditText = (EditText) findViewById(R.id.actionSendEditText);
        mActionUnspecifiedEditText = (EditText) findViewById(R.id.actionUnspecifiedEditText);

        mActionDoneEditText.setOnEditorActionListener(this);
        mActionGoEditText.setOnEditorActionListener(this);
        mActionNextEditText.setOnEditorActionListener(this);
        mActionNoneEditText.setOnEditorActionListener(this);
        mActionPreviousEditText.setOnEditorActionListener(this);
        mActionSearchEditText.setOnEditorActionListener(this);
        mActionSendEditText.setOnEditorActionListener(this);
        mActionUnspecifiedEditText.setOnEditorActionListener(this);
    }

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        doWhichOperation(actionId);
        Log.e("BALLACK", "event: " + event);
        Log.e("BALLACK", "v.getImeActionId(): " + v.getImeActionId());
        Log.e("BALLACK", "v.getImeOptions(): " + v.getImeOptions());
        Log.e("BALLACK", "----------------------------------------------");
        return true;
    }

    private void doWhichOperation(int actionId) {
        switch (actionId) {
            case EditorInfo.IME_ACTION_DONE:
                Log.e("BALLACK", "IME_ACTION_DONE");
                break;
            case EditorInfo.IME_ACTION_GO:
                Log.e("BALLACK", "IME_ACTION_GO");
                break;
            case EditorInfo.IME_ACTION_NEXT:
                Log.e("BALLACK", "IME_ACTION_NEXT");
                break;
            case EditorInfo.IME_ACTION_NONE:
                Log.e("BALLACK", "IME_ACTION_NONE");
                break;
            case EditorInfo.IME_ACTION_PREVIOUS:
                Log.e("BALLACK", "IME_ACTION_PREVIOUS");
                break;
            case EditorInfo.IME_ACTION_SEARCH:
                Log.e("BALLACK", "IME_ACTION_SEARCH");
                break;
            case EditorInfo.IME_ACTION_SEND:
                Log.e("BALLACK", "IME_ACTION_SEND");
                break;
            case EditorInfo.IME_ACTION_UNSPECIFIED:
                Log.e("BALLACK", "IME_ACTION_UNSPECIFIED");
                break;
            default:
                break;
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

在 onEditorAction方法中先通过doWhichOperation(actionId)判断点击的是什么操作,然后打印相关操作信息

        Log.e("BALLACK", "event: " + event);
        Log.e("BALLACK", "v.getImeActionId(): " + v.getImeActionId());
        Log.e("BALLACK", "v.getImeOptions(): " + v.getImeOptions());
        Log.e("BALLACK", "----------------------------------------------");
  • 1
  • 2
  • 3
  • 4

Android源码中,对EditorInfo定义了常用的IME常量值:

public static final int IME_ACTION_UNSPECIFIED = 0x00000000;
public static final int IME_ACTION_NONE = 0x00000001;
public static final int IME_ACTION_GO = 0x00000002;
public static final int IME_ACTION_SEARCH = 0x00000003;
public static final int IME_ACTION_SEND = 0x00000004;
public static final int IME_ACTION_NEXT = 0x00000005;
public static final int IME_ACTION_DONE = 0x00000006;
public static final int IME_ACTION_PREVIOUS = 0x00000007;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

下面是设置不同android:imeOptions时软键盘回车键显示的不同效果和点击之后系统的打印信息 
1. android:imeOptions=”actionDone” 
这里写图片描述 
图片右下角显示”完成 
这里写图片描述 
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_DONE,v.getImeOptions()=6 
actionId和 EditorInfo.IME_ACTION_DONE 可以一一对应。 
2. android:imeOptions=”actionGo” 
这里写图片描述 
图片右下角显示”前往 
这里写图片描述 
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_GO,v.getImeOptions()=2 
actionId和 EditorInfo.IME_ACTION_GO可以一一对应。 
3. android:imeOptions=”actionNext” 
这里写图片描述 
图片右下角显示”下一项 
这里写图片描述 
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_NEXT,v.getImeOptions()=5 
actionId和 EditorInfo.IME_ACTION_NEXT可以一一对应。 
4. android:imeOptions=”actionPrevious” 
这里写图片描述 
图片右下角显示”上一项 
这里写图片描述 
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_PREVIOUS,v.getImeOptions()=7,actionId和 EditorInfo.IME_ACTION_PREVIOUS可以一一对应。 
5. android:imeOptions=”actionSearch” 
这里写图片描述 
图片右下角显示”搜索 
这里写图片描述 
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_SEARCH,v.getImeOptions()=3,actionId和 EditorInfo.IME_ACTION_SEARCH可以一一对应。 
6. android:imeOptions=”actionSend” 
这里写图片描述 
图片右下角显示”发送 
这里写图片描述 
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_SEND,v.getImeOptions()=4,actionId和 EditorInfo.IME_ACTION_SEND可以一一对应。 
7. android:imeOptions=”actionUnspecified” 
这里写图片描述 
图片右下角显示”下一项 
这里写图片描述 
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_NEXT,v.getImeOptions()=0,actionId和 EditorInfo.IME_ACTION_UNSPECIFIED无法对应上。 
8. android:imeOptions=”actionNone” 
这里写图片描述 
图片右下角显示”回车 
这里写图片描述
在Log信息中,可以看到,通过actionId匹配到EditorInfo.IME_ACTION_UNSPECIFIED,v.getImeOptions()=1,actionId和 EditorInfo.IME_ACTION_NONE无法对应上,而且出现两次重复的Log信息。

在上述出现的8个不同效果上android:imeOptions=”actionUnspecified”和android:imeOptions=”actionNone”出现了错误的信息,无法和EditorInfo中的值对应上,v.getImeOptions()取得的值和EditorInfo中的常量可以一一对应。

Attention Two: 
actionId是系统捕捉到用户点击键盘时取得的数值,而v.getImeOptions()拿到的是在xml文件中对EditText设置的android:imeOptions属性的数值,这就是为什么v.getImeOptions()和EditorInfo永远可以一一对应的原因。

在源码中,对IME_ACTION_UNSPECIFIED定义是:

 /**
   * Bits of {@link #IME_MASK_ACTION}: no specific action has been
   * associated with this editor, let the editor come up with its own if
   * it can.
   */
public static final int IME_ACTION_UNSPECIFIED = 0x00000000;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

没有特别指定要进行什么动作,由编辑器自己指定,当EditText处于不同的位置的时候,系统自动判断当前EditText可能需要执行什么样的动作。当把指定为actionUnspecified的EditText放在最后面的时候,就会显示为”完成“,打印出来的信息也会是 IME_ACTION_DONE。

同理,IME_ACTION_NONE也是这样:

/**
  * Bits of {@link #IME_MASK_ACTION}: there is no available action.
  */
public static final int IME_ACTION_NONE = 0x00000001;
  • 1
  • 2
  • 3
  • 4

指定为actionDone的EditText没有指定任何动作,就会被系统默认显示回车。

在TextView的源码中,对接口OnEditoractionListener的定义如下:

/**
  * Interface definition for a callback to be invoked when an action is
  * performed on the editor.
  */
    public interface OnEditorActionListener {
        /**
         * Called when an action is being performed.
         *
         * @param v The view that was clicked.
         * @param actionId Identifier of the action.  This will be either the
         * identifier you supplied, or {@link EditorInfo#IME_NULL
         * EditorInfo.IME_NULL} if being called due to the enter key
         * being pressed.
         * @param event If triggered by an enter key, this is the event;
         * otherwise, this is null.
         * @return Return true if you have consumed the action, else false.
         */
        boolean onEditorAction(TextView v, int actionId, KeyEvent event);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

如果actionId 没有特别指定的话,就会被默认为IME_NULL,再查看EditorInfo源码发现:

/**
 * Generic unspecified type for {@link #imeOptions}.
 */
public static final int IME_NULL = 0x00000000;
  • 1
  • 2
  • 3
  • 4

在EditorInfo中,有两个常量IME_NULL和IME_ACTION_UNSPECIFIED的值都是0x00000000。 
android:imeOptions=”actionNone”打印出来的信息出现IME_ACTION_UNSPECIFIED,其实由于没有任何动作,应该是IME_NULL,只是在MainActivity代码中对actionId=0时统一指定为了IME_ACTION_UNSPECIFIED。

Attention Three: 
部分第三方的输入法,对EditorInfo支持的不一样,有的功能实现了,但是对应的图标没有修改过来,有的干脆功能就没有实现。比如谷歌自己的输入法,就不支持actionPrevious的图标和功能,而百度输入法的小米专用版中,actionPrevious功能虽然实现了,但是图标仍然显示的是回车的图标。

对EditText指定不同的imeOptions之后,就需要实现OnEditorActionListener 中的onEditorAction()方法,然后根据不同的动作执行进行响应。 
对于actionDone、actionNext和actionPrevious,系统都自己进行了部分处理。 
- actionDone:隐藏输入法 
- actionNext:跳到下一个EditText 
- actionPrevious:跳到上一个EditText

然并卵!!! 
原因在于,在TextView源码的方法onEditorAction()中:

public void onEditorAction(int actionCode) {
        final Editor.InputContentType ict = mEditor == null ? null : mEditor.mInputContentType;
        if (ict != null) {
            if (ict.onEditorActionListener != null) {
                if (ict.onEditorActionListener.onEditorAction(this,
                        actionCode, null)) {
                    return;
                }
            }

            // This is the handling for some default action.
            // Note that for backwards compatibility we don't do this
            // default handling if explicit ime options have not been given,
            // instead turning this into the normal enter key codes that an
            // app may be expecting.
            if (actionCode == EditorInfo.IME_ACTION_NEXT) {
                View v = focusSearch(FOCUS_FORWARD);
                if (v != null) {
                    if (!v.requestFocus(FOCUS_FORWARD)) {
                        throw new IllegalStateException("focus search returned a view " +
                                "that wasn't able to take focus!");
                    }
                }
                return;

            } else if (actionCode == EditorInfo.IME_ACTION_PREVIOUS) {
                View v = focusSearch(FOCUS_BACKWARD);
                if (v != null) {
                    if (!v.requestFocus(FOCUS_BACKWARD)) {
                        throw new IllegalStateException("focus search returned a view " +
                                "that wasn't able to take focus!");
                    }
                }
                return;

            } else if (actionCode == EditorInfo.IME_ACTION_DONE) {
                InputMethodManager imm = InputMethodManager.peekInstance();
                if (imm != null && imm.isActive(this)) {
                    imm.hideSoftInputFromWindow(getWindowToken(), 0);
                }
                return;
            }
        }

        ViewRootImpl viewRootImpl = getViewRootImpl();
        if (viewRootImpl != null) {
            long eventTime = SystemClock.uptimeMillis();
            viewRootImpl.dispatchKeyFromIme(
                    new KeyEvent(eventTime, eventTime,
                    KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                    KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
                    | KeyEvent.FLAG_EDITOR_ACTION));
            viewRootImpl.dispatchKeyFromIme(
                    new KeyEvent(SystemClock.uptimeMillis(), eventTime,
                    KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                    KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
                    | KeyEvent.FLAG_EDITOR_ACTION));
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

系统会首先判断用户实现的方法ict.onEditorActionListener.onEditorAction(this, actionCode, null)的返回值,一旦返回true,会立即return,因此系统的处理被直接跳过。 
如果想自己实现部分功能,然后其他的基本操作由系统完成,那就在实现方法onEditorAction()是返回false。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值