Christopher Perry
Christopher Perry

Reputation: 39225

Android: Error popup on EditText doesn't move down when keyboard goes away

I have an activity that displays a few EditTexts on screen for user input. To be sure the soft keyboard doesn't cover my fields when it displays I have set the property

android:windowSoftInputMode="adjustPan"

for my Activity in the manifest. I am validating the EditText's content when 1. The view loses focus 2. When the user performs the 'Enter' action. Upon validation, if the value is not valid I am calling

setError(CharSequence error)

on the EditText, which causes a popup to display containing the error I passed in. The problem is if the EditText is moved up when the soft keyboard displays, and the popup is displayed at that time (validation has failed), the popup doesn't follow the EditText down when the keyboard goes away, it stays where it was first displayed.

Any ideas on how to fix this? Is this a bug in Android?

Upvotes: 13

Views: 12367

Answers (5)

Jaume Colom Ferrer
Jaume Colom Ferrer

Reputation: 569

I found the the SOLUTION!

Here is the code:

import android.content.Context;
import android.os.Handler;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;

/**
 * Created by Jaume Colom Ferrer on 14/12/2015
 */
public class EditTextPopUpFixed extends AppCompatEditText {

    private static final int TIME_SLEEP = 500;

    private Context ctx;
    private EditTextListener mListener;
    private int backTimes = 0;

    public EditTextPopUpFixed(Context context) {
        super(context);
        ctx = context;
    }

    public EditTextPopUpFixed(Context context, AttributeSet attrs) {
        super(context, attrs);
        ctx = context;
    }

    public EditTextPopUpFixed(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        ctx = context;
    }

    public void setEditTextListener(EditTextListener lis) {
        mListener = lis;

        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                backTimes = 0;
                return false;
            }
        });
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_BACK)
            backTimes++;

        if (backTimes == 3) {
            backTimes = 0;
            mListener.close();
            return true;
        }

        if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ENTER) {
            InputMethodManager mgr = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
            if (mgr.isActive()) {
                mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);
                boolean mustRevalidate = getError() != null;
                setError(null);
                if (mListener != null && mustRevalidate) {
                    Handler han = new Handler();
                    han.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mListener.revalidateEditText();
                        }
                    }, TIME_SLEEP);
                }
            } else {
                backTimes = 0;
                super.onKeyPreIme(keyCode, event);
            }
            return true;
        }
        return false;
    }

    public interface EditTextListener {
        void revalidateEditText(); //Revalidates the EditText to refresh the Popup (Must revalidate the EditText in its implementation)

        void close(); // Method to close the activity or fragment (Must finish activity or go back in its implementation)
    }
}

You can use this Custom EditText to solve this bug. It refreshes the Error, after keyboard open/close, and your popup will always be displayed correctly.

You must implement close(finish or onBackPressed) and revalidate edittext methods in your class.

Upvotes: 0

jakub
jakub

Reputation: 3844

You can also create Your custom EditText and override method onKeyPreIme(int keyCode, KeyEvent event)

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        clearFocus();
        return false;
    }
}

Upvotes: 4

Jockel
Jockel

Reputation: 191

For me, it helped to wrap the layout in a ScrollView. After this, all the scrolling of the setError-box worked fine.

Upvotes: 2

N-JOY
N-JOY

Reputation: 7635

Have you tried validating your view, i.e. calling invalidate() method of your view.

try editText.invalidate();

Thanks.

Upvotes: 0

Blundell
Blundell

Reputation: 76458

If this is as you described, I think this may be a genuine bug, so may be worth writing it up on the Android Source site.

So evidently I can only think of hack work arounds!

Override when the keyboard disappears:

public boolean onKeyPreIme(int keyCode, KeyEvent event) {
 if (keyCode == KeyEvent.KEYCODE_BACK && 
     event.getAction() == KeyEvent.ACTION_UP) {
         revalidateEditText();
         return false;
 }
 return super.dispatchKeyEvent(event);
}

public void revalidateEditText(){
       // Dismiss your origial error dialog           
       setError(null);
       // figure out which EditText it is, you already have this code
       // call your validator like in the Q
       validate(editText); // or whatever your equivalent is
}

This will revalidate your EditText, dismiss your error dialog and re-show it.

How's that sound?

Inspired by: Get back key event on EditText

Upvotes: 11

Related Questions