我的视图中有一个 EditText 小部件。当用户选择 EditText 小部件时,我会显示一些说明并出现软键盘。
我使用 OnEditorActionListener 来检测用户何时完成文本输入,然后我关闭键盘、隐藏说明并执行一些操作。
我的问题是当用户通过按 BACK 键关闭键盘时。操作系统关闭键盘,但我的指令(我需要隐藏)仍然可见。
我尝试过覆盖 OnKeyDown,但是当使用 BACK 按钮关闭键盘时,它似乎没有被调用。
我尝试在 EditText 小部件上设置 OnKeyListener,但这似乎也没有被调用。
如何检测软键盘何时被关闭?
我知道一种方法来做到这一点。子类化 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
杰,你的解决方案很好!谢谢 :)
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
的任何特殊原因?
android.support.v7.widget.AppCompatEditText
以进行着色。
AppCompatEditText
用于 androidx
我通过调用 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 来检测键盘是否显示
/**
* 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);
}
}
现在是 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!!
}
使用@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
}
}
只需创建一个扩展 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;}
这是关键侦听器的解决方案。我不知道为什么会这样,但是如果您只是在自定义 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);
}
对于希望在 Xamarin 中做同样事情的任何人,我已经翻译了一些热门答案,因为它有点不同。我创建了一个要点 here,但总而言之,您创建了一个自定义 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>
};
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
也适用于横向模式的解决方案(如果用户在横向模式下按 Done
按钮,则不会调用 onKeyPreIme
):
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)
}
}
https://i.stack.imgur.com/IWN4J.png
onKeyPreIme
吗?还是有什么特别的原因让它不是这样?SearchView
中达到类似的效果,请参考stackoverflow.com/questions/9629313/…