- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
6. TextView 接收软键盘输入
Android 上的标准文本编辑控件是 EditText,而 EditText 对软键盘输入的处理,却是在 TextView 内部实现的。Android 为所有的 View 预留了一个接收软键盘输入的接口类,叫 InputConnection。软键盘以 InputConnection 为桥梁把文字输入、文字修改、文字删除等传递给 View。任意 View 只要重写 onCheckIsTextEditor() 并返回 true,然后重写 onCreateInputConnection(EditorInfo outAttrs) 返回一个 InputConnection 的实例,便可以接收软键盘的输入。TextView 的软键盘输入接收,是通过 EditableInputConnection 类来实现的。
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) { //把软键盘的 enter 设为下一步 outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; } else { //把软键盘的 enter 设为完成 outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE; } if (!shouldAdvanceFocusOnEnter()) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; } } if (isMultilineInputType(outAttrs.inputType)) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; } outAttrs.hintText = mHint; //判断 TextView 内部文本是否可编辑 if (mText instanceof Editable) { //返回 EditableInputConnection 实例 InputConnection ic = new EditableInputConnection(this); outAttrs.initialSelStart = getSelectionStart(); outAttrs.initialSelEnd = getSelectionEnd(); outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType()); return ic; } } return null; }
我们再来看一下 EditableInputConnection 里面的几个主要的方法:
首先是 commitText 方法,这个方法接收输入法输入的字符并提交给 TextView。
public boolean commitText(CharSequence text, int newCursorPosition) { //判断 TextView 是否为空 if (mTextView == null) { return super.commitText(text, newCursorPosition); } //判断文本是否 Span,来自输入法的 Span 一般只有 SuggestionSpan,SuggestionSpan 携带了输入法的错别字修正的词 if (text instanceof Spanned) { Spanned spanned = ((Spanned) text); SuggestionSpan[] spans = spanned.getSpans(0, text.length(), SuggestionSpan.class); mIMM.registerSuggestionSpansForNotification(spans); } mTextView.resetErrorChangedFlag(); //提交字符 boolean success = super.commitText(text, newCursorPosition); mTextView.hideErrorIfUnchanged(); //返回是否成功 return success; }
getEditable 方法,这个方法并不是 InputConnection 接口的一部分,而是 EditableInputConnection 的父类 BaseInputConnection 的方法,用来获取一个可编辑对象,EditableInputConnection 里面的所有修改都针对这个可编辑对象来做。
public Editable getEditable() { TextView tv = mTextView; if (tv != null) { //返回 TextView 的可编辑对象 return tv.getEditableText(); } return null; }
deleteSurroundingText 方法,这个方法用来删除光标前后的内容:
public boolean deleteSurroundingText(int beforeLength, int afterLength) { if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength + " / " + afterLength); final Editable content = getEditable(); if (content == null) return false; //批量删除标记 beginBatchEdit(); //获取当前已选择的文本的位置 int a = Selection.getSelectionStart(content); int b = Selection.getSelectionEnd(content); if (a > b) { int tmp = a; a = b; b = tmp; } int ca = getComposingSpanStart(content); int cb = getComposingSpanEnd(content); if (cb < ca) { int tmp = ca; ca = cb; cb = tmp; } if (ca != -1 && cb != -1) { if (ca < a) a = ca; if (cb > b) b = cb; } int deleted = 0; //删除光标之前的文本 if (beforeLength > 0) { int start = a - beforeLength; if (start < 0) start = 0; content.delete(start, a); deleted = a - start; } //删除光标之后的文本 if (afterLength > 0) { b = b - deleted; int end = b + afterLength; if (end > content.length()) end = content.length(); content.delete(b, end); } //结束批量编辑 endBatchEdit(); return true; }
commitCompletion 和 commitCorrection 方法,即是用来补全单词和修正错别字的方法,这两个方法内部都是调用 TextView 对应的方法来实现的。
public boolean commitCompletion(CompletionInfo text) { if (DEBUG) Log.v(TAG, "commitCompletion " + text); mTextView.beginBatchEdit(); mTextView.onCommitCompletion(text); mTextView.endBatchEdit(); return true; } @Override public boolean commitCorrection(CorrectionInfo correctionInfo) { if (DEBUG) Log.v(TAG, "commitCorrection" + correctionInfo); mTextView.beginBatchEdit(); mTextView.onCommitCorrection(correctionInfo); mTextView.endBatchEdit(); return true; }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论