Zurück-Taste von der Soft-Tastatur abfangen

84

Ich habe die Aktivität mit mehreren Eingabefeldern. Wenn die Aktivität gestartet wurde, wird eine weiche Tastatur angezeigt. Wenn die Zurück-Taste gedrückt wird, wird die Softtastatur geschlossen, und um die Aktivität zu schließen, muss ich die Zurück-Taste noch einmal drücken.

Die Frage ist also: Ist es möglich, die Zurück-Taste abzufangen, um die Softtastatur zu schließen und die Aktivität mit einem Druck auf die Zurück-Taste zu beenden, ohne eine benutzerdefinierte zu erstellen InputMethodService?

PS Ich weiß, wie man die Zurück-Taste in anderen Fällen abfängt: onKeyDown()oder onBackPressed()aber in diesem Fall funktioniert es nicht: Nur das zweite Drücken der Zurück-Taste wird abgefangen.

Sergey Glotov
quelle

Antworten:

76

Ja, es ist durchaus möglich, die Tastatur ein- und auszublenden und die Anrufe an die Zurück-Taste abzufangen. Es ist ein wenig zusätzlicher Aufwand, da bereits erwähnt wurde, dass es in der API keinen direkten Weg gibt, dies zu tun. Der Schlüssel ist das Überschreiben boolean dispatchKeyEventPreIme(KeyEvent)innerhalb eines Layouts. Wir erstellen unser Layout. Ich habe mich für RelativeLayout entschieden, da es die Basis meiner Aktivität war.

<?xml version="1.0" encoding="utf-8"?>
<com.michaelhradek.superapp.utilities.SearchLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.michaelhradek.superapp"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white">

Innerhalb unserer Aktivität richten wir unsere Eingabefelder ein und rufen die setActivity(...)Funktion auf.

private void initInputField() {
    mInputField = (EditText) findViewById(R.id.searchInput);        

    InputMethodManager imm = 
        (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 
            InputMethodManager.HIDE_IMPLICIT_ONLY);

    mInputField.setOnEditorActionListener(new OnEditorActionListener() {

        @Override
        public boolean onEditorAction(TextView v, int actionId,
                KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                performSearch();
                return true;
            }

            return false;
        }
    });

    // Let the layout know we are going to be overriding the back button
    SearchLayout.setSearchActivity(this);
}

Offensichtlich initInputField()richtet die Funktion das Eingabefeld ein. Es ermöglicht auch die Eingabetaste, um die Funktionalität auszuführen (in meinem Fall eine Suche).

@Override
public void onBackPressed() {
    // It's expensive, if running turn it off.
    DataHelper.cancelSearch();
    hideKeyboard();
    super.onBackPressed();
}

Wenn das onBackPressed()in unserem Layout aufgerufen wird, können wir tun, was wir wollen, wie die Tastatur verstecken:

private void hideKeyboard() {
    InputMethodManager imm = (InputMethodManager) 
        getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(mInputField.getWindowToken(), 0);
}

Wie auch immer, hier ist meine Überschreibung des RelativeLayout.

package com.michaelhradek.superapp.utilities;

import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.RelativeLayout;

/**
 * The root element in the search bar layout. This is a custom view just to 
 * override the handling of the back button.
 * 
 */
public class SearchLayout extends RelativeLayout {

    private static final String TAG = "SearchLayout";

    private static Activity mSearchActivity;;

    public SearchLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SearchLayout(Context context) {
        super(context);
    }

    public static void setSearchActivity(Activity searchActivity) {
        mSearchActivity = searchActivity;
    }

    /**
     * Overrides the handling of the back key to move back to the 
     * previous sources or dismiss the search dialog, instead of 
     * dismissing the input method.
     */
    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        Log.d(TAG, "dispatchKeyEventPreIme(" + event + ")");
        if (mSearchActivity != null && 
                    event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            KeyEvent.DispatcherState state = getKeyDispatcherState();
            if (state != null) {
                if (event.getAction() == KeyEvent.ACTION_DOWN
                        && event.getRepeatCount() == 0) {
                    state.startTracking(event, this);
                    return true;
                } else if (event.getAction() == KeyEvent.ACTION_UP
                        && !event.isCanceled() && state.isTracking(event)) {
                    mSearchActivity.onBackPressed();
                    return true;
                }
            }
        }

        return super.dispatchKeyEventPreIme(event);
    }
}

Leider kann ich nicht alle Kredite aufnehmen. Wenn Sie in der Android- Quelle nach dem schnellen SearchDialog-Feld suchen, sehen Sie, woher die Idee stammt.

mhradek
quelle
2
Dies funktionierte sehr gut und war nicht allzu schwer zu implementieren (obwohl es ein wenig beängstigend aussah).
Cesar
Vielen Dank dafür. Ich wünschte nur, ich hätte eine Möglichkeit, so etwas mit älteren Android-Versionen zum Laufen zu bringen.
Zidarsk8
3
Code kann durch einfaches Umwandeln getContext()in vereinfacht werden Activity. Vorausgesetzt, der Kontext des Layouts ist natürlich die betreffende Aktivität. Aber ich weiß nicht, ob es nicht sein könnte.
mxcl
3
statische mSearchActivity - das riecht nach einem Problem falsch. Was ist, wenn Sie zwei davon im Layout haben? Verwenden Sie stattdessen eine nicht statische Variable.
Zeiger Null
Klappt wunderbar. Aber ich denke, es ist besser, die lokale statische Schnittstelle zu verwenden und die Implementierung von innerhalb der Aktivität festzulegen, anstatt statische Aktivitäten zu verwenden ...
SpiralDev
79

onKeyDown () und onBackPressed () funktionieren in diesem Fall nicht. Sie müssen onKeyPreIme verwenden .

Zunächst müssen Sie einen benutzerdefinierten Bearbeitungstext erstellen, der EditText erweitert. Und dann müssen Sie onKeyPreIme Methode implementieren , die steuert KeyEvent.KEYCODE_BACK . Drücken Sie danach einmal genug, um Ihr Problem zu lösen. Diese Lösung funktioniert bei mir perfekt.

CustomEditText.java

public class CustomEditText extends EditText {

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            // User has pressed Back key. So hide the keyboard
            InputMethodManager mgr = (InputMethodManager)         

           getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);
            // TODO: Hide your view as you do it in your activity
        }
        return false;
}

In Ihrem XML

<com.YOURAPP.CustomEditText
     android:id="@+id/CEditText"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"/> 

In Ihrer Aktivität

public class MainActivity extends Activity {
   private CustomEditText editText;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      editText = (CustomEditText) findViewById(R.id.CEditText);
   }
}
Alican Temel
quelle
4
Ich liebe diese Antwort so sehr. Wenn Sie dies auch mit einem Fragment verwenden (innerhalb einer Suchleiste, die nicht immer angezeigt wird) und vorschlagen, den editText zu überschreiben, wird dieses Verhalten leicht nur dann eingekapselt, wenn es erforderlich ist
Jawad
2
Hinweis für alle: Das Hinzufügen einer schnellen Benutzeroberfläche macht das Leben so viel besser, und es ist zum Glück ziemlich einfach
Jawad
Wie rufe ich eine Methode in onKeyPreIme () auf, die sich in mainActivity.java befindet?
Abhigyan
Sie müssen onKeyPreIme in der Aktivität nicht aufrufen. Wenn Sie CustomEditText in Ihrer Aktivität verwenden, wird die Methode ausgelöst, wenn Sie eine beliebige Taste auf der Tastatur drücken.
Alican Temel
1
Ich denke, dies ist die beste Antwort, da die Implementierung viel einfacher ist. Tatsächlich müssen Sie nur die Klasse "CustomEditText" kopieren (und Ihren Edittext in XML umbenennen) und im Bereich // TODO das machen, was Sie wollen. Fügen Sie im TODO-Bereich einfach "return true" hinzu, wenn Sie Ihre Aktivität nicht schließen möchten. Vielen Dank !
Chrysotribax
10

Ich fand heraus, dass das Überschreiben der dispatchKeyEventPreIme- Methode der Layout-Klasse auch gut funktioniert. Legen Sie einfach Ihre Hauptaktivität als Attribut fest und starten Sie eine vordefinierte Methode.

public class LinearLayoutGradient extends LinearLayout {
    MainActivity a;

    public void setMainActivity(MainActivity a) {
        this.a = a;
    }

    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        if (a != null) {
            InputMethodManager imm = (InputMethodManager) a
                .getSystemService(Context.INPUT_METHOD_SERVICE);

            if (imm.isActive() && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                a.launchMethod;
            }
        }

        return super.dispatchKeyEventPreIme(event);
    }
}
Kirill Rakhman
quelle
Kannst du mir deine XML für dieses Layout zeigen? Ich versuche, diesen Ansatz zu überprüfen und ich habe Oberflächenansicht in meinem Custum-Layout, aber es fängt nichts ab
ZZZ
Grundsätzlich ist die benutzerdefinierte Ansicht das umgebende Element in der XML. <?xml version="1.0" encoding="utf-8"?> <view class="package.LinearLayoutGradient"...
Kirill Rakhman
Es funktioniert auch, wenn wir einfach eine einzelne Ansicht erweitern und ihre dispatchKeyEventPreIme () überschreiben. Es ist nicht erforderlich, das Layout selbst zu erweitern.
Faizal
7

Ich hatte Erfolg, indem ich dispatchKeyEvent überschrieb :

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        finish();
        return true;
    }
    return super.dispatchKeyEvent(event);
}

Es verbirgt die Tastatur und beendet die Aktivität.

jaseelder
quelle
Dies scheint nur auf bestimmten Geräten wie dem HTC Desire zu funktionieren.
Abhijit
5
funktioniert nicht mit Samsung Note 2. Das dispatchKeyEvent () wird nicht aufgerufen, wenn die Soft-Tastatur aktiv ist.
Faizal
Nun, ich habe dies mit dem Nexus 6 Emulator (Lollipop 5.0) versucht -> funktioniert und Note 4 (CM12 Lollipop 5.0.2) -> hat nicht so funktioniert, nicht wirklich die beste Lösung - aber trotzdem .. Danke :)
Martin Pfeffer
Das Klicken der Taste wird verbraucht, wenn die Tastatur geschlossen wird
Slott
4

Wie zeigen Sie die Softtastatur?

Wenn Sie verwenden InputMethodManager.showSoftInput(), können Sie versuchen, ein zu übergeben ResultReceiverund zu implementieren onReceiveResult(), um zu behandelnRESULT_HIDDEN

http://developer.android.com/reference/android/view/inputmethod/InputMethodManager.html

Superuser
quelle
Hört sich gut an, zeigt aber showSoftInput()keine Tastatur auf meinem Telefon und Emulator, also benutze ichtoggleSoftInput()
Sergey Glotov
1
Funktioniert bei mir nicht onReceiveResult () wird mit RESULT_SHOWN aufgerufen, aber nie später mit RESULT_HIDDEN
Yulia
2

Ich hatte das gleiche Problem, konnte es aber umgehen, indem ich den Druck auf die Zurück-Taste abfing. In meinem Fall (HTC Desire, Android 2.2, Anwendungs-API-Stufe 4) wird die Tastatur geschlossen und die Aktivität sofort beendet. Ich weiß nicht, warum das auch bei Ihnen nicht funktionieren sollte:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        onBackPressed();
        return true;
    }
    return super.onKeyUp(keyCode, event);
}

/**
 * Called when the activity has detected the user's press of the back key
 */
private void onBackPressed() {
    Log.e(TAG, "back pressed");
    finish();
}
whlk
quelle
Dies funktioniert nicht auf Samsung Galaxy Note 2. Die Onkeydown () oder Onbackpressed () werden nicht ausgelöst, wenn die Soft-Tastatur sichtbar ist und die Zurück-Taste gedrückt wird
faizal
1

Verwenden Sie die onKeyPreIme(int keyCode, KeyEvent event)Methode und suchen Sie nach KeyEvent.KEYCODE_BACKEreignissen. Es ist sehr einfach, ohne ausgefallene Codierung durchzuführen.

Umair
quelle
0

Versuchen Sie diesen Code in Ihrer BackPressed-Implementierung ( Block Back Button in Android ):

InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(myEditText.getWindowToken(), 0);

Ich empfehle Ihnen, einen Blick auf das Schließen / Ausblenden der Android-Softtastatur zu werfen

100rabh
quelle
1
Ich habe es bereits versucht, aber es muss noch zweimal auf den Zurück-Knopf gedrückt werden. Soweit ich weiß, wird der erste Klick von einer weichen Tastatur abgefangen und onBackPressed()funktioniert daher nicht. Und erst nach dem zweiten Presseprogramm fällt in die onBackPressed().
Sergey Glotov
@Sergey Glotov Ich habe viele Vorschläge für das Problem ausprobiert und schließlich eine nicht so gute Lösung gefunden, bei der ich ein eigenes SoftKeyBoard implementieren muss. Aber ich hoffe, dass die Android-Community bald eine viel bessere Lösung finden wird.
100 Rabh
0

meine Version der @ mhradek-Lösung:

Layout

class BazingaLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    ConstraintLayout(context, attrs, defStyleAttr) {

var activity: Activity? = null

override fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
    activity?.let {
        if (event.keyCode == KeyEvent.KEYCODE_BACK) {
            val state = keyDispatcherState
            if (state != null) {
                if (event.action == KeyEvent.ACTION_DOWN
                    && event.repeatCount == 0) {
                    state.startTracking(event, this)
                    return true
                } else if (event.action == KeyEvent.ACTION_UP && !event.isCanceled && state.isTracking(event)) {
                    it.onBackPressed()
                    return true
                }
            }
        }
    }
    return super.dispatchKeyEventPreIme(event)
}

}}

XML-Datei

<com... BazingaLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/grey">
 </com... BazingaLayout>

Fragment

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (view as BazingaLayout).activity = activity
        super.onViewCreated(view, savedInstanceState)
}
tasjapr
quelle