In an application with shopping cart, I give the option to change the amount of items via an EditText which allows only numerical input.
Everything works fine, except when the user changes the field and then presses the back key to hide the soft keyboard. In this case, the field shows the changed value, but I don't know how I can detect this change and react on it. Waiting for a switch to another activity is not an option.
When the user confirms with "done" button, I can handle this with a "OnEditorActionListener". But what about the back key?
update:
As it turned out, neither onKeyDown / onBackPressed nor OnKeyListener on the edit field do trigger when closing the soft keyboard with back key.
I had the same problem in an application which i wrote some time ago. It is discontiuned now but that's not your question xD.
In fact there is no option to track such a operation, but i've found a great (more or less) solution to add such a functionality. It's quite simple in theory but it's a "hack" too i think.
So what you will need is a custom linear layout which contains your application, or a special region. After that you have to add it a listener. And this will work only in portrait mode.
So here to the code: (i'm sorry but i can't remember the source)
The custom layout:
LinearLayoutThatDetactsSoftwarekeyboard.java (that's the original name of the layout xD)
package com.tundem.people.Layout;
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/*
* LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when
* the soft keyboard is shown and hidden (something Android can't tell you, weirdly).
*/
public class LinearLayoutThatDetectSoftkeyboard extends LinearLayout {
public LinearLayoutThatDetectSoftkeyboard(Context context, AttributeSet attrs) {
super(context, attrs);
}
public interface Listener {
public void onSoftKeyboardShown(boolean isShowing);
}
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
Activity activity = (Activity) getContext();
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
int diff = (screenHeight - statusBarHeight) - height;
if (listener != null) {
listener.onSoftKeyboardShown(diff > 128); // assume all soft
// keyboards are at
// least 128 pixels high
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
And how you add the listener:
final LinearLayoutThatDetectSoftkeyboard lltodetectsoftkeyboard = (LinearLayoutThatDetectSoftkeyboard) findViewById(R.id.LinearLayout_SoftKeyboard);
lltodetectsoftkeyboard.setListener(new Listener() {
public void onSoftKeyboardShown(boolean isShowing) {
if (actMode == MODE_SMS && isShowing) {
findViewById(R.id.LinearLayout_bottomnavigationbar).setVisibility(View.GONE);
} else {
findViewById(R.id.LinearLayout_bottomnavigationbar).setVisibility(View.VISIBLE);
}
}
});
The LinearLayout adds a Listener which will be called each time the layoutheight changes by at least 128 pixels. It's a trick and it won't work with keyboards that are smaller than 128 pixels (but i think each keyboard has such a height)
If the LayoutHeight has Changed you will get notified if it's showing now or not.
I hope my answer was useful. Perhaps you find the real source here on StackOverFlow again. I won't steal someones genius so. Credits goes to the unknown person ;)
This is the modified version of @mikepenz's answer, since it didn't work on Android 8 with windowSoftInputMode="adjustPan" mode.
The language is kotlin.
Use this view as root view and set the keyboardListener:
class KeyboardAwareConstraintLayout(context: Context?, attrs: AttributeSet?) : ConstraintLayout(context, attrs)
{
var keyboardListener: KeyboardListener? = null
constructor(context: Context?) : this(context, null)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
{
val rect = Rect()
getWindowVisibleDisplayFrame(rect)
val statusBarHeight = rect.top
val keyboardHeight = rootView.height - (rect.bottom - rect.top) - statusBarHeight
// R.dimen.keyboard_min_height has set to 120dp,
// as a reasonable minimum height of a soft keyboard
val minKeyboardHeight = resources.getDimensionPixelSize(R.dimen.keyboard_min_height)
keyboardListener?.onKeyboardShown(keyboardHeight > minKeyboardHeight)
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}
interface KeyboardListener
{
fun onKeyboardShown (shown: Boolean)
}
You can override the onBackPressed in your activity (pseudo-code - be warned):
@Override
public void onBackPressed() {
if(userHasChangedAnything) {
revertChanges();
} else {
super.onBackPressed();
}
}
With the new information of the code being in a fragment, I would suggest adding a key listener to the text box. This code hasn't been tested, but should work.
((EditText) findViewById(R.id.button)).setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
Log.d(this.getClass().getName(), "back button pressed");
return true;
}
return false;
}
})
Excuse me if this is not relevant but I once had this problem and forgot that I had added an OnKeyListener that caught the on back pressed for my dialogFragment.
I rewrote the listener as follows. The Top condition was added to solve the problem.
getDialog().setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialogInterface, int i, KeyEvent keyEvent) {
if(keyEvent.getKeyCode() == KeyEvent.KEYCODE_DEL) {
return false;
}
if(keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if(iYoutubePickerDialog != null) {
iYoutubePickerDialog.closeYoutubeDialog(getDialog());
}
}
return true;
}
});