Wie erfasse ich weiche Tastatureingaben in einer Ansicht?

Ich habe eine untergeordnete Ansicht, die die Tastatur öffnet, wenn sie in onTouchEvent ein “Aufbessern” erhält. Es zeigt dies an, indem es den Fokus anfordert, den InputMethodManager abruft und dann showSoftInput aufruft.

Jetzt muss ich herausfinden, wie man die abgetippten Buchstaben der Soft-Tastatur erfasst, wenn sie gedrückt werden . Ich bekomme derzeit nur eine Antwort, wenn die Next / Done-Taste auf der Soft-Tastatur gedrückt wird.

Hier ist meine class:

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... // . } } 

Die Tastatur zeigt an, aber mein onKeyListener wird nur ausgetriggers, wenn ich den “Next” -Button auf der Tastatur drücke. Ich brauche, welches Zeichen angetippt wird, damit ich es in meiner onDraw () Methode anzeigen kann.

Es ist tatsächlich möglich, die Schlüsselereignisse selbst zu behandeln, ohne Ihre Ansicht von TextView abzuleiten.

Um dies zu tun, ändern Sie einfach Ihren ursprünglichen Code wie folgt:

1) Ersetzen Sie die folgende Zeile in onCreateInputConnection():

 outAttrs.inputType = InputType.TYPE_CLASS_TEXT; 

mit diesem:

 outAttrs.inputType = InputType.TYPE_NULL; 

In der Dokumentation zu InputType.TYPE_NULL: “Dies sollte so interpretiert werden, dass die InputType.TYPE_NULL: nicht reich ist. Sie kann beispielsweise den Text nicht verarbeiten und InputType.TYPE_NULL: oder den aktuellen Text InputType.TYPE_NULL: Daher muss die Eingabemethode in a ausgeführt werden eingeschränkter Modus ‘Schlüsselereignisse generieren’. ”

2) Ersetzen Sie die folgende Zeile in der gleichen Methode:

 BaseInputConnection fic = new BaseInputConnection(this, true); 

mit diesem:

 BaseInputConnection fic = new BaseInputConnection(this, false); 

Das falsche zweite Argument setzt die BaseInputConnection in den “Dummy” -Modus, der auch benötigt wird, damit die Raw-Key-Ereignisse an Ihre Ansicht gesendet werden. Im BaseInputConnection-Code finden Sie mehrere Kommentare wie die folgenden: “Nur im Dummy-Modus wird ein Schlüsselereignis für den neuen Text gesendet und der aktuelle bearbeitbare Puffer gelöscht.”

Ich habe diesen Ansatz verwendet, damit die Soft-Tastatur unformatierte Ereignisse an eine von mir abgeleitete Ansicht sendet, die von LinearLayout abgeleitet ist (dh eine Ansicht, die nicht von TextView abgeleitet wurde) und bestätigen kann, dass sie funktioniert.

Wenn Sie den Wert für IME_ACTION_DONE imeOptions nicht so IME_ACTION_DONE müssen, dass auf der Tastatur die Schaltfläche ” IME_ACTION_DONE angezeigt wird, können Sie die onCreateInputConnection() und onCheckIsTextEditor() vollständig entfernen und die onCreateInputConnection() dann an Ihre Ansicht gesendet Standardmäßig wurde keine Eingangsverbindung definiert, die eine komplexere Verarbeitung ermöglichen würde.

Aber leider scheint es keine einfache Möglichkeit zu geben, die EditorInfo-Attribute zu konfigurieren, ohne diese Methoden zu überschreiben und ein BaseInputConnection-Objekt zur Verfügung zu stellen. Sobald Sie dies getan haben, müssen Sie die von diesem Objekt ausgeführte Verarbeitung wie oben beschrieben dutydown möchte noch einmal die rohen Schlüsselereignisse erhalten.

WARNUNG: In einigen aktuellen Versionen der standardmäßigen LatinIME-Tastatur, die mit Android (Google Keyboard) ausgeliefert wird, wurden zwei Fehler eingeführt, die die Tastaturereignisverarbeitung (wie oben beschrieben) beeinflussen können, wenn diese Tastatur verwendet wird. Ich habe einige Problemumgehungen auf der App Seite mit Beispielcode entwickelt, die diese Probleme zu umgehen scheinen. Zeigen Sie diese Problemumgehungen in der folgenden Antwort an:

Android – kann Backspace nicht erfassen / löschen in Soft drücken. Tastatur

Es stellte sich heraus, dass ich tatsächlich TextView und die Verwendung addTextChangedListener () ableiten musste, um eine eigene Implementierung von TextWatcher hinzuzufügen, um Softkey-Ereignisse anzuhören. Ich konnte keinen Weg finden, dies mit einem einfachen alten View zu tun.

Eine andere Sache, für diejenigen, die diese Technik versuchen werden; TextView ist nicht in der Lage, Text standardmäßig zu bearbeiten. Wenn Sie also Ihre Implementierung editierbar machen möchten (anstatt EditText abzuleiten, was ich nicht tun wollte), müssen Sie auch eine benutzerdefinierte InputConnection erstellen, etwa so:

  /** * 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; } } 

Dann überschreiben Sie onCheckisTextEditor und onCreateInputConnection mit etwas wie dem folgenden:

  @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; } 

Danach sollten Sie eine Ansicht haben, die die Soft-Tastatur hören kann und Sie können mit den Tasteneingabewerten machen, was Sie wollen.

Gemäß der Dokumentation empfängt ein View (Editor) Befehle von der Tastatur (IME) über eine InputConnection und sendet Befehle über einen InputMethodManager an die Tastatur.

Bildbeschreibung hier eingeben

Ich werde den gesamten Code unten zeigen, aber hier sind die Schritte.

1. Lassen Sie die Tastatur erscheinen

Da die Ansicht einen Befehl an die Tastatur sendet, muss ein InputMethodManager . Für das Beispiel werden wir sagen, dass wenn die Ansicht angetippt wird, wird die Tastatur angezeigt (oder ausgeblendet, wenn sie bereits angezeigt wird).

 @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; } 

Die Ansicht muss auch setFocusableInTouchMode(true) zuvor festgelegt haben.

2. Empfangen Sie Eingaben von der Tastatur

Damit die Ansicht Eingaben von der Tastatur empfängt, muss onCreateInputConnection() überschrieben werden. Dies gibt die InputConnection , mit der die Tastatur mit der Ansicht kommuniziert.

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

Die outAttrs geben an, welche Art von Tastatur die Ansicht anfordert. Hier fordern wir nur eine normale Texttastatur an. Wenn Sie TYPE_CLASS_NUMBER , wird ein Nummernblock angezeigt (falls verfügbar). Es gibt viele andere Möglichkeiten. Siehe EditorInfo .

Sie müssen eine InputConnection , bei der es sich normalerweise um eine benutzerdefinierte Unterklasse von BaseInputConnection . In dieser Unterklasse geben Sie einen Verweis auf Ihre bearbeitbare Zeichenfolge an, zu der die Tastatur Aktualisierungen vornehmen wird. Da ein SpannableStringBuilder die Editable Schnittstelle implementiert, werden wir das in unserem grundlegenden Beispiel verwenden.

 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; } } 

Alles, was wir hier getan haben, war, die Eingabeverbindung mit einem Verweis auf die Textvariable in unserer benutzerdefinierten Ansicht zu versehen. Die BaseInputConnection kümmert sich um die Bearbeitung dieses mText . Dies könnte sehr gut alles sein, was Sie tun müssen. Sie können jedoch den Quellcode auschecken und sehen, welche Methoden “Standardimplementierung” sagen, insbesondere “Standardimplementierung tut nichts”. Dies sind andere Methoden, die Sie möglicherweise überschreiben möchten, je nachdem, wie stark Ihre Editor-Ansicht sein wird. Sie sollten auch alle Methodennamen in der Dokumentation durchsehen . Einige von ihnen haben Notizen zu “Herausgeberautoren”. Achten Sie besonders auf diese.

Einige Tastaturen senden aus bestimmten InputConnection bestimmte Eingaben nicht über die InputConnection (z. B. Löschen , Eingabe und einige Zifferntasten ). Für diese habe ich einen OnKeyListener hinzugefügt. Beim Testen dieses Setups auf fünf verschiedenen Soft-Tastaturen schien alles zu funktionieren. Ergänzende Antworten dazu finden Sie hier:

  • Unterscheidung des Text-Keycodes vom Control-Keycode in Android KeyEvent
  • Brauchen Sie Tabelle der Schlüsselcodes für Android und Moderator
  • Eingabeverbindung für numerische Tastatur

Vollständiger Projektcode

Hier ist mein vollständiges Beispiel als Referenz.

Bildbeschreibung hier eingeben

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

MeineInputConnection.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"?>    

Es gibt nichts Besonderes im MainActivity.java-Code.

Bitte hinterlassen Sie einen Kommentar, wenn dies für Sie nicht funktioniert. Ich verwende diese grundlegende Lösung für einen benutzerdefinierten EditText in einer Bibliothek, die ich mache, und wenn es irgendwelche Randfälle gibt, in denen es nicht funktioniert, möchte ich wissen. Wenn Sie dieses Projekt anzeigen möchten, finden Sie hier die benutzerdefinierte Ansicht. Es ist InputConnection ist hier .

verbunden

  • So erstellen Sie eine benutzerdefinierte Systemtastatur
  • So erstellen Sie eine benutzerdefinierte In-App-Tastatur

Mein Verständnis ist, dass Ihr onKeyListener Hardware Keyboard Key Events nur bekommen wird.

Sie erhalten Zugriff auf alle Eingabe-Key-Ereignisse, wenn Sie den boolean View.onKeyPreIme(int keyCode, KeyEvent event)

Auf diese Weise können Sie die Schlüsselereignisaktion [ DOWN | MULTIPLE | UP ] [ DOWN | MULTIPLE | UP ] [ DOWN | MULTIPLE | UP ] und true , oder erlauben Sie der normalen Schlüsselverarbeitung, damit umzugehen ( return super.onKeyPreIme() )