How to update extracted text in a custom editor view

旧街凉风 提交于 2019-12-10 11:54:50

问题


I've created a custom editor for vertical Mongolian text. The circled views show my my custom editor. They keyboards at the bottom are system keyboards.

When a system keyboard inputs text into it in landscape orientation, though, the keyboard will likely show an extracted text view rather than my custom editor view. The circled view below is the extracted text view (which is not getting updated by my custom editor).

I need to somehow send updates from my view to the input method manager. This is stated in the InputConnection documentation:

Editor authors: as a general rule, try to comply with the fields in request for how many chars to return, but if performance or convenience dictates otherwise, please feel free to do what is most appropriate for your case. Also, if the GET_EXTRACTED_TEXT_MONITOR flag is set, you should be calling InputMethodManager.updateExtractedText(View, int, ExtractedText) whenever you call InputMethodManager.updateSelection(View, int, int, int, int).

I've been exploring the source code related to extracted text

  • TextView
  • Editor
  • InputMethodManager
  • InputMethodService
  • BaseInputConnection

but I'm lost.

Here is the closest I've gotten. This is a method inside my custom editor.

private void reportExtractedText() {

    // TODO we should be modifying this based on an ExtractedTextRequest

    ExtractedText et = new ExtractedText();
    final CharSequence content = getText();
    final int length = content.length();
    et.partialStartOffset = 0;
    et.partialEndOffset = length;
    et.startOffset = 0;
    et.selectionStart = getSelectionStart();
    et.selectionEnd = getSelectionEnd();
    et.flags = 0;
    et.text = content.subSequence(0, length);

    // FIXME: should be returning this from the ExtractedTextRequest
    int requestToken = 0;

    InputMethodManager imm = (InputMethodManager) getContext()
            .getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm == null) return;
    imm.updateExtractedText(this, requestToken, et);
}

When I am inside my editor I don't have a reference to the ExtractedTextRequest, which should be used to modify what I include in my extracted text update.

Here is another method inside my BaseInputConnection subclass (slightly modified from here). I have access to the ExtractedTextRequest but this is not where I am calling the updates from. It will cause the extracted text view to show the initial text correctly, but the updates still don't get applied. This method is called by the InputMethodService and can also be called by custom input methods.

@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
    if (request == null)
        return null;

    if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0)
        mExtractedTextRequest = request;  // mExtractedTextRequest currently doing nothing

    Editable editable = getEditable();
    if (editable == null) {
        return null;
    }
    int selStart = Selection.getSelectionStart(editable);
    int selEnd = Selection.getSelectionEnd(editable);

    ExtractedText extract = new ExtractedText();
    extract.flags = 0;
    extract.partialStartOffset = -1;
    extract.partialEndOffset = -1;
    extract.selectionStart = selStart;
    extract.selectionEnd = selEnd;
    extract.startOffset = 0;
    if ((request.flags & GET_TEXT_WITH_STYLES) != 0) {
        extract.text = new SpannableString(editable);
    } else {
        extract.text = editable.toString();
    }
    return extract;
}

I added a more generic MCVE here.


回答1:


The key to updating the extracted text view is to set the ExtractedTextRequest token. Without the token the updates don't take effect. Thanks to this answer for help with the token.

We can the token in the input connection's getExtractedText() with request.token and then add a method to the custom view to set it:

@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
    if (request == null)
        return null;

    Editable editable = getEditable();
    if (editable == null) {
        return null;
    }

    // passing the token to the custom view here
    mMongolEditText.setExtractedTextToken(request.token);

    int selStart = Selection.getSelectionStart(editable);
    int selEnd = Selection.getSelectionEnd(editable);

    ExtractedText extract = new ExtractedText();
    extract.flags = 0;
    extract.partialStartOffset = -1;
    extract.partialEndOffset = -1;
    extract.selectionStart = selStart;
    extract.selectionEnd = selEnd;
    extract.startOffset = 0;
    if ((request.flags & GET_TEXT_WITH_STYLES) != 0) {
        extract.text = new SpannableString(editable);
    } else {
        extract.text = editable.toString();
    }
    return extract;
}

That allows me to use the token when I call InputMethodManager.updateExtractedText() from within my custom view.

private int mExtractedTextRequestToken = 0;

void setExtractedTextToken(int token) {
    mExtractedTextRequestToken = token;
}

private void reportExtractedText() {

    int requestToken = mExtractedTextRequestToken;

    ExtractedText et = new ExtractedText();
    final CharSequence content = getText();
    final int length = content.length();
    et.partialStartOffset = -1;
    et.partialEndOffset = -1;
    et.startOffset = 0;
    et.selectionStart = getSelectionStart();
    et.selectionEnd = getSelectionEnd();
    et.flags = 0;
    et.text = content.subSequence(0, length);

    InputMethodManager imm = (InputMethodManager) getContext()
            .getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm == null) return;
    imm.updateExtractedText(this, requestToken, et);
}

You can view my full code here:

  • Custom view
  • Input connection


来源:https://stackoverflow.com/questions/50829361/how-to-update-extracted-text-in-a-custom-editor-view

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!