我正在写一个IME(InputMethodService),我在IME本身有一个编辑文本,但是当我想在编辑文本中输入文本时,编辑文本无法聚焦,我输入的内容只是转到外面的另一个编辑文本IME.如何使IME中的编辑文本像普通编辑文本一样工作
我解决这个问题的方法是保持两个输入连接,一个连接到用户正在编辑的原始文本字段,另一个连接到键盘内的文本字段.更多关于第二个.你应该已经拥有的第一个,它是键盘发送所有命令的地方.就我而言,它被称为mIC.所以我在我的类上创建了另一个名为mOtherIC的InputConnection变量,该变量具有InputConnection,并引入了一个名为getIC()的新方法,该方法只返回当时正在使用的两个InputConnections中的相应一个(在我的情况下由布尔值控制, true表示使用键盘中的文本字段,false表示使用最初显示键盘的文本字段.每次我以前访问过mIC时,我都用getIC()替换它,这样它就可以无缝地将输入发送到正确的输入连接.
键盘中文本字段的InputConnection在这里很有意思,我基本上必须将内部Android EditableInputConnection复制到我自己的CustomInputConnection类中:
public class CustomInputConnection extends BaseInputConnection { private static final boolean DEBUG = false; private static final String TAG = "CustomInputConnection"; private final TextView mTextView; // Keeps track of nested begin/end batch edit to ensure this connection always has a // balanced impact on its associated TextView. // A negative value means that this connection has been finished by the InputMethodManager. private int mBatchEditNesting; public CustomInputConnection(TextView textview) { super(textview, true); mTextView = textview; } @Override public Editable getEditable() { TextView tv = mTextView; if (tv != null) { return tv.getEditableText(); } return null; } @Override public boolean beginBatchEdit() { synchronized(this) { if (mBatchEditNesting >= 0) { mTextView.beginBatchEdit(); mBatchEditNesting++; return true; } } return false; } @Override public boolean endBatchEdit() { synchronized(this) { if (mBatchEditNesting > 0) { // When the connection is reset by the InputMethodManager and reportFinish // is called, some endBatchEdit calls may still be asynchronously received from the // IME. Do not take these into account, thus ensuring that this IC's final // contribution to mTextView's nested batch edit count is zero. mTextView.endBatchEdit(); mBatchEditNesting--; return true; } } return false; } // @Override // protected void reportFinish() { // super.reportFinish(); // // synchronized(this) { // while (mBatchEditNesting > 0) { // endBatchEdit(); // } // // Will prevent any further calls to begin or endBatchEdit // mBatchEditNesting = -1; // } // } @Override public boolean clearMetaKeyStates(int states) { final Editable content = getEditable(); if (content == null) return false; KeyListener kl = mTextView.getKeyListener(); if (kl != null) { try { kl.clearMetaKeyState(mTextView, content, states); } catch (AbstractMethodError e) { // This is an old listener that doesn't implement the // new method. } } return true; } @Override public boolean commitCompletion(CompletionInfo text) { if (DEBUG) Log.v(TAG, "commitCompletion " + text); mTextView.beginBatchEdit(); mTextView.onCommitCompletion(text); mTextView.endBatchEdit(); return true; } /** * Calls the {@link TextView#onCommitCorrection} method of the associated TextView. */ @Override public boolean commitCorrection(CorrectionInfo correctionInfo) { if (DEBUG) Log.v(TAG, "commitCorrection" + correctionInfo); mTextView.beginBatchEdit(); mTextView.onCommitCorrection(correctionInfo); mTextView.endBatchEdit(); return true; } @Override public boolean performEditorAction(int actionCode) { if (DEBUG) Log.v(TAG, "performEditorAction " + actionCode); mTextView.onEditorAction(actionCode); return true; } @Override public boolean performContextMenuAction(int id) { if (DEBUG) Log.v(TAG, "performContextMenuAction " + id); mTextView.beginBatchEdit(); mTextView.onTextContextMenuItem(id); mTextView.endBatchEdit(); return true; } @Override public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { if (mTextView != null) { ExtractedText et = new ExtractedText(); if (mTextView.extractText(request, et)) { if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) { // mTextView.setExtracting(request); } return et; } } return null; } @Override public boolean performPrivateCommand(String action, Bundle data) { mTextView.onPrivateIMECommand(action, data); return true; } @Override public boolean commitText(CharSequence text, int newCursorPosition) { if (mTextView == null) { return super.commitText(text, newCursorPosition); } 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; } @Override public boolean requestCursorUpdates(int cursorUpdateMode) { if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode); // It is possible that any other bit is used as a valid flag in a future release. // We should reject the entire request in such a case. final int KNOWN_FLAGS_MASK = InputConnection.CURSOR_UPDATE_IMMEDIATE | InputConnection.CURSOR_UPDATE_MONITOR; final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK; if (unknownFlags != 0) { if (DEBUG) { Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." + " cursorUpdateMode=" + cursorUpdateMode + " unknownFlags=" + unknownFlags); } return false; } return false; // if (mIMM == null) { // // In this case, TYPE_CURSOR_ANCHOR_INFO is not handled. // // TODO: Return some notification code rather than false to indicate method that // // CursorAnchorInfo is temporarily unavailable. // return false; // } // mIMM.setUpdateCursorAnchorInfoMode(cursorUpdateMode); // if ((cursorUpdateMode & InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0) { // if (mTextView == null) { // // In this case, FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is silently ignored. // // TODO: Return some notification code for the input method that indicates // // FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is ignored. // } else if (mTextView.isInLayout()) { // // In this case, the view hierarchy is currently undergoing a layout pass. // // IMM#updateCursorAnchorInfo is supposed to be called soon after the layout // // pass is finished. // } else { // // This will schedule a layout pass of the view tree, and the layout event // // eventually triggers IMM#updateCursorAnchorInfo. // mTextView.requestLayout(); // } // } // return true; } }
这个类看起来很糟糕,因为我注释掉了不能构建的代码,可能是因为它访问了内部的Android API.也就是说,有一些警告(当你切换到这个输入法时,可能需要手动设置光标,两个InputConnections之间的大写类型的结转),它可以工作.您现在要做的就是在某个时刻将mOtherIC从我的第一段设置为新的CustomInputConnection(yourTextfield),其中yourTextfield是键盘内的文本字段.