我正在实施一个用于输入PIN码的自定义文本编辑器,在运行Android 5的三星手机上工作正常,但停止在Android 6的Motorola Nexus上接收用户输入。我已经实现了 onCheckIsTextEditor , onCreateInputConnection 和 checkInputConnectionProxy 方法,告诉系统我的视图是文本编辑器并激活数字键盘。我还实现了 InputConnection 类。现在,在Android 5上它工作正常 - 调用 begin / endBatchEdit 方法,并成功处理用户的输入。然而,在Android 6上,0-9键绕过我的InputConnection对象并直接传递给编辑器的键监听器,但标点符号键(',','。',' - '和'')仍然通过 InputConnection传递。开始/ endBatchEdit 。有没有办法让数字通过 InputConnection ?
我的编辑器的源代码如下(跳过不相关的部分):
//this part is invoked from editor's constructor:
setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
//handling character deletion:
if(0 == mBatchLevel && event.getAction() == KeyEvent.ACTION_UP) {
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
synchronized(this) {
mInputConnection.deleteSurroundingText(1, 0);
}
return true;
//this is the workaround to handle digits which I don't want to do here:
// case KeyEvent.KEYCODE_0: case KeyEvent.KEYCODE_1: //handling keys here in addition to batch mode, because batch doesn't always work...
// case KeyEvent.KEYCODE_2: case KeyEvent.KEYCODE_3:
// case KeyEvent.KEYCODE_4: case KeyEvent.KEYCODE_5:
// case KeyEvent.KEYCODE_6: case KeyEvent.KEYCODE_7:
// case KeyEvent.KEYCODE_8: case KeyEvent.KEYCODE_9:
// if(getPasscode().length() < passcodeSize) {
// Editable editable = mInputConnection.getEditable();
// editable.append(event.getNumber());
// setPasscode(editable.toString());
// }
//
// return true;
}
}
return false;
}
});
public void setPasscode(String passcode) {
//skipped: making sure new passcode is not too long and updating UI
...
//then updating InputConnection:
mPasscode = passcode;
if(mInputConnection != null) {
Editable editable = mInputConnection.getEditable();
editable.replace(0, editable.length(), mPasscode); //because if the passcode is trimmed, the editable's content won't match it anymore
mInputConnection.updateInputManager();
}
}
public String getPasscode() {
return mPasscode;
}
...
//implementation of InputConnection, based on android.view.inputmethod.BaseInputConnection:
private static ExtractedText createEmptyExtractedText() {
ExtractedText text = new ExtractedText();
text.startOffset = 0;
text.partialEndOffset = text.partialStartOffset = -1;
return text;
}
class InputConnection extends BaseInputConnection {
public InputConnection(PasscodeInputWidget editor) {
super(editor, true);
getEditable().append(getPasscode());
}
private void updateInputManager() {
String content = getPasscode();
int length = content.length();
InputMethodManager inputManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
PasscodeInputWidget passcodeWidget = PasscodeInputWidget.this;
inputManager.updateSelection(passcodeWidget, length, length, length, length);
if(mTextExtractionToken != -1) {
ExtractedText extrText = createEmptyExtractedText();
extrText.text = content;
extrText.selectionEnd = extrText.selectionStart = content.length();
inputManager.updateExtractedText(passcodeWidget, mTextExtractionToken, extrText);
}
}
@Override
public boolean beginBatchEdit() {
synchronized(this) {
++mBatchLevel;
}
return true;
}
@Override
public boolean endBatchEdit() {
synchronized(this) {
boolean batchEnded = 0 == mBatchLevel || 0 == --mBatchLevel;
if(batchEnded) {
Editable editable = getEditable();
if(!TextUtils.equals(getPasscode(), editable)) {
setPasscode(editable.toString());
}
}
return !batchEnded;
}
}
protected void reportFinish() {
synchronized(this) {
while(mBatchLevel > 0) {
endBatchEdit();
}
}
}
@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
ExtractedText extrText = createEmptyExtractedText();
if(request.hintMaxLines > 0) {
String content = getPasscode();
extrText.text = content.substring(0, request.hintMaxChars);
extrText.selectionEnd = extrText.selectionStart = extrText.text.length();
}
PasscodeInputWidget.this.mTextExtractionToken = (GET_EXTRACTED_TEXT_MONITOR == flags) ? request.token : -1;
return extrText;
}
@Override
public CharSequence getTextAfterCursor(int length, int flags) {
return "";
}
@Override
public CharSequence getTextBeforeCursor(int length, int flags) {
return getPasscode();
}
@Override
public boolean setSelection(int start, int end) {
int atEnd = getPasscode().length();
return super.setSelection(atEnd, atEnd);
}
};
@Override
public boolean onCheckIsTextEditor() { return true; }
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.fieldId = getId();
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
outAttrs.imeOptions = EditorInfo.IME_NULL;
outAttrs.initialSelEnd = outAttrs.initialSelStart = getPasscode().length();
outAttrs.packageName = getContext().getPackageName();
synchronized(this) {
mInputConnection = new InputConnection(this);
}
mInputConnection.updateInputManager();
return mInputConnection;
}
@Override
public boolean checkInputConnectionProxy(View view) { return true; }