Android上的自定义文本编辑器和接收键盘输入

时间:2016-08-03 07:24:11

标签: android text-editor android-input-method

我正在实施一个用于输入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; }

0 个答案:

没有答案