ソフトキーボードから戻るボタンをインターセプトする


84

いくつかの入力フィールドがあるアクティビティがあります。アクティビティが開始されると、ソフトキーボードが表示されます。戻るボタンを押すとソフトキーボードが閉じ、アクティビティを閉じるには、もう一度戻るボタンを押す必要があります。

だから質問:カスタムを作成せずに、戻るボタンをインターセプトしてソフトキーボードを閉じ、戻るボタンを1回押すだけでアクティビティを終了することは可能InputMethodServiceですか?

PS他の場合に戻るボタンを傍受する方法を知っています:onKeyDown()またはonBackPressed()、この場合は機能しません:戻るボタンを2回押すだけが傍受されます。

回答:


76

はい、キーボードの表示と非表示を切り替えたり、戻るボタンへの呼び出しを傍受したりすることは完全に可能です。APIでこれを行う直接的な方法はないと述べられているため、少し余分な労力がかかります。重要なのはboolean dispatchKeyEventPreIme(KeyEvent)、レイアウト内でオーバーライドすることです。私たちがしているのは、レイアウトを作成することです。アクティビティのベースであるRelativeLayoutを選択しました。

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

アクティビティ内で、入力フィールドを設定し、setActivity(...)関数を呼び出します。

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

明らかに、initInputField()関数は入力フィールドを設定します。また、Enterキーで機能(私の場合は検索)を実行できます。

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

したがってonBackPressed()、レイアウト内でが呼び出されると、キーボードを非表示にするなど、必要な操作を実行できます。

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

とにかく、これが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);
    }
}

残念ながら、私はすべてのクレジットを取ることはできません。クイックSearchDialogボックスのAndroidソースをチェックすると、アイデアがどこから来たのかがわかります。


2
これは非常にうまく機能し、実装するのはそれほど難しくありませんでした(少し怖いように見えますが)。
セザール

どうもありがとうございました。古いバージョンのAndroidでそのようなものを動作させる方法があればいいのにと思います。
zidarsk8 2011

3
にキャストgetContext()するだけでコードを簡略化できますActivity。もちろん、レイアウトのコンテキストが問題のアクティビティである場合。しかし、それができなかったかどうかはわかりません。
mxcl 2012年

3
staticmSearchActivity-問題によって悪臭がします。これらのうち2つをレイアウトしている場合はどうなりますか?代わりに非静的変数を使用してください。
ポインタヌル

チャームのように機能します。ただし、静的なアクティビティを使用するのではなく、ローカルの静的インターフェイスを使用して、内部アクティビティから実装を設定する方がよいと思います...
SpiralDev 2016

80

この場合、onKeyDown()およびonBackPressed()は機能しません。onKeyPreImeを使用する必要があります。

最初に、EditTextを拡張するカスタム編集テキストを作成する必要があります。次に、KeyEvent.KEYCODE_BACKを制御するonKeyPreImeメソッドを実装する必要があります。この後、問題を解決するのに十分なバックプレスを1回押します。このソリューションは私にとって完璧に機能します。

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

XMLで

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

あなたの活動で

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

4
この答えが大好きです。これをフラグメントでも使用し(常に表示されるとは限らない検索バー内で)、editTextをオーバーライドすることを提案することで、この動作を必要な場合にのみ簡単にカプセル化します
Jawad

2
みんなへの注意:これにクイックインターフェースを追加すると、人生がとても良くなり、幸いなことにとても簡単にできます
Jawad 2015

mainActivity.javaにあるonKeyPreIme()でメソッドを呼び出す方法は?
Abhigyan 2015

アクティビティでonKeyPreImeを呼び出す必要はありません。アクティビティでCustomEditTextを使用する場合、キーボードのいずれかのボタンを押すとメソッドがトリガーされます。
アリカンテメル2015

1
実装がはるかに簡単なので、これが最良の答えだと思います。実際、クラス「CustomEditText」をコピーして(そしてXMLで編集テキストの名前を変更して)、// TODO領域で必要なものを作成する必要があります。アクティビティを閉じたくない場合は、TODO領域に「returntrue」を追加するだけです。ありがとう!
Chrysotribax

10

LayoutClassのdispatchKeyEventPreImeメソッドをオーバーライドすることもうまく機能することがわかりました。メインのアクティビティを属性として設定し、事前定義されたメソッドを起動するだけです。

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

このレイアウトのxmlを見せてもらえますか?私はこのアプローチをチェックしようとしています、そして私は私のcustumレイアウトに表面ビューを持っています、しかしそれは何も傍受していません
ZZZ

基本的に、カスタムビューはxmlの周囲の要素です。<?xml version="1.0" encoding="utf-8"?> <view class="package.LinearLayoutGradient"...
Kirill Rakhman 2011

単一のビューを単純に拡張し、そのdispatchKeyEventPreIme()をオーバーライドする場合にも機能します。レイアウト自体を拡張する必要はありません。
faizal 2013

7

dispatchKeyEventをオーバーライドすることで成功しました:

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

キーボードを非表示にして、アクティビティを終了します。


これは、HTCDesireなどの特定のデバイスでのみ機能するようです。
Abhijit

5
samsung note 2では機能しません。ソフトキーボードが起動している場合、dispatchKeyEvent()は呼び出されません。
faizal 2013

さて、私はNexus 6エミュレーター(Lollipop 5.0)でこれを試しました->動作し、Note 4(CM12 Lollipop 5.0.2)->動作しませんでした、実際には最良の解決策ではありません-しかしとにかく..ありがとう:)
Martin Pfeffer

キーボードを閉じると、ボタンのクリックが消費されます
slott 2016年

4

ソフトキーボードをどのように表示していますか?

を使用している場合はInputMethodManager.showSoftInput()、を渡してResultReceiver実装onReceiveResult()して処理してみてくださいRESULT_HIDDEN

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


良さshowSoftInput()そうにtoggleSoftInput()
聞こえ

1
私にはうまくいきません。onReceiveResult()はRESULT_SHOWNで呼び出されますが、後でRESULT_HIDDENで呼び出されることはありません
Yulia Rogovaya 2011

2

私も同じ問題を抱えていましたが、戻るキーの押下を傍受することで問題を回避しました。私の場合(HTC Desire、Android 2.2、アプリケーションAPIレベル4)、キーボードを閉じてすぐにアクティビティを終了します。これがあなたにとってもうまくいかない理由がわからない:

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

これはサムスンギャラクシーノート2では機能しません。ソフトキーボードが表示され、戻るボタンが押された場合、onkeydown()またはonbackpressed()はトリガーされません
faizal 2013

1

使用onKeyPreIme(int keyCode, KeyEvent event)のための方法およびチェックKeyEvent.KEYCODE_BACKイベントが。特別なコーディングをしなくても非常に簡単です。


0

BackPressed実装でこのコードを試してください(Androidのブロックバックボタン):

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

Androidソフトキーボードを閉じる/隠す@をご覧になることをお勧めします


1
私はすでにこれを試しましたが、それでも戻るボタンを2回押す必要があります。私が理解しているように、最初のクリックはソフトキーボードによって傍受されるため、onBackPressed()機能しません。そして、2番目のプレスプログラムがに分類された後でのみonBackPressed()
Sergey Glotov 2010

@Sergey Glotov私はこの問題について多くの提案を試み、最終的にはあまり良くない解決策を思いついたので、独自のSoftKeyBoardを実装する必要がありますが、Androidコミュニティがすぐにもっと良い解決策を発表することを願っています。
100rabh 2010

0

@mhradekソリューションの私のバージョン:

レイアウト

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ファイル

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

断片

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        (view as BazingaLayout).activity = activity
        super.onViewCreated(view, savedInstanceState)
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.