Detecting when user has dismissed the soft keyboard

I have an EditText widget in my view. When the user selects the EditText widget, I display some instructions and the soft keyboard appears.

I use an OnEditorActionListener to detect when the user has completed text entry and I dismiss the keyboard, hide the instructions and perform some action.

My problem is when the user dismisses the keyboard by pressing the BACK key. The OS dismisses the keyboard, but my instructions (which I need to hide) are still visible.

I've tried overriding OnKeyDown, but that doesn't seem to get called when the BACK button is used to dismiss the keyboard.

I've tried setting an OnKeyListener on the EditText widget, but that doesn't seem to get called either.

How can I detect when the soft keyboard is being dismissed?

84134 次浏览

我知道一种方法来做到这一点。子类化 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);
}

Here is a link on how to use your custom views (for when you subclass EditText): Http://developer.android.com/guide/topics/ui/custom-components.html

杰,你的解决方案很好! 谢谢:)

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

通过调用 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);
}
};

只需创建一个扩展 Edittext 的类并在代码中使用该编辑文本, you should just override the following method in the custom 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;}

下面是我的自定义 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);
}
}

这里有一个关键侦听器的解决方案。 我不知道为什么这个工作,但 OnKeyListener 工作,如果你只是纯粹覆盖 onKeyPreIme 在您的自定义 EditText。

Some Class.java

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

Java

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

Using @olivier_sdg's answer, but converted to 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
}
}

对于任何想在 Xamarin 做同样事情的人,我翻译了一些顶尖的答案,因为它们有点不同。我创建了一个 gist 给你,但总的来说,您创建了一个自定义 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>
};

现在是2019年了。
So I created a more neat solution with 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!!
}

当键盘关闭时,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
}

Koltin解决方案,工程在横向模式,以及(onKeyPreIme没有被调用,如果用户按 Done按钮在横向模式) :

class CustomEditText(context: Context, attrs: AttributeSet) : AppCompatEditText(context, attrs) {


override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
if ((event?.keyCode == KeyEvent.KEYCODE_ENTER) && (event?.action == KeyEvent.ACTION_UP)) {
if (hasFocus())
clearFocus()
}
return super.onKeyUp(keyCode, event)
}


override fun onKeyPreIme(keyCode: Int, event: KeyEvent?): Boolean {
if ((event?.keyCode == KeyEvent.KEYCODE_BACK) && (event?.action == KeyEvent.ACTION_UP)) {
if (hasFocus())
clearFocus()
}
return super.onKeyPreIme(keyCode, event)
}
}

enter image description here