检测用户何时关闭软键盘


114

我的视图中有一个EditText小部件。当用户选择EditText小部件时,我显示一些说明,然后出现软键盘。

我使用OnEditorActionListener来检测用户何时完成了文本输入,并关闭了键盘,隐藏了指令并执行了一些操作。

我的问题是,当用户通过按BACK键关闭键盘时。操作系统关闭了键盘,但是我的指令(我需要隐藏)仍然可见。

我尝试覆盖OnKeyDown,但是当使用BACK按钮关闭键盘时,似乎没有被调用。

我尝试在EditText小部件上设置OnKeyListener,但这似乎也没有被调用。

如何检测何时关闭软键盘?

Answers:


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
等等等等,我只是第三次看着这个-超级电话不应该onKeyPreIme吗?还是有一个特殊的原因使它不是如此?
Erhannis 2014年

看起来很有用,除非EditText不能被子类化(例如在SearchView中)。当关闭键盘时尝试隐藏SearchView(如果为空)时,这是一个问题。我想知道为什么android人士不仅仅为这类事情提供一些不错的OSK API。
tbm

2
@tbm要达到类似的效果SearchView,请参阅stackoverflow.com/questions/9629313/...
卓晏城

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

3
确保扩展android.support.v7.widget.AppCompatEditText以进行着色。
Sanvywell,2016年

扩展:AppCompatEditText适用于androidx
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解决方案。虽然在实现时我注意到它会在一次键盘更改以及启动时多次触发监听器。我必须存储打开/未打开状态,并且仅在值实际不同时才调用侦听器。
RandomEngy

@RandomEngy在KeyboardToggleListener中修复了它。感谢注意到
利奥Droidcoder

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

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/…–
Jared,

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
        }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.