Как реализовать извлеченный текст для пользовательского представления Android

Фон

Пользовательский вид редактора в Android может получать текст с системной клавиатуры через InputConnection, Я был в состоянии сделать такой взгляд успешно. Однако, когда устройство находится в ландшафтном режиме, система иногда выбирает отображение извлеченного текстового представления. Когда пользователи вводят в этом режиме, извлеченное текстовое представление должно обновляться тем же текстом, что и в пользовательском представлении.

Я не был в состоянии реализовать извлеченную функциональность представления текста. ( Вот некоторые вещи, которые я пробовал.)

Я также не смог найти какой-либо четкой документации или полных примеров того, как это сделать. (Вот некоторые из лучших вещей, которые я прочитал: раз, два, три, четыре).

MCVE

Я создал самый простой пользовательский редактор, который я могу. Следующий рисунок показывает функциональность. Он может получать текст с клавиатуры, но не обновляет извлеченное текстовое представление в альбомной ориентации. Таким образом, вы не сможете увидеть обновленный текст, пока не отпустите клавиатуру.

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>

Резюме

Я ищу канонический ответ, который описывает и дает пример того, как реализовать извлеченные текстовые обновления для пользовательского представления редактора.

Если я сам это выясню, я добавлю свой ответ. До этого момента лучшее, что мне удавалось сделать, это вообще отключить извлеченный текст. Это не идеально.

2 ответа

Решение

Ты можешь использовать inputMethodManager.updateExtractedText(view, token, extractedText) для этого.

Первый параметр этого метода прост. Вы можете передать экземпляр вашего CustomView там. Последний тоже. Просто создайте ExtractedText и установите его поля следующим образом.

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

Сложнее передать правильный токен. Чтобы узнать правильное значение этого параметра, вы можете переопределить методgetExtractedText(ExtractedTextRequest request, int flags)внутри вашего класса MyInputConnection (токен хранится в объекте запроса).

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

Я возвращаю пустой объект ExtractedText из этого метода, чтобы сделать представление активным (по умолчанию текст выглядит как подсказка).

Вы можете найти мое решение здесь https://github.com/ljarka/ExtractedText

Предварительный просмотр извлеченного текста

Вы можете просто удалить извлеченный вид. Я попробовал это, и ваш пользовательский виден во время использования клавиатуры.

@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);
}
Другие вопросы по тегам