安卓框架,新增一个EditText的inputType类型

有个需求需要在全键盘时可以用代码设置时优先显示字母还是数字,其实inputtype:datetime时就是优先数字,就是这样设置输入的标点符号会被过滤,这就需要新增一个inputType了。

首先看TextView的源码

一:找到inputType 的来源
1.来自xml
case com.android.internal.R.styleable.TextView_inputType:
inputType = a.getInt(attr, EditorInfo.TYPE_NULL);
break;
2.肯定是调用setInputType,其实1的最后还是调用了setInputType

再看看inputype在attr中的定义

    <!-- The type of data being placed in a text field, used to help an
         input method decide how to let the user enter text.  The constants
         here correspond to those defined by
         {@link android.text.InputType}.  Generally you can select
         a single value, though some can be combined together as
         indicated.  Setting this attribute to anything besides
         <var>none</var> also implies that the text is editable. -->
    <attr name="inputType">
        <!-- There is no content type.  The text is not editable. -->
        <flag name="none" value="0x00000000" />
        <!-- Just plain old text.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_NORMAL}. -->
        <flag name="text" value="0x00000001" />
        <!-- Can be combined with <var>text</var> and its variations to
             request capitalization of all characters.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_CHARACTERS}. -->
        <flag name="textCapCharacters" value="0x00001001" />
        <!-- Can be combined with <var>text</var> and its variations to
             request capitalization of the first character of every word.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_WORDS}. -->
        <flag name="textCapWords" value="0x00002001" />
        <!-- Can be combined with <var>text</var> and its variations to
             request capitalization of the first character of every sentence.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_CAP_SENTENCES}. -->
        <flag name="textCapSentences" value="0x00004001" />
        <!-- Can be combined with <var>text</var> and its variations to
             request auto-correction of text being input.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_AUTO_CORRECT}. -->
        <flag name="textAutoCorrect" value="0x00008001" />
        <!-- Can be combined with <var>text</var> and its variations to
             specify that this field will be doing its own auto-completion and
             talking with the input method appropriately.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_AUTO_COMPLETE}. -->
        <flag name="textAutoComplete" value="0x00010001" />
        <!-- Can be combined with <var>text</var> and its variations to
             allow multiple lines of text in the field.  If this flag is not set,
             the text field will be constrained to a single line.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_MULTI_LINE}. -->
        <flag name="textMultiLine" value="0x00020001" />
        <!-- Can be combined with <var>text</var> and its variations to
             indicate that though the regular text view should not be multiple
             lines, the IME should provide multiple lines if it can.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_IME_MULTI_LINE}. -->
        <flag name="textImeMultiLine" value="0x00040001" />
        <!-- Can be combined with <var>text</var> and its variations to
             indicate that the IME should not show any
             dictionary-based word suggestions.  Corresponds to
             {@link android.text.InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS}. -->
        <flag name="textNoSuggestions" value="0x00080001" />
        <!-- Text that will be used as a URI.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_URI}. -->
        <flag name="textUri" value="0x00000011" />
        <!-- Text that will be used as an e-mail address.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_EMAIL_ADDRESS}. -->
        <flag name="textEmailAddress" value="0x00000021" />
        <!-- Text that is being supplied as the subject of an e-mail.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT}. -->
        <flag name="textEmailSubject" value="0x00000031" />
        <!-- Text that is the content of a short message.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE}. -->
        <flag name="textShortMessage" value="0x00000041" />
        <!-- Text that is the content of a long message.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE}. -->
        <flag name="textLongMessage" value="0x00000051" />
        <!-- Text that is the name of a person.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_PERSON_NAME}. -->
        <flag name="textPersonName" value="0x00000061" />
        <!-- Text that is being supplied as a postal mailing address.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_POSTAL_ADDRESS}. -->
        <flag name="textPostalAddress" value="0x00000071" />
        <!-- Text that is a password.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_PASSWORD}. -->
        <flag name="textPassword" value="0x00000081" />
        <!-- Text that is a password that should be visible.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_VISIBLE_PASSWORD}. -->
        <flag name="textVisiblePassword" value="0x00000091" />
        <!-- Text that is being supplied as text in a web form.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}. -->
        <flag name="textWebEditText" value="0x000000a1" />
        <!-- Text that is filtering some other data.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_FILTER}. -->
        <flag name="textFilter" value="0x000000b1" />
        <!-- Text that is for phonetic pronunciation, such as a phonetic name
             field in a contact entry.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_PHONETIC}. -->
        <flag name="textPhonetic" value="0x000000c1" />
        <!-- Text that will be used as an e-mail address on a web form.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS}. -->
        <flag name="textWebEmailAddress" value="0x000000d1" />
        <!-- Text that will be used as a password on a web form.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_TEXT} |
             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_PASSWORD}. -->
        <flag name="textWebPassword" value="0x000000e1" />
        <!-- A numeric only field.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_NUMBER} |
             {@link android.text.InputType#TYPE_NUMBER_VARIATION_NORMAL}. -->
        <flag name="number" value="0x00000002" />
        <!-- Can be combined with <var>number</var> and its other options to
             allow a signed number.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_NUMBER} |
             {@link android.text.InputType#TYPE_NUMBER_FLAG_SIGNED}. -->
        <flag name="numberSigned" value="0x00001002" />
        <!-- Can be combined with <var>number</var> and its other options to
             allow a decimal (fractional) number.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_NUMBER} |
             {@link android.text.InputType#TYPE_NUMBER_FLAG_DECIMAL}. -->
        <flag name="numberDecimal" value="0x00002002" />
        <!-- A numeric password field.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_NUMBER} |
             {@link android.text.InputType#TYPE_NUMBER_VARIATION_PASSWORD}. -->
        <flag name="numberPassword" value="0x00000012" />
        <!-- For entering a phone number.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_PHONE}. -->
        <flag name="phone" value="0x00000003" />
        <!-- For entering a date and time.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_DATETIME} |
             {@link android.text.InputType#TYPE_DATETIME_VARIATION_NORMAL}. -->
        <flag name="datetime" value="0x00000004" />
        <!-- For entering a date.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_DATETIME} |
             {@link android.text.InputType#TYPE_DATETIME_VARIATION_DATE}. -->
        <flag name="date" value="0x00000014" />
        <!-- For entering a time.  Corresponds to
             {@link android.text.InputType#TYPE_CLASS_DATETIME} |
             {@link android.text.InputType#TYPE_DATETIME_VARIATION_TIME}. -->
        <flag name="time" value="0x00000024" />
    </attr>

注意注释

原来所有的inputype是由标志位自由合成的,分别是类型和以及变量(通俗说就是变种),以及flag(分不清和变种有什么区别)
再看TextView源码 

final int variation =
inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
final boolean passwordInputType = variation
== (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
final boolean webPasswordInputType = variation
== (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD);
final boolean numberPasswordInputType = variation
== (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);


这些标志位的作用就是为了辨别是那个类,那个变量以及那个flag用来控制软键盘的显示模式和editext,那我的需求是全键盘,可以优先数字或者字母,那么新增的标志位类是
<flag name="text" value="0x00000001" />
如何新增flag可以模仿嘛
在<flag name="textNoSuggestions" value="0x00080001" />后面新增一个
<flag name="XXXXXXXXXXXXXXXXXX" value="0x00100001" />即可完成flag

贴出标志位的范围
public static final int TYPE_MASK_CLASS = 0x0000000f;
public static final int TYPE_MASK_VARIATION = 0x00000ff0;
public static final int TYPE_MASK_FLAGS = 0x00fff000;

二:看setInputType是如何设置inptuType的

private void setInputType(int type, boolean direct) {
    final int cls = type & EditorInfo.TYPE_MASK_CLASS;
    KeyListener input;
    if (cls == EditorInfo.TYPE_CLASS_TEXT) {
        boolean autotext = (type & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0;
        TextKeyListener.Capitalize cap;
        if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {
            cap = TextKeyListener.Capitalize.CHARACTERS;
        } else if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS) != 0) {
            cap = TextKeyListener.Capitalize.WORDS;
        } else if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) {
            cap = TextKeyListener.Capitalize.SENTENCES;
        } else {
            cap = TextKeyListener.Capitalize.NONE;
        }
        input = TextKeyListener.getInstance(autotext, cap);
    } else if (cls == EditorInfo.TYPE_CLASS_NUMBER) {
        input = DigitsKeyListener.getInstance(
                (type & EditorInfo.TYPE_NUMBER_FLAG_SIGNED) != 0,
                (type & EditorInfo.TYPE_NUMBER_FLAG_DECIMAL) != 0);
    } else if (cls == EditorInfo.TYPE_CLASS_DATETIME) {
        switch (type & EditorInfo.TYPE_MASK_VARIATION) {
            case EditorInfo.TYPE_DATETIME_VARIATION_DATE:
                input = DateKeyListener.getInstance();
                break;
            case EditorInfo.TYPE_DATETIME_VARIATION_TIME:
                input = TimeKeyListener.getInstance();
                break;
            default:
                input = DateTimeKeyListener.getInstance();
                break;
        }
    } else if (cls == EditorInfo.TYPE_CLASS_PHONE) {
        input = DialerKeyListener.getInstance();
    } else {
        input = TextKeyListener.getInstance();
    }
    setRawInputType(type);
    if (direct) {
        createEditorIfNeeded();
        mEditor.mKeyListener = input;
    } else {
        setKeyListenerOnly(input);
    }
}

我们只看EditorInfo.TYPE_CLASS_TEXT的情况
出现了TextKeyListener,点进去发现只和硬件的键盘有关系,可以pass
再看setRawInputType(type);

public void setRawInputType(int type) {
    if (type == InputType.TYPE_NULL && mEditor == null) return; //TYPE_NULL is the default value
    createEditorIfNeeded();
    mEditor.mInputType = type;
}

貌似起作用的就是 mEditor.mInputType = type;我们看下mEditor做什么了。

三.Editor和EditorInfo
好吧,Editor这个类没有实现Parcelable接口,因为我们是要控制到软键盘的必须是跨进程,Editor只起到中间作用,我们再看下EditorInfo,

public class EditorInfo implements InputType, Parcelable

就是我们要的最终能控制软键盘的显示模式的类,通过outAttrs.inputType = getInputType();EditorInfo 获取了inputType 如下

public int getInputType() {
    return mEditor == null ? EditorInfo.TYPE_NULL : mEditor.mInputType;
}


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

onCreateInputConnection这是重写了父类View的方法,返回类型是InputConnection 实现类是EditableInputConnection。我们先看下onCreateInputConnection

四。onCreateInputConnection
好吧,又出现新的类InputMethodManager.java:,该类的startInputInner方法调用了当前edittext的onCreateInputConnection,且传入对象tba:EditorInfo tba = new EditorInfo(),该对象重新被Edittext赋值

 boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
            int windowFlags) {
        final View view;
        synchronized (mH) {
            view = mServedView;

            // Make sure we have a window token for the served view.
            if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
            if (view == null) {
                if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
                return false;
            }
        }

        // Now we need to get an input connection from the served view.
        // This is complicated in a couple ways: we can't be holding our lock
        // when calling out to the view, and we need to make sure we call into
        // the view on the same thread that is driving its view hierarchy.
        Handler vh = view.getHandler();
        if (vh == null) {
            // If the view doesn't have a handler, something has changed out
            // from under us, so just close the current input.
            // If we don't close the current input, the current input method can remain on the
            // screen without a connection.
            if (DEBUG) Log.v(TAG, "ABORT input: no handler for view! Close current input.");
            closeCurrentInput();
            return false;
        }
        if (vh.getLooper() != Looper.myLooper()) {
            // The view is running on a different thread than our own, so
            // we need to reschedule our work for over there.
            if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
            vh.post(new Runnable() {
                @Override
                public void run() {
                    startInputInner(null, 0, 0, 0);
                }
            });
            return false;
        }

        // Okay we are now ready to call into the served view and have it
        // do its stuff.
        // Life is good: let's hook everything up!
        EditorInfo tba = new EditorInfo();
        tba.packageName = view.getContext().getPackageName();
        tba.fieldId = view.getId();
        InputConnection ic = view.onCreateInputConnection(tba);
        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);

        synchronized (mH) {
            // Now that we are locked again, validate that our state hasn't
            // changed.
            if (mServedView != view || !mServedConnecting) {
                // Something else happened, so abort.
                if (DEBUG) Log.v(TAG, 
                        "Starting input: finished by someone else (view="
                        + mServedView + " conn=" + mServedConnecting + ")");
                return false;
            }

            // If we already have a text box, then this view is already
            // connected so we want to restart it.
            if (mCurrentTextBoxAttribute == null) {
                controlFlags |= CONTROL_START_INITIAL;
            }

            // Hook 'em up and let 'er rip.
            mCurrentTextBoxAttribute = tba;
            mServedConnecting = false;
            // Notify the served view that its previous input connection is finished
            notifyInputConnectionFinished();
            mServedInputConnection = ic;
            ControlledInputConnectionWrapper servedContext;
            if (ic != null) {
                mCursorSelStart = tba.initialSelStart;
                mCursorSelEnd = tba.initialSelEnd;
                mCursorCandStart = -1;
                mCursorCandEnd = -1;
                mCursorRect.setEmpty();
                mCursorAnchorInfo = null;
                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
            } else {
                servedContext = null;
            }
            if (mServedInputConnectionWrapper != null) {
                mServedInputConnectionWrapper.deactivate();
            }
            mServedInputConnectionWrapper = servedContext;

            try {
                if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
                        + ic + " tba=" + tba + " controlFlags=#"
                        + Integer.toHexString(controlFlags));
                InputBindResult res;
                if (windowGainingFocus != null) {
                    res = mService.windowGainedFocus(mClient, windowGainingFocus,
                            controlFlags, softInputMode, windowFlags,
                            tba, servedContext);
                } else {
                    res = mService.startInput(mClient,
                            servedContext, tba, controlFlags);
                }
                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                if (res != null) {
                    if (res.id != null) {
                        setInputChannelLocked(res.channel);
                        mBindSequence = res.sequence;
                        mCurMethod = res.method;
                        mCurId = res.id;
                        mNextUserActionNotificationSequenceNumber =
                                res.userActionNotificationSequenceNumber;
                    } else {
                        if (res.channel != null && res.channel != mCurChannel) {
                            res.channel.dispose();
                        }
                        if (mCurMethod == null) {
                            // This means there is no input method available.
                            if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
                            return true;
                        }
                    }
                }
                if (mCurMethod != null && mCompletions != null) {
                    try {
                        mCurMethod.displayCompletions(mCompletions);
                    } catch (RemoteException e) {
                    }
                }
            } catch (RemoteException e) {
                Log.w(TAG, "IME died: " + mCurId, e);
            }
        }

        return true;
    }

最后通过调用服务InputMethodManagerService的方法如下,就可以通知到软键盘inputType。
if (windowGainingFocus != null) {
res = mService.windowGainedFocus(mClient, windowGainingFocus,
controlFlags, softInputMode, windowFlags,
tba, servedContext);
} else {
res = mService.startInput(mClient,
servedContext, tba, controlFlags);
}

只要软键盘收到inputType,最后就是修改软键盘的源码实现想要的效果

猜你喜欢

转载自blog.csdn.net/u013762045/article/details/81450653