Как захватить программную клавиатуру в виде? - PullRequest
44 голосов
/ 24 марта 2011

У меня есть подклассное представление, которое выскакивает на клавиатуре, когда он получает «прикосновение» в onTouchEvent. Это показывает это путем запроса фокуса, получения InputMethodManager и последующего вызова showSoftInput.

Теперь мне нужно выяснить, как перехватить нажатые буквы программной клавиатуры, при их нажатии . В настоящее время я получаю ответ только при нажатии кнопки «Далее / Готово» на программной клавиатуре.

Вот мой класс:

public class BigGrid extends View {

    private static final String TAG = "BigGrid";

    public BigGrid(Context context) {
        super(context);
        setFocusableInTouchMode(true); // allows the keyboard to pop up on
                                       // touch down

        setOnKeyListener(new OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                Log.d(TAG, "onKeyListener");
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    // Perform action on key press
                    Log.d(TAG, "ACTION_DOWN");
                    return true;
                }
                return false;
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        Log.d(TAG, "onTOUCH");
        if (event.getAction() == MotionEvent.ACTION_UP) {

            // show the keyboard so we can enter text
            InputMethodManager imm = (InputMethodManager) getContext()
                    .getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.showSoftInput(this, InputMethodManager.SHOW_FORCED);
        }
        return true;
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        Log.d(TAG, "onCreateInputConnection");

        BaseInputConnection fic = new BaseInputConnection(this, true);
        outAttrs.actionLabel = null;
        outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
        outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
        return fic;
    }

    @Override
    public boolean onCheckIsTextEditor() {
        Log.d(TAG, "onCheckIsTextEditor");
        return true;
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(R.color.grid_bg);
        // .
        // .
        // alot more drawing code...
        // .
    }
}

Клавиатура отображается, но мой onKeyListener срабатывает только при нажатии кнопки «Далее» на клавиатуре. Мне нужно, какой символ коснулся, чтобы я мог отобразить его в моем методе onDraw ().

Ответы [ 4 ]

41 голосов
/ 12 сентября 2011

На самом деле можно обрабатывать ключевые события самостоятельно, не выводя свое представление из TextView.

Для этого просто измените исходный код следующим образом:

1) Замените следующую строкув onCreateInputConnection():

outAttrs.inputType = InputType.TYPE_CLASS_TEXT;

с этим:

outAttrs.inputType = InputType.TYPE_NULL;

Согласно документации для InputType.TYPE_NULL: "Это должно быть истолковано как означающее, что целевое входное соединение не богато, ононе может обрабатывать и отображать такие вещи, как текст-кандидат или извлекать текущий текст, поэтому метод ввода должен будет работать в ограниченном режиме «генерации ключевых событий». *

2) Замените следующую строку в том же методе:

BaseInputConnection fic = new BaseInputConnection(this, true);

с этим:

BaseInputConnection fic = new BaseInputConnection(this, false);

Второй ложный аргумент переводит BaseInputConnection в режим «фиктивного», который также требуется для того, чтобынеобработанные ключевые события для отправки на ваш взгляд.В коде BaseInputConnection вы можете найти несколько комментариев, таких как следующее: «только если фиктивный режим отправляет ключевое событие для нового текста и текущий редактируемый буфер очищен».

Я использовал этот подход дляпусть программная клавиатура отправляет необработанные события в мое представление, производное от LinearLayout (т. е. представление, не производное от TextView), и может подтвердить, что оно работает.

Конечно, если вам это не нужночтобы установить значение IME_ACTION_DONE imeOptions для отображения кнопки «Готово» на клавиатуре, вы можете просто полностью удалить переопределения onCreateInputConnection() и onCheckIsTextEditor(), и необработанные события будут по умолчанию отправляться в ваш просмотр, поскольку входное соединение отсутствуетспособный к более сложной обработке, был бы определен.

Но, к сожалению, кажется, что не существует простого способа настройки атрибутов EditorInfo без переопределения этих методов и предоставления объекта BaseInputConnection, и как только вы это сделаете, вам придется затормозить обработку, выполняемую этимобъект, как описано выше, если вы хотите еще раз получить необработанные ключевые события.

ПРЕДУПРЕЖДЕНИЕ. В некоторых последних версиях клавиатуры LatinIME по умолчанию, поставляемых с Android (Google Keyboard), были добавлены две ошибки, которые могут повлиять на обработку событий клавиатуры (как описано выше), когда эта клавиатура используется.Я разработал некоторые обходные пути на стороне приложения, с примером кода, который, кажется, обходит эти проблемы.Чтобы просмотреть эти обходные пути, см. Следующий ответ:

Android - не удается захватить клавишу возврата / удалить нажатие в программном обеспечении.клавиатура

21 голосов
/ 16 апреля 2011

Оказывается, что мне действительно нужно было создать подкласс TextView и использовать addTextChangedListener (), чтобы добавить мою собственную реализацию TextWatcher для прослушивания событий программных клавиш.Я не мог найти способ сделать это с простым старым видом.

Еще одна вещь, для тех, кто попробует эту технику;TextView не может редактировать текст по умолчанию, поэтому, если вы хотите сделать свою реализацию редактируемой (вместо того, чтобы создавать подклассы EditText, чего я не хотел делать), вы также должны сделать пользовательский InputConnection, что-то вроде следующего:

 /**
 * MyInputConnection
 * BaseInputConnection configured to be editable
 */
class MyInputConnection extends BaseInputConnection {
    private SpannableStringBuilder _editable;
    TextView _textView;

    public MyInputConnection(View targetView, boolean fullEditor) {
        super(targetView, fullEditor);
        _textView = (TextView) targetView;
    }

    public Editable getEditable() {
        if (_editable == null) {
            _editable = (SpannableStringBuilder) Editable.Factory.getInstance()
            .newEditable("Placeholder");
        }
        return _editable;
    }

    public boolean commitText(CharSequence text, int newCursorPosition) {
        _editable.append(text);
        _textView.setText(text);
        return true;
    }
}

Затем вы переопределяете onCheckisTextEditor и onCreateInputConnection следующим образом:

 @Override
 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
     outAttrs.actionLabel = null;
     outAttrs.label = "Test text";
     outAttrs.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
     outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;

     return new MyInputConnection(this, true);
 }

 @Override
 public boolean onCheckIsTextEditor() {
     return true;
 }

После этого у вас должен быть View, который может слушать программную клавиатуру, и вы можете делать все, что угоднохочу с помощью клавиш ввода значений.

16 голосов
/ 03 июня 2017

Согласно документации , View (редактор) получает команды от клавиатуры (IME) через InputConnection и отправляет команды на клавиатуру через InputMethodManager.

enter image description here

Я покажу весь код ниже, но вот шаги.

1.Сделайте так, чтобы клавиатура отображалась

Поскольку представление отправляет команду на клавиатуру, ему необходимо использовать InputMethodManager.В качестве примера мы скажем, что при касании вида он покажет клавиатуру (или скроет ее, если он уже отображается).

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_UP) {
        InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY);
    }
    return true;
}

Вид также должен иметь setFocusableInTouchMode(true) установить ранее.

2.Получать ввод с клавиатуры

Чтобы представление получало ввод с клавиатуры, необходимо переопределить onCreateInputConnection().Это возвращает InputConnection, который клавиатура использует для связи с представлением.

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

outAttrs указывает, какую клавиатуру запрашивает представление.Здесь мы просто запрашиваем нормальную текстовую клавиатуру.Выбор TYPE_CLASS_NUMBER приведет к отображению цифровой клавиатуры (если доступно).Есть много других вариантов.См. EditorInfo.

Вы должны вернуть InputConnection, который обычно является пользовательским подклассом BaseInputConnection.В этом подклассе вы предоставляете ссылку на вашу редактируемую строку, которую клавиатура будет обновлять.Поскольку SpannableStringBuilder реализует интерфейс Editable, мы будем использовать его в нашем базовом примере.

public class MyInputConnection extends BaseInputConnection {

    private SpannableStringBuilder mEditable;

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

    @Override
    public Editable getEditable() {
        return mEditable;
    }
}

Все, что мы здесь сделали, - это предоставили вводсоединение со ссылкой на текстовую переменную в нашем пользовательском представлении.BaseInputConnection позаботится о редактировании этого mText.Это вполне может быть все, что вам нужно сделать.Однако вы можете проверить исходный код и посмотреть, какие методы говорят «реализация по умолчанию», особенно «реализация по умолчанию ничего не делает».Это другие методы, которые вы можете переопределить в зависимости от степени вовлеченности вашего представления редактора.Вам также следует просмотреть все имена методов в документации .Ряд из них имеют заметки для «редакторов».Обратите на это особое внимание.

Некоторые клавиатуры по какой-то причине не посылают определенный ввод через InputConnection (например, удаление , ввод и некоторые клавиши цифровой клавиатуры ).Для тех, кого я добавил OnKeyListener.Протестировав эту настройку на пяти разных программных клавиатурах, казалось, все работает.Дополнительные ответы, относящиеся к этому, приведены здесь:

Полный код проекта

Вот мой полный пример для справки.

enter image description here

MyCustomView.java

public class MyCustomView extends View {

    SpannableStringBuilder mText;

    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();

        // handle key presses not handled by the InputConnection
        setOnKeyListener(new OnKeyListener() {
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {

                    if (event.getUnicodeChar() == 0) { // control character

                        if (keyCode == KeyEvent.KEYCODE_DEL) {
                            mText.delete(mText.length() - 1, mText.length());
                            Log.i("TAG", "text: " + mText + " (keycode)");
                            return true;
                        }
                        // TODO handle any other control keys here
                    } else { // text character
                        mText.append((char)event.getUnicodeChar());
                        Log.i("TAG", "text: " + mText + " (keycode)");
                        return true;
                    }
                }
                return false;
            }
        });
    }

    // toggle whether the keyboard is showing when the view is clicked
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY);
        }
        return true;
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
        // outAttrs.inputType = InputType.TYPE_CLASS_NUMBER; // alternate (show number pad rather than text)
        return new MyInputConnection(this, true);
    }
}

MyInputConnection.java

public class MyInputConnection extends BaseInputConnection {

    private SpannableStringBuilder mEditable;

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

    @Override
    public Editable getEditable() {
        return mEditable;
    }

    // just adding this to show that text is being committed.
    @Override
    public boolean commitText(CharSequence text, int newCursorPosition) {
        boolean returnValue = super.commitText(text, newCursorPosition);
        Log.i("TAG", "text: " + mEditable);
        return returnValue;
    }
}

activity_main.xml

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

    <com.example.editorview.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>

В коде MainActivity.java нет ничего особенного.

Пожалуйста, оставьте комментарий, если это не работает длявы.Я использую это базовое решение для пользовательского EditText в библиотеке, которую я создаю, и если есть какие-либо крайние случаи, в которых это не работает, я хочу знать.Если вы хотите просмотреть этот проект, пользовательское представление будет здесь .Это InputConnection здесь здесь .

Связанные

4 голосов
/ 14 апреля 2011

Насколько я понимаю, ваш onKeyListener будет получать только события аппаратной клавиатуры.

Вы получите доступ ко всем событиям клавиш ввода, если переопределите boolean View.onKeyPreIme(int keyCode, KeyEvent event)

Таким образом, вы можете выбрать обработку события события ключа [ DOWN | MULTIPLE | UP ] и вернуть true или разрешить обычной обработке ключа справиться с ним (return super.onKeyPreIme())

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...