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).
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.
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>
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With