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?
I know a way to do this. Subclass the EditText and implement:
@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
Jay, your solution is good ! thanks :)
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
as well?
android.support.v7.widget.AppCompatEditText
for tinting.
AppCompatEditText
for androidx
I made a slight change on Jay's solution by calling super.onKeyPreIme():
_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);
}
};
Here is my custom EditText to detect whether keyboard is showing or not
/**
* 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);
}
}
It's 2019 now... So I created a more neat solution with Kotlin
1.Create an extension function:
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.Where the toggle listener is:
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.Use it in any Activity as simple as this:
addKeyboardToggleListener {shown ->
// hurray! Now you know when the keyboard is shown and hidden!!
}
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)
}
}
Usage:
keyboardEditText.listener = object : KeyboardEditText.Listener {
override fun onImeBack(editText: KeyboardEditText) {
//Back detected
}
}
Just create a class that extends Edittext and use that edittext in your code, 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;}
Here's a solution with the key listener. I have no idea why this works but OnKeyListener works if you just purely override onKeyPreIme on your custom EditText.
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);
}
For anyone looking to do the same in Xamarin, I've translated some of the top answers as it's a bit different. I created a gist here but summarizing, you create a custom EditText and override OnKeyPreIme
like so:
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);
}
}
... and then in the view...
editText = FindViewById<CustomEditText>(Resource.Id.MyEditText);
editText.BackPressed += (s, e) =>
{
// <insert code here>
};
hideSoftInputFromWindow
returns true when keyboard closes use it's value to detect keyboard close in 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
solution which works in landscape mode as well (onKeyPreIme
was not being called if user press Done
button in landscape mode):
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
Success story sharing
onKeyPreIme
? Or is there a particular reason for it not to be so?SearchView
, please refer to stackoverflow.com/questions/9629313/…