preface

Recently, I encountered a small knowledge point when I changed the bug. I hoped that the Enter key on the keyboard would be changed to the search button in the search interface. The effect shown below is not very common.

Then I remember Editext has an imeOptions property that sets the effect of the Enter key. So write Android :imeOptions=”actionSearch” in the XML and change the problem to fixed, confident. The result is that when the compilation runs, it doesn’t work on the phone, so it’s still Enter. I googled it and found that everyone replied like this:

If the imeOptions attribute is set and the keyboard does not change, then you need to add some other attributes to use it separately.

1 Set singleLine to true

2. Alternatively, set inputType to text

I tried it and it worked. But why is it right to set the display this way? Do you have the same question? So let’s find out!

ImeOptions attribute source code parsing

AndroidSourceView is the first plugin to look at the source code.

Step1

Because if it’s in a JAVA file, we set the imeOptions property code to editText.setimeOptions (editorInfo.ime_action_search); SetImeOptions () is not available in EdiText, so skip to the parent TextView in EditText and find the target:

 /**
     * Change the editor type integer associated with the text view, which
     * is reported to an Input Method Editor (IME) with {@link EditorInfo#imeOptions}
     * when it has focus.
     * @see #getImeOptions
     * @see android.view.inputmethod.EditorInfo
     * @attr ref android.R.styleable#TextView_imeOptions* When the input method compiler (IME) gets focus, the {@link EditorInfo is used#imeOptions} report to the input method editor,* to change the editor type value associated with the text view. */ public voidsetImeOptions(int imeOptions) { //1. Check whether the Editor object createEditorIfNeeded() is created. / / 2. Determine whether you need to create InputContentType mEditor. CreateInputContentTypeIfNeeded (); / / 3. Will the parameter assigned to InputContentType. ImeOptions mEditor. MInputContentType. ImeOptions = imeOptions; }Copy the code

This method is to determine whether need to create some objects, then will we into the assignment to InputContentType refs. ImeOptions. From the comments on this method we know that the key object is EditorInfo#imeOptions.

Step2

Continue searching for the keyword imeOptions and find the following method:

 @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        if (onCheckIsTextEditor() && isEnabled()) {
            mEditor.createInputMethodStateIfNeeded();
            outAttrs.inputType = getInputType();
            if(mEditor.mInputContentType ! = null) { outAttrs.imeOptions = mEditor.mInputContentType.imeOptions; outAttrs.privateImeOptions = mEditor.mInputContentType.privateImeOptions; outAttrs.actionLabel = mEditor.mInputContentType.imeActionLabel; outAttrs.actionId = mEditor.mInputContentType.imeActionId; outAttrs.extras = mEditor.mInputContentType.extras; outAttrs.hintLocales = mEditor.mInputContentType.imeHintLocales; }else {
                outAttrs.imeOptions = EditorInfo.IME_NULL;
                outAttrs.hintLocales = null;
            }
            if(focusSearch(FOCUS_DOWN) ! = null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; }if(focusSearch(FOCUS_UP) ! = null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; }if ((outAttrs.imeOptions & EditorInfo.IME_MASK_ACTION)
                    == EditorInfo.IME_ACTION_UNSPECIFIED) {
                if((outAttrs.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) ! = 0) { // An action has not beenset, but the enter key will move to
                    // the next focus, so set the action to that.
                    outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
                } else {
                    // An action has not been set, and there is no focus to move
                    // to, so let's just supply a "done" action. outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE; } if (! shouldAdvanceFocusOnEnter()) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; } } if (isMultilineInputType(outAttrs.inputType)) { // Multi-line text editors should always show an enter key. outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; } outAttrs.hintText = mHint; if (mText instanceof Editable) { InputConnection ic = new EditableInputConnection(this); outAttrs.initialSelStart = getSelectionStart(); outAttrs.initialSelEnd = getSelectionEnd(); outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType()); return ic; } } return null; }Copy the code

Then we only care about the information we are exploring, so the pseudocode is as follows:

public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
            //  return mEditor == null ? EditorInfo.TYPE_NULL : mEditor.mInputType
            outAttrs.inputType = getInputType();
            if(mEditor.mInputContentType ! = null) { outAttrs.imeOptions = mEditor.mInputContentType.imeOptions; }else {
                outAttrs.imeOptions = EditorInfo.IME_NULL;
            }
            if(focusSearch(FOCUS_DOWN) ! = null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; }if(focusSearch(FOCUS_UP) ! = null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS; }if((outAttrs.imeOptions & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_UNSPECIFIED) { //.. Code omission}if(isMultilineInputType(outattrs.inputType)) {// Multi-line text editors should always show an enter key. // The multi-line text compiler always displays Enter outAttrs. ImeOptions | = EditorInfo. IME_FLAG_NO_ENTER_ACTION; }return null;
    }

Copy the code
Step3

Let’s first analyze:

  • The first if judgment:mEditor.mInputContentTypeIs this value null? The answer is false, because we’re insetImeOptionsStep 3 of the method has already assigned it a value, so it’s not null, sooutAttrs.imeOptionsThe result is the value we set
  • The second if is to find if there is a View in the direction of our Editext object that can get focus. This depends on whether there is an input box in the layout below the actual input box. The project does not have one, so false
  • The third if judgment: as in the second condition, only the direction is up
  • The fourth if judgment: first of all(outAttrs.imeOptions & EditorInfo.IME_MASK_ACTIONWhat is the value? The value that we set in our code is zeroIME_ACTION_SEARCH, the value is 3,EditorInfo.IME_MASK_ACTIONThe binary value is 00000011&1111 1111 = 00000011 and the decimal value is 3EditorInfo.IME_ACTION_UNSPECIFIEDThe value is 0, so the result is false.

So far outattrs. imeOptions results in the same values we set in the code.

Step4

The final if judgment: isMultilineInputType(outattrs.inputType) Take a look at the code:

private static boolean isMultilineInputType(int type) {
        return (type & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE))
                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE);
    }
Copy the code

The value of type is obtained from the onCreateInputConnection() method by getInputType (). The result is to return the value of inputType set in our code, which is not set in our code. So what’s the default value for this inputType? Not found in the source code, then I debug it myself, the result is as follows:

public interface InputType {
    int TYPE_CLASS_DATETIME = 4;
    int TYPE_CLASS_NUMBER = 2;
    int TYPE_CLASS_PHONE = 3;
    int TYPE_CLASS_TEXT = 1;
    int TYPE_DATETIME_VARIATION_DATE = 16;
    int TYPE_DATETIME_VARIATION_NORMAL = 0;
    int TYPE_DATETIME_VARIATION_TIME = 32;
    int TYPE_MASK_CLASS = 15;
    int TYPE_MASK_FLAGS = 16773120;
    int TYPE_MASK_VARIATION = 4080;
    int TYPE_NULL = 0;
    int TYPE_NUMBER_FLAG_DECIMAL = 8192;
    int TYPE_NUMBER_FLAG_SIGNED = 4096;
    int TYPE_NUMBER_VARIATION_NORMAL = 0;
    int TYPE_NUMBER_VARIATION_PASSWORD = 16;
    int TYPE_TEXT_FLAG_AUTO_COMPLETE = 65536;
    int TYPE_TEXT_FLAG_AUTO_CORRECT = 32768;
    int TYPE_TEXT_FLAG_CAP_CHARACTERS = 4096;
    int TYPE_TEXT_FLAG_CAP_SENTENCES = 16384;
    int TYPE_TEXT_FLAG_CAP_WORDS = 8192;
    int TYPE_TEXT_FLAG_IME_MULTI_LINE = 262144;
    int TYPE_TEXT_FLAG_MULTI_LINE = 131072;
    int TYPE_TEXT_FLAG_NO_SUGGESTIONS = 524288;
    int TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 32;
    int TYPE_TEXT_VARIATION_EMAIL_SUBJECT = 48;
    int TYPE_TEXT_VARIATION_FILTER = 176;
    int TYPE_TEXT_VARIATION_LONG_MESSAGE = 80;
    int TYPE_TEXT_VARIATION_NORMAL = 0;
    int TYPE_TEXT_VARIATION_PASSWORD = 128;
    int TYPE_TEXT_VARIATION_PERSON_NAME = 96;
    int TYPE_TEXT_VARIATION_PHONETIC = 192;
    int TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 112;
    int TYPE_TEXT_VARIATION_SHORT_MESSAGE = 64;
    int TYPE_TEXT_VARIATION_URI = 16;
    int TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 144;
    int TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 160;
    int TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 208;
    int TYPE_TEXT_VARIATION_WEB_PASSWORD = 224;
}

Copy the code

Default inputType of EditText in Android That is inputType default values for the inputType. TYPE_CLASS_TEXT | inputType. TYPE_TEXT_FLAG_MULTI_LINE combination results.

Then we move on to the return value of the isMultilineInputType() method

(type & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE))
                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
Copy the code

Converted to a decimal for (131073 & (15 | 131072)) = = 1 | 131072 converted to binary as follows: (100000000000000001 & 1111 | (100000000000000000)) = = 1 | 100000000000000000 or 100000000000000001 = = 100000000000000001 True.

So it goes into the last condition, and the return value clearly indicates that the multiline text compiler always displays the Enter key. So we’ll see that the property that we set is invalid.

If we set singleLine = true, the source code will look like this:

 /**
     * Adds or remove the EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE on the mInputType.
     * @param singleLine
     */
    private void setInputTypeSingleLine(boolean singleLine) {
        if(mEditor ! = null && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) {if (singleLine) {
                mEditor.mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
            } else{ mEditor.mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; }}}Copy the code

If singleLine is true, inputType = ~ 131072, that is, 0111111111111111111111111 == 100000000000000001, Obviously false.

If inputType = TYPE_CLASS_TEXT is set, the return value is false, so there is no criterion, i.e., the value of imeOptions is set in code or XML.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — 2018/12/05 update — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Thank you very much for your correction. I had a little bit of a misunderstanding.

We take a look at the last step if satisfied isMultilineInputType () = = true on this condition, will be executed outAttrs. ImeOptions. | = EditorInfo IME_FLAG_NO_ENTER_ACTION; Note that the IME_FLAG_NO_ENTER_ACTION* flag is set and only the “Enter” key is displayed. Therefore, in order to achieve the effect we want to set, the key is not “cannot meet the characteristics of multi-line input”, but need to clear the flag, make the flag invalid.

Therefore, the following questions arise:

How do we set EditText to support multi-line properties and the imeOptions effect that we set? The key is to clear the IME_FLAG_NO_ENTER_ACTION flag, usually with a bit operation. So we first need to override the EditText onCreateInputConnection () method and then clear the IME_FLAG_NO_ENTER_ACTION flag. The specific code is as follows:

@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
        if(inputConnection ! = null){ outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION; }return inputConnection;
    }
Copy the code

Ps: There is another solution to this problem:

editText.setHorizontallyScrolling(false); 
editText.setMaxLines(Integer.MAX_VALUE);
Copy the code

I looked at the code and didn’t see how it worked. Welcome to exchange views.

conclusion

The key to making imeOptions work for us is to unflag the IME_FLAG_NO_ENTER_ACTION bit.

You can use either of the following methods:

1. Set the singleLine property to true

2. Alternatively, set inputType. And it doesn’t have to be text, which is TYPE_CLASS_TEXT, As long as the end result is not 131072 (namely InputType TYPE_CLASS_TEXT | InputType. TYPE_TEXT_FLAG_MULTI_LINE can, according to the demand to set up a text or pure digital keyboards, etc.)

You can override the onCreateInputConnection () method of the EditText if you want to satisfy multiple lines of input and set imeOptins, and then clear IME_FLAG_NO_ENTER_ACTION

Refer to the article

EditorInfo document

Invalid use and setting of imeOptions property in EditText resolved

Online base conversion