2

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.

Torben
  • 3,805
  • 26
  • 31
didi_X8
  • 5,018
  • 10
  • 42
  • 46

5 Answers5

3

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

mikepenz
  • 12,708
  • 14
  • 77
  • 117
  • I have not yet tried this solution, but looking at the code I'm quite sure it works. I never expected such a hack to be needed for this seemingly minor issue. Thanks for the code (even if not self-written)! – didi_X8 Aug 19 '11 at 15:10
1

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)
}
Shayan_Aryan
  • 2,002
  • 1
  • 29
  • 31
0

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;
    }
});
user2288580
  • 2,210
  • 23
  • 16
0

You can override the onBackPressed in your activity (pseudo-code - be warned):

@Override
public void onBackPressed() {
    if(userHasChangedAnything) {
        revertChanges();
    } else {
        super.onBackPressed();
    }
}
Martyn
  • 16,432
  • 24
  • 71
  • 104
  • I also thought about that, but (assuming it works) that would be a big hack since the EditText is in a Fragment, not in an Activity, so the code to handle this would be scattered around. No better solution to this? – didi_X8 Aug 02 '11 at 21:07
  • This also doesn't work (as doesn't onKeyDown). When closing the keyboard, it just doesn't fire. – didi_X8 Aug 08 '11 at 22:03
0

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;
    }
})
Martyn
  • 16,432
  • 24
  • 71
  • 104