ユーザーがソフトキーボードを閉じたときの検出


114

ビューにEditTextウィジェットがあります。ユーザーがEditTextウィジェットを選択すると、いくつかの指示が表示され、ソフトキーボードが表示されます。

OnEditorActionListenerを使用して、ユーザーがテキスト入力を完了したことを検出し、キーボードを閉じ、指示を非表示にして、何らかのアクションを実行します。

私の問題は、ユーザーがBACKキーを押してキーボードを閉じたときです。OSはキーボードを閉じますが、私の指示(非表示にする必要があります)はまだ表示されています。

OnKeyDownをオーバーライドしようとしましたが、[戻る]ボタンを使用してキーボードを非表示にしたときに呼び出されないようです。

EditTextウィジェットでOnKeyListenerを設定しようとしましたが、それも呼び出されないようです。

ソフトキーボードが閉じられたことをどのように検出できますか?

回答:


160

私はこれを行う方法を知っています。EditTextをサブクラス化して実装します。

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
    // Do your thing.
    return true;  // So it is not propagated.
  }
  return super.dispatchKeyEvent(event);
}

カスタムビューの使用方法に関するリンクは次のとおりです(EditTextをサブクラス化する場合):http ://developer.android.com/guide/topics/ui/custom-components.html


2
ハードウェアキーボードを使用しているAndroidユーザーから、何らかの方法でキーを押すのを妨げるという報告を受けています。現在、追加情報はありません。
esilver

私はいくつかの解決策を探してきましたが、これは断然最高です!
Friesgaard、2014年

10
待って待って待って、私はこれを3回目に見ただけです-スーパーコールはするべきではありませんonKeyPreImeか?それともそうでないのには特別な理由がありますか?
Erhannis 14

EditTextをサブクラス化できない場合(SearchViewなど)を除いて、便利に見えます。これは、キーボードが閉じられたときに空の場合にSearchViewを非表示にしようとするときの問題です。なぜAndroidの人々はこの種のことのためにいくつかの素晴らしいOSK APIを提供しないのか不思議に思う必要があります。
tbm 2014

2
@tbmで同様の効果を達成するにSearchViewは、stackoverflow.com
Yan Cheng

124

ジェイ、あなたの解決策は良いです!おかげで:)

public class EditTextBackEvent extends EditText {

    private EditTextImeBackListener mOnImeBack;

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

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

    public EditTextBackEvent(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && 
            event.getAction() == KeyEvent.ACTION_UP) {
            if (mOnImeBack != null) 
                mOnImeBack.onImeBack(this, this.getText().toString());
        }
        return super.dispatchKeyEvent(event);
    }

    public void setOnEditTextImeBackListener(EditTextImeBackListener listener) {
        mOnImeBack = listener;
    }

}

public interface EditTextImeBackListener {
    public abstract void onImeBack(EditTextBackEvent ctrl, String text);
}

私たちも検出KeyEvent.ACTION_UPしたい特別な理由はありますか?
Cheok Yan Cheng 2015

2
@CheokYanChengこれは、ユーザーのアクションは通常、ボタンを離したときに有効になり、ボタンを押し始めたときに有効にならないためです。
jayeffkay

3
android.support.v7.widget.AppCompatEditText着色のために拡張することを確認してください。
Sanvywell

延長:AppCompatEditTextandroidx
COYG

すごい!私はあなたの解決策を一般化するためだけに改善を提案するでしょう。onKeyPreImeから引数を「そのまま」リスナーに渡します。これにより、必要に応じてさまざまな方法でロジックを実装できます。
marcRDZ

17

super.onKeyPreIme()を呼び出して、Jayのソリューションにわずかな変更を加えました。

_e = new EditText(inflater.getContext()) {
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK){
            cancelTextInput();
        }
        return super.onKeyPreIme(keyCode, event);
    }
};

すばらしい解決策、ジェイ、+ 1!


14

これは、キーボードが表示されているかどうかを検出するためのカスタムEditTextです

/**
 * Created by TheFinestArtist on 9/24/15.
 */
public class KeyboardEditText extends EditText {

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

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

    public KeyboardEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        if (listener != null)
            listener.onStateChanged(this, true);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
                && event.getAction() == KeyEvent.ACTION_UP) {
            if (listener != null)
                listener.onStateChanged(this, false);
        }
        return super.onKeyPreIme(keyCode, event);
    }

    /**
     * Keyboard Listener
     */
    KeyboardListener listener;

    public void setOnKeyboardListener(KeyboardListener listener) {
        this.listener = listener;
    }

    public interface KeyboardListener {
        void onStateChanged(KeyboardEditText keyboardEditText, boolean showing);
    }
}

8

2019年です...
Kotlinでよりきちんとしたソリューションを作成しました

1.拡張関数を作成します。

fun Activity.addKeyboardToggleListener(onKeyboardToggleAction: (shown: Boolean) -> Unit): KeyboardToggleListener? {
    val root = findViewById<View>(android.R.id.content)
    val listener = KeyboardToggleListener(root, onKeyboardToggleAction)
    return root?.viewTreeObserver?.run {
        addOnGlobalLayoutListener(listener)
        listener
    }
}

2.トグルリスナーは次のとおりです:

open class KeyboardToggleListener(
        private val root: View?,
        private val onKeyboardToggleAction: (shown: Boolean) -> Unit
) : ViewTreeObserver.OnGlobalLayoutListener {
    private var shown = false
    override fun onGlobalLayout() {
        root?.run {
            val heightDiff = rootView.height - height
            val keyboardShown = heightDiff > dpToPx(200f)
            if (shown != keyboardShown) {
                onKeyboardToggleAction.invoke(keyboardShown)
                shown = keyboardShown
            }
        }
    }
}

fun View.dpToPx(dp: Float) = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics).roundToInt()

3.次のように単純なアクティビティで使用します

addKeyboardToggleListener {shown ->
          // hurray! Now you know when the keyboard is shown and hidden!!
      }

Kotlinソリューションをありがとう。私がそれを実装したとき、私はそれが1つのキーボードの変更と起動時にリスナーを複数回起動することに気付きました。開いている/開いていない状態を保存し、値が実際に異なる場合にのみリスナーを呼び出す必要がありました。
RandomEngy

@RandomEngyがKeyboardToggleListenerで修正しました。
お知らせ

4

Edittextを拡張するクラスを作成し、そのedittextをコードで使用するだけで、カスタムedittextで次のメソッドをオーバーライドするだけです。

@Override
 public boolean onKeyPreIme(int keyCode, KeyEvent event) {
 if (keyCode == KeyEvent.KEYCODE_BACK) {

    //Here it catch all back keys
    //Now you can do what you want.

} else if (keyCode == KeyEvent.KEYCODE_MENU) {
    // Eat the event
    return true;
}
return false;}

キーボードが開いたときに検出する方法はありますか?
powder366 2016年

3

キーリスナーを使用したソリューションを次に示します。なぜこれが機能するのかはわかりませんが、カスタムEditTextでonKeyPreImeを単にオーバーライドするだけでOnKeyListenerが機能します。

SomeClass.java

customEditText.setOnKeyListener((v, keyCode, event) -> {
            if(event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (keyCode) {
                    case KeyEvent.KEYCODE_BACK:
                        getPresenter().onBackPressed();
                        break;
                }
            }
            return false;
        }); 

CustomEditText.java

@Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        return super.dispatchKeyEvent(event);
    }

3

@olivier_sdgの回答を使用しますが、Kotlinに変換します。

class KeyboardEditText : AppCompatEditText {

    var listener: Listener? = null
  
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  
    override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
        if (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
            listener?.onImeBack(this)
        }
        return super.dispatchKeyEvent(event)
    }
  
    interface Listener {
        fun onImeBack(editText: KeyboardEditText)
    }

}

使用法:

keyboardEditText.listener = object : KeyboardEditText.Listener {
    override fun onImeBack(editText: KeyboardEditText) {
        //Back detected
    }
}

2

Xamarinで同じことを行うことを検討している人のために、少し異なるので、上位の回答のいくつかを翻訳しました。ここで要点を作成しましたが、要約すると、カスタムEditTextを作成し、次のOnKeyPreImeようにオーバーライドします。

public class CustomEditText : EditText
{
    public event EventHandler BackPressed;

    // ...

    public override bool OnKeyPreIme([GeneratedEnum] Keycode keyCode, KeyEvent e)
    {
        if (e.KeyCode == Keycode.Back && e.Action == KeyEventActions.Up)
        {
            BackPressed?.Invoke(this, new EventArgs());
        }

        return base.OnKeyPreIme(keyCode, e);
    }
}

...そして次にビューで...

editText = FindViewById<CustomEditText>(Resource.Id.MyEditText);
editText.BackPressed += (s, e) => 
{
    // <insert code here>
};

これは単純な例ですが、イベントハンドラで匿名メソッドを使用しないことをお勧めします。これらはメモリリークを引き起こし、多くの人がここにある例を使用して、これを認識せずに実行します。出典:docs.microsoft.com/en-us/xamarin/cross-platform/deploy-test/...
ジャレド

0

hideSoftInputFromWindow キーボードが閉じるとtrueを返し、その値を使用してAndroidでキーボードが閉じることを検出します

InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

        if (imm.hideSoftInputFromWindow(findFocus().getWindowToken(),
                InputMethodManager.HIDE_NOT_ALWAYS)) {
            //keyboard is closed now do what you need here
        }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.