当前位置:   article > 正文

android -- EditText 设置 imeOptions 属性为何失效?_setimeoptions

setimeoptions
前言

最近改 bug 的时候碰到一个小知识点,在搜索界面希望键盘上的 enter 键改为搜索按钮。也就是下图的效果,是不是非常常见。
在这里插入图片描述

然后我就记得 Editext 有个 imeOptions 的属性,可以设置 enter 键的效果。所以果断在 xml 中写下 android:imeOptions="actionSearch",然后把问题改为已修复,信心满满。结果等编译运行起来在手机上发现没有起作用,还是 enter 键。 搜索了一下,发现大家都是这样回答的:

如果设置了 imeOptions 属性,键盘没有变化 ,那么需要单独加一些其他的属性配合使用,xml中 属性设置:
1 将singleLine设置为true
2 或者将inputType设置为text

试了一下,果然 ok 了。但为什么这样设置显示就对了呢?你是不是同样也有疑问? 所以就让我们共同去探寻答案吧!

ImeOptions 属性源码解析

这里首先推荐一个看源码的插件:AndroidSourceView

Step1

因为如果是在 JAVA 文件中,我们设置 imeOptions 属性代码为: editText.setImeOptions(EditorInfo.IME_ACTION_SEARCH);
所以首先要定位到 setImeOptions() 这个方法 ,在 EdiText 中没有搜到,所以果断跳到 EditText 的父类 TextView 的源码中,然后发现目标:

 /**
     * 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
     * 当输入法编译器(IME)获取焦点的时候,通过{@link EditorInfo#imeOptions} 报告给输入法编辑器,
     * 来更改与文本视图关联的编辑器类型值。
     */
    public void setImeOptions(int imeOptions) {
       //1.判断是否需要创建 Editor 对象
        createEditorIfNeeded();
        //2.判断是否需要创建 InputContentType 
        mEditor.createInputContentTypeIfNeeded();
        //3. 将入参赋值给InputContentType.imeOptions
        mEditor.mInputContentType.imeOptions = imeOptions;
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这个方法里面只是进行了判断是否需要创建一些对象,然后将我们的入参赋值给 InputContentType.imeOptions。从这方法的注释中我们可以知道关键对象是 EditorInfo#imeOptions

Step2

继续搜索关键字 imeOptions ,然后发现下面这个方法:

 @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 been set, 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;
    }

  • 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

然后我们只关心我们探究的信息,所以伪代码如下:

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) {
             //..代码省略
            }
            if (isMultilineInputType(outAttrs.inputType)) {
                // Multi-line text editors should always show an enter key.
                //多行文本编译器总是会显示 enter 键
                outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
            }
        return null;
    }

  • 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
Step3

我们首先分析一下:

  • 第一个 if 判断: mEditor.mInputContentType 这个值是否为空,答案是 false ,因为我们在setImeOptions 方法的第 3 步已经给它赋值了,所以不会为空,所以outAttrs.imeOptions 结果为我们设置的值
  • 第二个 if 判断:是来寻找我们的 Editext 对象的往下的一个方向是否存在可以获取焦点的 View,这个得看实际输入框下面布局是否还会有一个输入框,目前项目没有,所以 false
  • 第三个 if 判断:跟第二个条件一样,只过是方向为向上查找
  • 第四个 if 判断:首先 (outAttrs.imeOptions & EditorInfo.IME_MASK_ACTION值是多少呢,我们代码里设置的值是 IME_ACTION_SEARCH ,值为 3,EditorInfo.IME_MASK_ACTION 的值为 255 ,取二进制值为 00000011 & 1111 1111 = 00000011 十进制为 3,而EditorInfo.IME_ACTION_UNSPECIFIED 值为 0,所以结果为 false。

到目前为止 outAttrs.imeOptions 的结果依然为我们在代码中设置的值。

Step4

来看最后一个 if 判断:isMultilineInputType(outAttrs.inputType) 光看字段意思我们能猜到判断输入框是不是多行输入。看一下具体代码:

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);
    }
  • 1
  • 2
  • 3
  • 4

type 这个值为我们在 onCreateInputConnection() 方法中通过getInputType () 方法获取,结果为返回我们代码中设置的 inputType 值,而我们代码中没有设置,那么这个 inputType 的默认值是什么呢?源码中没有找到,然后我自己 debug 了一下,结果如下图:
在这里插入图片描述
所以默认值为 131073,而 InputType 各种类型的值如下:

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;
}

  • 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

结合 stackoverflow 上 Default inputType of EditText in android 答案和 InputType 的值,也就是说 inputType 默认值 为 InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_FLAG_MULTI_LINE 组合的结果。

然后我们继续往下看,isMultilineInputType() 方法的返回值

(type & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE))
                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
  • 1
  • 2

转换为十进制为:(131073 & (15 | 131072)) == 1 | 131072
转换为二进制为:(100000000000000001 &(1111 | 100000000000000000)) == 1 | 100000000000000000
100000000000000001 == 100000000000000001
即 true。

所以会进入最后一个条件中,而该返回值明确指出 多行文本编译器总是会显示 enter 键 。所以我们会看到我们设置的属性失效了。

而如果我们设置了 singleLine = true ,源码中是这样设置的:

 /**
     * 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;
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

如果 singleLine 为 true,inputType = ~ 131072,即011111111111111111 ,最后计算结果为 111111111111111111 == 100000000000000001,显然 false。
或者设置了inputType = TYPE_CLASS_TEXT 我们也可以得出返回值为 false,所以不会进判断条件,也就是 imeOptions 的值就是我们在代码里或者 xml 中设置的。

我们来看一下最后一步如果满足了isMultilineInputType() == true这个条件,就会执行outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;,需要注意的是 IME_FLAG_NO_ENTER_ACTION* 这个 flag,只要设置了这个 flag,就只会显示 “enter” 键。所以想要到达我们要设置的效果,关键不是“不能满足多行输入的特性”,而是需要清除这个flag,让这个flag 失效

所以,就衍生了下面的问题:

如何设置 EditText 支持多行属性同时我们设置的 imeOptions 效果也能实现?
答案的关键就是来清除 IME_FLAG_NO_ENTER_ACTION 这个 flag ,一般用位运算清除。
所以我们首先需要重写 EditText 的 onCreateInputConnection() 方法,然后清除 IME_FLAG_NO_ENTER_ACTION 这个 flag 。
具体代码如下:

@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
        if(inputConnection != null){
            outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
        }
        return inputConnection;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

ps: 查这个问题的时候还有种方案是这样的:

editText.setHorizontallyScrolling(false); 
editText.setMaxLines(Integer.MAX_VALUE);
  • 1
  • 2

看了代码,并没有发现其原理。欢迎交流看法。

总结

所以要让 imeOptions 满足我们的需求,关键是取消 IME_FLAG_NO_ENTER_ACTION 这个标记位

可以选择如下处理方式:

1. 设置 singleLine 属性为 true

2. 或者设置 inputType 属性。 而这个属性值不一定非要是 ‘text’ 即TYPE_CLASS_TEXT 类型,只要最后结果不是 131072 (也就是 InputType.TYPE_CLASS_TEXT |InputType.TYPE_TEXT_FLAG_MULTI_LINE 即可,根据需求来定是 设置 text 或者 纯数字键盘等等)

3. 如果需要同时满足多行输入和 设置imeOptins 有效果可以重写 EditText 的 onCreateInputConnection() 方法,然后清除 IME_FLAG_NO_ENTER_ACTION

参考文章

EditorInfo文档
EditText中imeOptions属性使用及设置无效解决
在线进制转换

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/391003
推荐阅读
相关标签
  

闽ICP备14008679号