Time Options Response to Android Soft Keyboard Event

Keywords: Android Attribute xml Google

Thank you for sharing:

Reproduced from: http://blog.csdn.net/liuweiballack/article/details/46708697


In the process of android development, sometimes it is necessary to monitor EditText's soft keyboard.  
When clicking on the return position button of the soft keyboard, it is necessary to achieve completion, forward, next item, search, send or other functions, which requires developers to capture the click event of the return of the soft keyboard.  
For example, in the login interface, users need to click on the soft keyboard after entering the password to log in directly without clicking on the login button on the screen. We can set android:imeOptions = "actionDone" in the EditText used in password, and then set OnEditorActionListener to listen on EditText, and call the login method when the user clicks on it. (IME English full name Input Method Editors, Chinese Name Input Method Editor)

Look at a demo first.
The layout file is as follows:

<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:  
When using the android:imeOptions attribute, be sure to set android:inputType for EditText or android:singleline= "true".

In the activity_main.xml file, eight EditText s are defined, imeOptions are:
actionDone completes the corresponding EditorInfo.IME_ACTION_DONE
actionGo advances correspond to EditorInfo.IME_ACTION_GO
The next item under actionNext corresponds to EditorInfo.IME_ACTION_NEXT
actionNone has no action corresponding to EditorInfo.IME_ACTION_NONE
An item on action Previous corresponds to EditorInfo.IME_ACTION_PREVIOUS
actionSearch Search Search Search Corresponds to EditorInfo.IME_ACTION_SEARCH
Action Unspecified does not specify the corresponding EditorInfo.IME_ACTION_UNSPECIFIED
actionSend sends the corresponding EditorInfo.IME_ACTION_SEND

In 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

In the onEditor Action method, we first use doWhichOperation(actionId) to determine what operation to click on, and then print the relevant operation information.

        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

In Android source code, common IME constants are defined for EditorInfo:

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

The following are the different effects of the soft keyboard return key when setting different android:imeOptions and the printing information of the system after clicking.
1. android:imeOptions="actionDone" 
 
The bottom right corner of the picture is displayed.
 
In the Log information, you can see that EditorInfo.IME_ACTION_DONE, v.getImeOptions()=6 are matched by actionId
actionId and EditorInfo.IME_ACTION_DONE can correspond one by one.  
2. android:imeOptions="actionGo" 
 
The bottom right corner of the picture shows "Go to"
 
In the Log information, you can see that EditorInfo.IME_ACTION_GO, v.getImeOptions()=2 are matched by actionId.
actionId and EditorInfo.IME_ACTION_GO can correspond one by one.  
3. android:imeOptions="actionNext" 
 
Show the next item in the bottom right corner of the picture.
 
In the Log information, you can see that EditorInfo.IME_ACTION_NEXT, v.getImeOptions()=5 are matched by actionId.
actionId and EditorInfo.IME_ACTION_NEXT can correspond one by one.  
4. android:imeOptions="actionPrevious" 
 
The bottom right corner of the picture shows the previous item.
 
In Log information, you can see that EditorInfo.IME_ACTION_PREVIOUS, v.getImeOptions()=7, actionId and EditorInfo.IME_ACTION_PREVIOUS can be matched one by one.  
5. android:imeOptions="actionSearch" 
 
Show "Search" in the bottom right corner of the picture
 
In Log information, you can see that EditorInfo.IME_ACTION_SEARCH, v.getImeOptions()=3, actionId and EditorInfo.IME_ACTION_SEARCH can be matched one by one.  
6. android:imeOptions="actionSend" 
 
Show "Send" in the lower right corner of the picture
 
In Log information, you can see that EditorInfo.IME_ACTION_SEND, v.getImeOptions()=4, actionId and EditorInfo.IME_ACTION_SEND can be matched one by one.  
7. android:imeOptions="actionUnspecified" 
 
Show the next item in the bottom right corner of the picture.
 
In the Log information, you can see that EditorInfo.IME_ACTION_NEXT, v.getImeOptions()=0, actionId and EditorInfo.IME_ACTION_UNSPECIFIED cannot be matched by actionId.  
8. android:imeOptions="actionNone" 
 
The lower right corner of the picture shows "Enter the train".

In the Log information, you can see that EditorInfo.IME_ACTION_UNSPECIFIED, v.getImeOptions()=1, actionId and EditorInfo.IME_ACTION_NONE can not be matched by actionId, and there are two repeated logs.

In the eight different effects mentioned above, there are erroneous messages in android:imeOptions = "actionUnspecified" and android:imeOptions = "actionNone", which cannot correspond to the values in EditorInfo. The values obtained by v.getImeOptions() can correspond to the constants in EditorInfo one by one.

Attention Two:  
actionId is the value captured by the system when the user clicks on the keyboard, while v.getImeOptions() gets the value of the android:imeOptions attribute set to EditText in the xml file, which is why v.getImeOptions() and EditorInfo can always correspond one by one.

In source code, IME_ACTION_UNSPECIFIED is defined as:

 /**
   * 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

There is no specific action to be specified, which is specified by the editor itself. When EditText is in a different position, the system automatically determines what action the current EditText may need to perform. When the EditText designated as action Unspecified is placed at the end, it will be displayed as "completed" and the printed information will be IME_ACTION_DONE.

Similarly, IME_ACTION_NONE is the same:

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

EditText designated as actionDone does not specify any action, and it will be displayed by default.

In the source code of TextView, the interface OnEditoractionListener is defined as follows:

/**
  * 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

If actionId is not specifically specified, it will be defaulted to IME_NULL, and then look at the EditorInfo source code to find that:

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

In EditorInfo, two constants IME_NULL and IME_ACTION_UNSPECIFIED have values of 0x00000000.  
android:imeOptions= "actionNone" printed information appears IME_ACTION_UNSPECIFIED, in fact, because there is no action, it should be IME_NULL, only in the MainActivity code when actionId=0, uniformly designated as IME_ACTION_UNSPECIFIED.

Attention Three:  
Some third-party input methods support EditorInfo differently, some functions are implemented, but the corresponding icon has not been modified, some simply functions are not implemented. For example, Google's own input method does not support actionPrevious's icons and functions. In the millet version of Baidu's input method, actionPrevious functions are implemented, but the icon still shows the return icon.

After specifying different imeOptions for EditText, you need to implement the onEditorAction() method in OnEditorActionListener and then respond to different actions.  
For actionDone, actionNext and actionPrevious, the system has partially processed itself.  
- actionDone: Hidden Input Method
- Action Next: Jump to the next EditText
- Action Previous: Jump to the last EditText

However, it doesn't make any difference!!!  
The reason is that in the method onEditorAction() of TextView source code:

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

The system will first determine the return value of the user-implemented method ict.onEditorActionListener.onEditorAction(this, actionCode, null). Once it returns true, it will return immediately, so the system's processing is skipped directly.  
If you want to implement some functions by yourself, and then other basic operations are done by the system, then the implementation method onEditorAction () returns false.

Posted by dagee on Fri, 17 May 2019 22:29:18 -0700