30

When the soft keyboard opens I want a scroll view to scroll down to the bottom.

For this I can use: fullScroll(View.FOCUS_DOWN);

But how do I fire that command after the soft keyboard opening event triggers?

Noah Seidman
  • 4,109
  • 5
  • 23
  • 28
  • [My simplest solution to detect keyboard. Its just a simple if condition](https://stackoverflow.com/a/61552248/11958371) – Rohith S May 01 '20 at 23:17

8 Answers8

45

Here is my solution:

1/ A simple interface

public interface KeyboardVisibilityListener {
    void onKeyboardVisibilityChanged(boolean keyboardVisible);
}

2/ A utility method (put it where you want, for instance in a class named KeyboardUtil)

public static void setKeyboardVisibilityListener(Activity activity, KeyboardVisibilityListener keyboardVisibilityListener) {
    View contentView = activity.findViewById(android.R.id.content);
    contentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        private int mPreviousHeight;

        @Override
        public void onGlobalLayout() {
            int newHeight = contentView.getHeight();
            if (mPreviousHeight != 0) {
                if (mPreviousHeight > newHeight) {
                    // Height decreased: keyboard was shown
                    keyboardVisibilityListener.onKeyboardVisibilityChanged(true);
                } else if (mPreviousHeight < newHeight) {
                    // Height increased: keyboard was hidden
                    keyboardVisibilityListener.onKeyboardVisibilityChanged(false);
                } else {
                    // No change
                }
            }
            mPreviousHeight = newHeight;
        }
    });
}

3/ Use from an Activity this way (a good place is in onCreate):

KeyboardUtil.setKeyboardVisibilityListener(this, mKeyboardVisibilityListener);
BoD
  • 10,222
  • 6
  • 60
  • 57
  • 2
    I liked your suggestion.. its juts important to point that this will only work with the adjustResize, isn't it? Is there any other requirements for this proposal that you can see here? – Guilherme Oliveira Jan 06 '15 at 13:07
  • Pretty nice way to do that, although I would advice you to introduce a way to remove previously added `Listener` from `ViewTreeObserver` (for example in case you used this method in `Fragment`, and it is being detached from `Activity`). – Bartek Lipinski Apr 02 '15 at 12:58
  • 2
    Fails for `android:inputType="textPassword"` Neverthless a neat way! – User3 Oct 22 '15 at 06:50
  • @User3 I really don't see why this should fail depending on the inputType. – BoD Oct 22 '15 at 10:23
  • Because for passwords there is no prediction and you miss the top row, give it a try on 5.0.1 environment. – User3 Oct 23 '15 at 04:53
  • This should be marked as the correct answer. Works on all Android versions, including 6.0.1 – Rabi Apr 10 '16 at 07:35
  • @BoD I don't understand what I have to do after step 3 – Nicolas Oct 16 '16 at 15:53
  • It works well but I have to manage also the rotation of screen (onConfigChange) because we use "configChanges" – Alecs Dec 14 '16 at 08:41
29

Per this post and this post on android-developers it doesn't seem like it's possible to do what you want. You may want to re-examine your use case for what you're doing. Maybe one of the softInputMode flags will work for you.

Rich Schuler
  • 40,748
  • 6
  • 68
  • 58
  • Im having trouble with onSizeChanged(). Any ideas how to use it on a Linear Layout as the height changes? – Noah Seidman Jun 21 '10 at 02:37
  • 57
    In december of 2012. Yes that is 2012, Android still has no good way of notifying if the soft keyboard is visible or not. Amazing! – sebrock Dec 06 '12 at 16:28
  • 47
    @sebrock Same here in 2014 – Rod_Algonquin May 01 '14 at 21:22
  • The way the soft keyboard and it's apis are designed makes me want to gouge my eyes out. – Not Gabriel Jan 11 '16 at 15:47
  • 28
    yup 2016 is here :( – Arlind Feb 04 '16 at 10:24
  • 25
    Almost 2017. We have new emulator, new IDE and still no solution to check the keyboard state. – Val Apr 13 '16 at 08:36
  • 13
    Its 2017, awaiting for something miracle – Ichigo Kurosaki Jan 05 '17 at 06:00
  • 11
    still 2017. what a year to be alive. – CptEric Mar 02 '17 at 10:06
  • 1
    Struggling with this just the last day of August 2017. I think we will get an open API for reminders (https://issuetracker.google.com/issues/36760283) before a basic callback for keyboard opening/closing :P – Jose_GD Aug 31 '17 at 19:48
  • 3
    Soon 2018, rocking it with Kotlin and architecture components, fancy! Still would be nice . . . – Adam Dec 06 '17 at 19:46
  • 2
    2018 has arrived, but the API is nowhere to be found. We are forced to keep using horrible hacks that impact battery and performance. Thanks, Google devs, for forcing for us to resort for over 8 years to heavily intensive hacks that are constantly triggered with every slight layout change instead of providing an API that indicates when the keyboard is open or closed. The `adjust_resize` already has this built in, you just choose not to expose it for development since the beginning. Then users complaint that their apps are draining battery, like Messenger and other keyboard centered apps. – Shadow Jan 23 '18 at 15:36
  • 21
    2019 Is here. The first man will make his first step on Mars in the nearest future. A few weeks ago the world have seen devices with folding touch screens. Robots are everywhere. But we still cannot detect the keyboard visibility in Android easily. Hurrra!!! – bukka.wh Mar 07 '19 at 11:05
  • 12
    2020 arrived. This issue made 10 years! Google please don't fix this, I kind of feel emotionally attached to it, so many years with us... – Pedro Gonzalez Jan 14 '20 at 11:03
  • 4
    It's still 2020 but the day has come, we can now detect more easily if a keyboard is displaying in Android 11. What a day to be alive! https://twitter.com/chrisbanes/status/1230598177511788545 – Gimberg Feb 21 '20 at 08:13
  • 2
    Its 2021, the whole world went through pandemic but this little problem still exists – Zohab Ali Feb 03 '21 at 15:16
  • 1
    My IOS dev friends are mocking my career choice. Developing apps on a platform that cannot even tell if the keyboard is visible or not in 2021. Any bets on which will come first, this feature or our evolution away from fingers? – szaske Apr 27 '21 at 19:45
7

watching the date , possibly you have a solution for your question, otherwise:

Here is the same response i made to another question related : Is there a way to tell if the soft-keyboard is shown?

but i copy full response here to avoid dead links:

Please check Configuration Changes for your Activity

This for your AndroidManifest.xml

and this for your Activity class http://developer.android.com/reference/android/app/Activity.html#onConfigurationChanged(android.content.res.Configuration)

You will need to @Override the public method onConfigurationChanged(android.content.res.Configuration) of your Activity to be able to handle, for example, this values:
hardKeyboardHidden,
keyboard,
keyboardHidden

For all possible values check http://developer.android.com/reference/android/content/res/Configuration.html

You will see there something like this:

HARDKEYBOARDHIDDEN_NO   
HARDKEYBOARDHIDDEN_UNDEFINED    
HARDKEYBOARDHIDDEN_YES  
KEYBOARDHIDDEN_NO   
KEYBOARDHIDDEN_UNDEFINED    
KEYBOARDHIDDEN_YES  
KEYBOARD_12KEY  
KEYBOARD_NOKEYS 
KEYBOARD_QWERTY 
KEYBOARD_UNDEFINED

Also there you will be able to read something like this:

public int  hardKeyboardHidden  A flag indicating whether the hard keyboard has been      hidden.
public int  keyboard    The kind of keyboard attached to the device.
public int  keyboardHidden  A flag indicating whether any keyboard is available.

UPDATE:

Here is a specific sample of what i´m talking about:

http://developer.android.com/guide/topics/resources/runtime-changes.html

    
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
    // Checks whether a hardware keyboard is available
    if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
    } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
    }
}

I hope this help you

Community
  • 1
  • 1
yeradis
  • 4,985
  • 5
  • 23
  • 25
2

The only way I was able to work around this is by setting my activity's android:windowSoftInputMode="adjustResize" and then embed a custom "detector view" in the layout to handle a container size change and propagate that as a custom event (via a Listener) for soft keyboard on/off.

The following post describes an approach to implementing it: EditText does not trigger changes when back is pressed

Community
  • 1
  • 1
Roberto Andrade
  • 1,643
  • 20
  • 24
1

This works for me

parent.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

            boolean someHasFocus = false;

            if(host.hasFocus())
                someHasFocus = true;
            if(folder.hasFocus())
                someHasFocus = true;
            if(user.hasFocus())
                someHasFocus = true;
            if(pass.hasFocus())
                someHasFocus = true;

            if(someHasFocus){
                if(bottom>oldBottom){
                    // Keyboard Close
                    viewToHide.setVisibility(View.VISIBLE);

                }else if(bottom<oldBottom){
                   // Keyboard Open
                    viewToHide.setVisibility(View.GONE);
                }

            }else{
                // show
                viewToHide.setVisibility(View.VISIBLE);
            }
        }
    });

Where parent is the main layout, viewToHide is the view that shows or hides when the keyboard is shown, and host,folder,user and pass are the EditText of my form.

And this in the manifest

android:windowSoftInputMode="stateHidden|adjustResize"

Hope this help

0

for this what i used to do same:

  import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class SoftKeyboard implements View.OnFocusChangeListener
{
    private static final int CLEAR_FOCUS = 0;

    private ViewGroup layout;
    private int layoutBottom;
    private InputMethodManager im;
    private int[] coords;
    private boolean isKeyboardShow;
    private SoftKeyboardChangesThread softKeyboardThread;
    private List<EditText> editTextList;

    private View tempView; // reference to a focused EditText

    public SoftKeyboard(ViewGroup layout, InputMethodManager im)
    {
        this.layout = layout;
        keyboardHideByDefault();
        initEditTexts(layout);
        this.im = im;
        this.coords = new int[2];
        this.isKeyboardShow = false;
        this.softKeyboardThread = new SoftKeyboardChangesThread();
        this.softKeyboardThread.start();
    }


    public void openSoftKeyboard()
    {
        if(!isKeyboardShow)
        {
            layoutBottom = getLayoutCoordinates();
            im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
            softKeyboardThread.keyboardOpened();
            isKeyboardShow = true;
        }
    }

    public void closeSoftKeyboard()
    {
        if(isKeyboardShow)
        {
            im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
            isKeyboardShow = false;
        }
    }

    public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback)
    {
        softKeyboardThread.setCallback(mCallback);
    }

    public void unRegisterSoftKeyboardCallback()
    {
        softKeyboardThread.stopThread();
    }

    public interface SoftKeyboardChanged
    {
        public void onSoftKeyboardHide();
        public void onSoftKeyboardShow();
    }

    private int getLayoutCoordinates()
    {
        layout.getLocationOnScreen(coords);
        return coords[1] + layout.getHeight();
    }

    private void keyboardHideByDefault()
    {
        layout.setFocusable(true);
        layout.setFocusableInTouchMode(true);
    }

    /*
     * InitEditTexts now handles EditTexts in nested views
     * Thanks to Francesco Verheye (verheye.francesco@gmail.com)
     */
    private void initEditTexts(ViewGroup viewgroup)
    {
        if(editTextList == null)
            editTextList = new ArrayList<EditText>();

        int childCount = viewgroup.getChildCount();
        for(int i=0; i<= childCount-1;i++)
        {
            View v = viewgroup.getChildAt(i);

            if(v instanceof ViewGroup)
            {
                initEditTexts((ViewGroup) v);
            }

            if(v instanceof EditText)
            {
                EditText editText = (EditText) v;
                editText.setOnFocusChangeListener(this);
                editText.setCursorVisible(true);
                editTextList.add(editText);
            }
        }
    }

    /*
     * OnFocusChange does update tempView correctly now when keyboard is still shown
     * Thanks to Israel Dominguez (dominguez.israel@gmail.com)
     */
    @Override
    public void onFocusChange(View v, boolean hasFocus)
    {
        if(hasFocus)
        {
            tempView = v;
            if(!isKeyboardShow)
            {
                layoutBottom = getLayoutCoordinates();
                softKeyboardThread.keyboardOpened();
                isKeyboardShow = true;
            }
        }
    }

    // This handler will clear focus of selected EditText
    private final Handler mHandler = new Handler()
    {
        @Override
        public void handleMessage(Message m)
        {
            switch(m.what)
            {
                case CLEAR_FOCUS:
                    if(tempView != null)
                    {
                        tempView.clearFocus();
                        tempView = null;
                    }
                    break;
            }
        }
    };

    private class SoftKeyboardChangesThread extends Thread
    {
        private AtomicBoolean started;
        private SoftKeyboardChanged mCallback;

        public SoftKeyboardChangesThread()
        {
            started = new AtomicBoolean(true);
        }

        public void setCallback(SoftKeyboardChanged mCallback)
        {
            this.mCallback = mCallback;
        }

        @Override
        public void run()
        {
            while(started.get())
            {
                // Wait until keyboard is requested to open
                synchronized(this)
                {
                    try
                    {
                        wait();
                    } catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }

                int currentBottomLocation = getLayoutCoordinates();

                // There is some lag between open soft-keyboard function and when it really appears.
                while(currentBottomLocation == layoutBottom && started.get())
                {
                    currentBottomLocation = getLayoutCoordinates();
                }

                if(started.get())
                    mCallback.onSoftKeyboardShow();

                // When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
                // and at some moment equals layoutBottom.
                // That broke the previous logic, so I added this new loop to handle this.
                while(currentBottomLocation >= layoutBottom && started.get())
                {
                    currentBottomLocation = getLayoutCoordinates();
                }

                // Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
                while(currentBottomLocation != layoutBottom && started.get())
                {
                    synchronized(this)
                    {
                        try
                        {
                            wait(500);
                        } catch (InterruptedException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    currentBottomLocation = getLayoutCoordinates();
                }

                if(started.get())
                    mCallback.onSoftKeyboardHide();

                // if keyboard has been opened clicking and EditText.
                if(isKeyboardShow && started.get())
                    isKeyboardShow = false;

                // if an EditText is focused, remove its focus (on UI thread)
                if(started.get())
                    mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
            }
        }

        public void keyboardOpened()
        {
            synchronized(this)
            {
                notify();
            }
        }

        public void stopThread()
        {
            synchronized(this)
            {
                started.set(false);
                notify();
            }
        }

    }
}

and in your Activity or fragment call this method in onCreate()

  private void hideAndShowKeyBOrd() {
        InputMethodManager im = (InputMethodManager) getActivity().getSystemService(Service.INPUT_METHOD_SERVICE);

/*
Instantiate and pass a callback
*/
        SoftKeyboard softKeyboard;
        softKeyboard = new SoftKeyboard(mainLayout, im);
        softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged() {

            @Override
            public void onSoftKeyboardHide() {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                    }
                });
            }

            @Override
            public void onSoftKeyboardShow() {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        if (viewV.getVisibility() == View.VISIBLE) {
                            viewV.setVisibility(View.GONE);
                        }
                    }
                });

            }
        });
    }

enjoy your code:)

John smith
  • 1,671
  • 14
  • 27
0

Here is my solution. It doesn't need android:windowSoftInputMode="adjustResize"

public abstract class KeyboardActivity extends Activity {
    public static final int MIN_KEYBOARD_SIZE = 100;
    private Window mRootWindow;
    private View mRootView;
    private int mKeyboardHeight = -1;
    private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {

    public int height;
    public void onGlobalLayout() {
            Rect r = new Rect();
        View view = mRootWindow.getDecorView();
        view.getWindowVisibleDisplayFrame(r);
        if (height != r.height()) {
            int diff = height - r.height();
            height = r.height();
            if (Math.abs(diff) > MIN_KEYBOARD_SIZE) {
                int diff = height - r.height();
                if (height != 0 && Math.abs(diff) > MIN_KEYBOARD_SIZE) {
                    mKeyboardHeight = Math.abs(diff);
                    if (diff > 0) {
                        onKeyboardOpen();
                    } else {
                        onKeyboardClosed();
                    }
                }
                height = r.height();
            }
        }
    };

    protected abstract void onKeyboardClosed();

    protected abstract void onKeyboardOpen();

    /**
     * Should return keyboard height, if keyboard was shown at least once;
     * @return keyboard height or -1
     */
    protected int getKeyboardHeight() {
        return mKeyboardHeight;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mRootWindow = getWindow();
        mRootView = mRootWindow.getDecorView().findViewById(android.R.id.content);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
    }

    @Override
    protected void onStop() {
        super.onStop();
        mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
    }
}

Then I've just extend my activity from this activity and override onKeyboardClosed/onKeyboardOpen methods.

Grimmy
  • 1,861
  • 1
  • 14
  • 24
0

@BoD's answer works fine if I remove the following line.

if (mPreviousHeight != 0) {
     /* other code is same, because
        mPreviousHeight is 0 when it comes first */
}
Nathan Tuggy
  • 2,239
  • 27
  • 28
  • 36
CdVr
  • 171
  • 10