How to implement Extracted Text for a custom Android View
Asked Answered
B

2

8

Background

A custom editor view in Android is able to receive text from a system keyboard though an InputConnection. I have been able to make such a view successfully. However, when the device is in landscape mode, the system will sometimes choose to show a extracted text view. When users type in this mode the extracted text view should be updated with the same text that is in the custom view.

I have not been able to implement the extracted text view functionality. (Here are some things I've tried.)

I also have not been able to find any clear documentation or full examples of how to do it. (Here are some of the better things I've read: one, two, three, four).

MCVE

I've created the most basic custom editor that I can. The following gif shows the functionality. It can receive text from the keyboard but it does not update the extracted text view in landscape orientation. Thus you can't see the updated text unless you dismiss the keyboard.

enter image description here

MyCustomView.java

public class MyCustomView extends View {

    SpannableStringBuilder mText;
    Paint mPaint;

    public MyCustomView(Context context) {
        this(context, null, 0);
    }

    public MyCustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setFocusableInTouchMode(true);
        mText = new SpannableStringBuilder();
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setTextSize(60);
        mPaint.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText(mText, 0, mText.length(), 50, 100, mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            if (imm == null) return false;
            imm.showSoftInput(this, InputMethodManager.SHOW_FORCED);
        }
        return true;
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
        return new MyInputConnection(this, true);
    }
}

MyInputConnection.java

public class MyInputConnection extends BaseInputConnection {

    private MyCustomView customView;

    MyInputConnection(View targetView, boolean fullEditor) {
        super(targetView, fullEditor);
        customView = (MyCustomView) targetView;
    }

    @Override
    public Editable getEditable() {
        return customView.mText;
    }

    @Override
    public boolean commitText(CharSequence text, int newCursorPosition) {
        boolean returnValue = super.commitText(text, newCursorPosition);
        customView.invalidate();
        return returnValue;
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <net.example.extractedtext.MyCustomView
        android:id="@+id/myCustomView"
        android:background="@android:color/holo_blue_bright"
        android:layout_margin="50dp"
        android:layout_width="300dp"
        android:layout_height="150dp"
        android:layout_centerHorizontal="true"
        />

</RelativeLayout>

Summary

I am looking for a canonical answer that describes and gives an example of how to implement extracted text updates for a custom editor view.

If I figure it out myself, I will add my own answer. Until then the best I have been able to do it just disable extracted text altogether. This is not ideal.

Brighten answered 14/6, 2018 at 4:10 Comment(0)
U
1

You can use inputMethodManager.updateExtractedText(view, token, extractedText) for that.

First parameter of this method is easy. You can pass instance of your CustomView there. Last one also. Just create ExtractedText and set its fields like this.

ExtractedText extractedText = new ExtractedText();
extractedText.text = "sample text";

More difficult is to pass correct token. To get to know right value of this parameter you can override methodgetExtractedText(ExtractedTextRequest request, int flags) inside your MyInputConnection class (token is stored in request object).

@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
        currentToken = request.token;
        return new ExtractedText();
}

I return empty ExtractedText object from this method to make view active (by default text looks like hint).

You can find my solution here https://github.com/ljarka/ExtractedText

Extracted text preview

Unclear answered 22/6, 2018 at 18:6 Comment(2)
The key was the token. I thought I could ignore it and just set it to 0, but it actually needs to be set.Brighten
For how I used your answer to solve my larger problem, see this answer.Brighten
O
0

You can just remove the extracted view. I tried it and your custom view is visible while the keyboard is being used.

@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
    outAttrs.inputType = InputType.TYPE_CLASS_TEXT;

    //remove extract view
    outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI;

    return new MyInputConnection(this, true);
}
Oddfellow answered 17/6, 2018 at 13:20 Comment(1)
At the end of my question I also have a link to an answer that tells how to disable extracted text. However, I don't want to disable it. I want to learn how to use it, because in certain situations the keyboard takes up too much room and the text can't be seen clearly. It would be better to use the extracted text view in these situations.Brighten

© 2022 - 2024 — McMap. All rights reserved.