Soft keyboard open and close listener in an activity in Android
Asked Answered
H

31

174

I have an Activity where there are 5 EditTexts. When the user clicks on the first EditText, the soft keyboard opens to enter some value in it. I want to set some other View's visibility to Gone when the soft keyboard opens and also when the user clicks on the first EditText and also when the soft keyboard closes from the same EditText on the back button press. Then I want to set some other View's visibility to visible.

Is there any listener or callback or any hack for when the soft keyboard opens from a click on the first EditText in Android?

Hoary answered 9/8, 2014 at 8:37 Comment(15)
No. There are no such listeners. There are hacks to achieve what you are trying to. Here's a possible approach: How to send out pointer event in Android.Kathiekathleen
@Kathiekathleen I am not looking for trying to detect the virtual keyboard height in Android.Hoary
I know. If you go through the code, you will see how the height is being determined. A pointer event is being sent -> two cases => 1. if the keyboard is open => & if the pointer's X and Y location falls on/over the keyboard => SecurityException => decrement Y and try again => until no exception is thrown => current Y value is the keyboard height. 2. if the keyboard is not open => no SecurityException.Kathiekathleen
How does it apply to your scenario? Send a pointer event at let's say 2/3 of screen's height. If a SecurityException is thrown => keyboard is open. Else, keyboard is closed.Kathiekathleen
@Kathiekathleen I only want this for first EditText not other EditText. How I can distinguish this ?Hoary
I see. You really do mean listener. I'm quite sure that android doesn't provide any. Probably, someone else can offer another hack.Kathiekathleen
@Kathiekathleen Yeah let's wait for other answer.Hoary
Try adding listener to your first edit textIndicatory
@Indicatory I am not sure what listener you are talking.Hoary
@Williams - I was saying about addTextChangedListener. manu also has specified 1 way using Focus Listener.Indicatory
Look at this link it will works perfect #2150578Catchascatchcan
try the library.. works for me! android-arsenal.com/details/1/2519Jammiejammin
Possible duplicate of Getting notified when virtual keyboard shown / dismissed for an EditText?Renaldo
Possible duplicate of Android: Detect softkeyboard openScornful
This question has only one proper answer: https://mcmap.net/q/36356/-soft-keyboard-open-and-close-listener-in-an-activity-in-androidMordred
O
100

This only works when android:windowSoftInputMode of your activity is set to adjustResize in the manifest. You can use a layout listener to see if the root layout of your activity is resized by the keyboard.

I use something like the following base class for my activities:

public class BaseActivity extends Activity {
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
            int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

            LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);

            if(heightDiff <= contentViewTop){
                onHideKeyboard();

                Intent intent = new Intent("KeyboardWillHide");
                broadcastManager.sendBroadcast(intent);
            } else {
                int keyboardHeight = heightDiff - contentViewTop;
                onShowKeyboard(keyboardHeight);

                Intent intent = new Intent("KeyboardWillShow");
                intent.putExtra("KeyboardHeight", keyboardHeight);
                broadcastManager.sendBroadcast(intent);
            }
        }
    };

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

    protected void onShowKeyboard(int keyboardHeight) {}
    protected void onHideKeyboard() {}

    protected void attachKeyboardListeners() {
        if (keyboardListenersAttached) {
            return;
        }

        rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

        keyboardListenersAttached = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (keyboardListenersAttached) {
            rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
        }
    }
}

The following example activity uses this to hide a view when the keyboard is shown and show it again when the keyboard is hidden.

The xml layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/rootLayout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">              

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

        <!-- omitted for brevity -->

    </ScrollView>

    <LinearLayout android:id="@+id/bottomContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <!-- omitted for brevity -->

    </LinearLayout>

</LinearLayout>

And the activity:

public class TestActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);

        attachKeyboardListeners();
    }

    @Override
    protected void onShowKeyboard(int keyboardHeight) {
        // do things when keyboard is shown
        bottomContainer.setVisibility(View.GONE);
    }

    @Override
    protected void onHideKeyboard() {
        // do things when keyboard is hidden
        bottomContainer.setVisibility(View.VISIBLE);
    }        
}
Overgrowth answered 5/9, 2014 at 8:2 Comment(15)
I suppose this will work as a solution for the given problem.Astra
+1 Yeah This is the perfect solution for my problem.Hoary
Hi, you used getTop() on the Window.ID_ANDROID_CONTENT. Get top doesn't work for me. it's always 0 here, it works like it is supposed to using getHeight() instead.Anesthesia
hi, I have used your solution, but in onShowKeyboard(), after bottomcontainer.setVisibility(GONE), I try to scroll above ScrollView to show editText in ScrollView, and I failed , the code followingCivism
: protected void onShowKeyboard(final int keyboardHeight) { if (selectedFragment.getTagText().equals(PersonalInfoFragment.class.getSimpleName())){ mRgBottomBanner.setVisibility(View.GONE); final ScrollView bsv = ButterKnife.findById(this, R.id.bsv); if (bsv != null){ bsv.post(new Runnable() { @Override public void run() { bsv.smoothScrollTo(0, 0-keyboardHeight); } }); } Is there anyting I am wrong?Civism
This solution actually consumes a lot of memory.Riha
Tricky but nice solution. I used a boolean that keeps the latest keyboard state in memory, so that the callback/broadcast does not happen continuously with the same value. Memory usage is fine by my tests.Restriction
where do you get rootLayout = (ViewGroup) findViewById(R.id.rootLayout);from?Lanni
Not working for me for some reason, it always calls onShowKeyboard either I open it or close it. I'm using findViewById(android.R.id.content), maybe that's the problem?Priority
getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop() always return 0.Koral
Nice solution, although at my case it didn't work out of the box. I had to add contentViewTop=contentViewTop+100 after the initializationIdes
if move listener in abstract class (BaseClass) and provide checks there itself?Burlie
@Ides your +100 solution depends on specific screen. Failed on tablets and hdpi phones. I used correction as ten percent of device height. That means if view height is lower than screenHeight - 10% the keyboard is open. else keyboard is closed. Here is my contentViewTop in onGlobalLayout: contentViewTop = (getWindow().getDecorView().getBottom() / 10)Mangonel
adjustResize doesn't work (layout isn't resized) if your activity has transparent status/navigation barsSula
What is the broadcastManager doing here?Metameric
S
125

Piece of cake with the awesome KeyboardVisibilityEvent library

KeyboardVisibilityEvent.setEventListener(
    getActivity(),
    new KeyboardVisibilityEventListener() {
        @Override
        public void onVisibilityChanged(boolean isOpen) {
            // Ah... at last. do your thing :)
        }
    });

Credits for Yasuhiro SHIMIZU

Sferics answered 26/7, 2016 at 15:51 Comment(11)
This won't work because keyboards don't have static heights, and height in this library is set to 100dp.Ramey
@Ramey a threshold height of 100dp is used for keyboard detection. No assumption is made about the actual keyboard heightPluto
It's still hard coded. Multi window? Samsung split view? Picture in picture mode? Also there is a minimal one-row keyboard that will fall bellow 100dp. There is no silver bullet here...Ramey
Because there is no catch all for this issue, this seems to be the easiest to implement and just get back to the code you actually want to work on :)Trafalgar
This is by far the best answer, totally reliable on any deviceBrickwork
@Brickwork totally not. Just look at some of the open issuesBlayne
@TimCastelijns in my experience, I've tried some variations and this is the one that works better in most devices. Maybe is not perfect, but I had a lot of problems with this feature on Nexus 5X and other Nexus devices, and this worked perfectly.Brickwork
@Brickwork this is the one that works better in most devices so don't claim that it is "totally reliable on any device"Blayne
It's upsetting that such a basic functionality doesn't have a native solution in Android! Google seems to cram so much unnecessary crap on new Android versions, but not address such important shortcommings...Harvestman
It is not working when set in menifest file android:windowSoftInputMode="adjustNothing" .Improbable
This answer is terribly outdated and may not work properly for many of cases. Please use official way of solving this problem: https://mcmap.net/q/36356/-soft-keyboard-open-and-close-listener-in-an-activity-in-androidMordred
O
100

This only works when android:windowSoftInputMode of your activity is set to adjustResize in the manifest. You can use a layout listener to see if the root layout of your activity is resized by the keyboard.

I use something like the following base class for my activities:

public class BaseActivity extends Activity {
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
            int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

            LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);

            if(heightDiff <= contentViewTop){
                onHideKeyboard();

                Intent intent = new Intent("KeyboardWillHide");
                broadcastManager.sendBroadcast(intent);
            } else {
                int keyboardHeight = heightDiff - contentViewTop;
                onShowKeyboard(keyboardHeight);

                Intent intent = new Intent("KeyboardWillShow");
                intent.putExtra("KeyboardHeight", keyboardHeight);
                broadcastManager.sendBroadcast(intent);
            }
        }
    };

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

    protected void onShowKeyboard(int keyboardHeight) {}
    protected void onHideKeyboard() {}

    protected void attachKeyboardListeners() {
        if (keyboardListenersAttached) {
            return;
        }

        rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

        keyboardListenersAttached = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (keyboardListenersAttached) {
            rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
        }
    }
}

The following example activity uses this to hide a view when the keyboard is shown and show it again when the keyboard is hidden.

The xml layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/rootLayout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">              

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

        <!-- omitted for brevity -->

    </ScrollView>

    <LinearLayout android:id="@+id/bottomContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <!-- omitted for brevity -->

    </LinearLayout>

</LinearLayout>

And the activity:

public class TestActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);

        attachKeyboardListeners();
    }

    @Override
    protected void onShowKeyboard(int keyboardHeight) {
        // do things when keyboard is shown
        bottomContainer.setVisibility(View.GONE);
    }

    @Override
    protected void onHideKeyboard() {
        // do things when keyboard is hidden
        bottomContainer.setVisibility(View.VISIBLE);
    }        
}
Overgrowth answered 5/9, 2014 at 8:2 Comment(15)
I suppose this will work as a solution for the given problem.Astra
+1 Yeah This is the perfect solution for my problem.Hoary
Hi, you used getTop() on the Window.ID_ANDROID_CONTENT. Get top doesn't work for me. it's always 0 here, it works like it is supposed to using getHeight() instead.Anesthesia
hi, I have used your solution, but in onShowKeyboard(), after bottomcontainer.setVisibility(GONE), I try to scroll above ScrollView to show editText in ScrollView, and I failed , the code followingCivism
: protected void onShowKeyboard(final int keyboardHeight) { if (selectedFragment.getTagText().equals(PersonalInfoFragment.class.getSimpleName())){ mRgBottomBanner.setVisibility(View.GONE); final ScrollView bsv = ButterKnife.findById(this, R.id.bsv); if (bsv != null){ bsv.post(new Runnable() { @Override public void run() { bsv.smoothScrollTo(0, 0-keyboardHeight); } }); } Is there anyting I am wrong?Civism
This solution actually consumes a lot of memory.Riha
Tricky but nice solution. I used a boolean that keeps the latest keyboard state in memory, so that the callback/broadcast does not happen continuously with the same value. Memory usage is fine by my tests.Restriction
where do you get rootLayout = (ViewGroup) findViewById(R.id.rootLayout);from?Lanni
Not working for me for some reason, it always calls onShowKeyboard either I open it or close it. I'm using findViewById(android.R.id.content), maybe that's the problem?Priority
getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop() always return 0.Koral
Nice solution, although at my case it didn't work out of the box. I had to add contentViewTop=contentViewTop+100 after the initializationIdes
if move listener in abstract class (BaseClass) and provide checks there itself?Burlie
@Ides your +100 solution depends on specific screen. Failed on tablets and hdpi phones. I used correction as ten percent of device height. That means if view height is lower than screenHeight - 10% the keyboard is open. else keyboard is closed. Here is my contentViewTop in onGlobalLayout: contentViewTop = (getWindow().getDecorView().getBottom() / 10)Mangonel
adjustResize doesn't work (layout isn't resized) if your activity has transparent status/navigation barsSula
What is the broadcastManager doing here?Metameric
G
83

As Vikram pointed out in the comments, detecting whether the softkeyboard is shown or has disappeared is only possible with some ugly hacks.

Maybe it is enough to set a focus listener on the edittext:

yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            //got focus
        } else {
            //lost focus
        }
   }
});
Gamages answered 5/9, 2014 at 7:8 Comment(8)
suppose I click on the edittext then it setOnFocusChangeListener listener will be called then I press back then it closes keyboard & but didn't click on other views, now again I click on same edittext which already have focus then what will happen ?Hoary
@Williams I'm no entirely sure, but I suspect that onFocusChange() will not be called.Gamages
you are right then how to handle that situation this is why I put this question on bountyHoary
this is not my question. Please read my question again - I have Activity where there are 5 EditText. When user click on first EditText then soft keyboard open to enter some value in it. I want to set the some other View visibility to Gone when soft keyboard open when user click on first EditText and when soft keyboard close from same EditText on back press then I want to set the some other View visibility to visible. Is there any listener or callback or any hack when soft keyboard open on click on first EditText in Android?Hoary
When you press back then it dismiss the keyboard that time onfocus listener never call that's is I am looking not which you are suggesting.Hoary
Guys don't look this answer because he is telling something different even I don't understand.Hoary
Anyway, It doesn't work for me... When the soft keyboard is hidden, There is no any focus change happened on the EditText... So I can't get notify from this Listener.Adust
If user just close keyboard (down arrow) focus is still on EditText but keyboard is closedMadalene
S
60

For Activity:

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();

                activityRootView.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { 
                 //enter your code here
                }else{
                 //enter code for hid
                }
            }
        });

For Fragment:

    view = inflater.inflate(R.layout.live_chat_fragment, null);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                //r will be populated with the coordinates of your view that area still visible.
                view.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...

                }
            }
        });
Saltigrade answered 21/7, 2016 at 13:34 Comment(5)
Used it for activity, but instead of comparing with view i compared with screen size. works greatWarden
better would be to compare heightDiff with a height in dp rather then in pixels. It can vary significantly.Vibratory
Does this need the android:windowSoftInputMode="adjustResize" in manifest?Tarratarradiddle
android:windowSoftInputMode="adjustResize" android:configChanges="orientation|keyboard|keyboardHidden"Saltigrade
It works for me, still, I have a question. Is it cost lots of resouces?Adust
D
36

Jaap's answer won't work for AppCompatActivity. Instead get the height of the Status Bar and Navigation bar etc and compare to your app's window size.

Like so:

    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // navigation bar height
        int navigationBarHeight = 0;
        int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // status bar height
        int statusBarHeight = 0;
        resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // display window size for the app layout
        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);

        // screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard
        int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height());

        if (keyboardHeight <= 0) {
            onHideKeyboard();
        } else {
            onShowKeyboard(keyboardHeight);
        }
    }
};
Dissipate answered 21/6, 2016 at 15:17 Comment(1)
Seems to work pretty well with one exception: breaks in split screen mode. Otherwise it's great.Bellanca
O
23

You can try it:

private void initKeyBoardListener() {
    // Минимальное значение клавиатуры. 
    // Threshold for minimal keyboard height.
    final int MIN_KEYBOARD_HEIGHT_PX = 150;
    // Окно верхнего уровня view. 
    // Top-level window decor view.
    final View decorView = getWindow().getDecorView();
    // Регистрируем глобальный слушатель. Register global layout listener.
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        // Видимый прямоугольник внутри окна. 
        // Retrieve visible rectangle inside window.
        private final Rect windowVisibleDisplayFrame = new Rect();
        private int lastVisibleDecorViewHeight;

        @Override
        public void onGlobalLayout() {
            decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
            final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();

            if (lastVisibleDecorViewHeight != 0) {
                if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
                    Log.d("Pasha", "SHOW");
                } else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
                    Log.d("Pasha", "HIDE");
                }
            }
            // Сохраняем текущую высоту view до следующего вызова.
            // Save current decor view height for the next call.
            lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
    });
}
Orv answered 5/5, 2017 at 8:35 Comment(0)
R
13

I am late but I just found a very convenient dependency out there. Using it you can check the visibility of the keyboard as well as make the keyboard "Hide" and Show Whenever you want with a single Line of Code.

implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'

And then you simply use this code segment to check the keyboard visibility.

KeyboardVisibilityEvent.setEventListener(this, new KeyboardVisibilityEventListener() {
        
@Override
  public void onVisibilityChanged(boolean isOpen) {

if (isOpen) 
  Toast.makeText(MainActivity.this, "keyboard opened",Toast.LENGTH_SHORT).show();
else 
  Toast.makeText(MainActivity.this, "keyboard hidden", Toast.LENGTH_SHORT).show();
}
});

Then if you want to Hide/Show keyboard at any point of time then you can just write one of these single lines to achieve it.

        UIUtil.showKeyboard(this,edittext_to_be_focused);
        UIUtil.hideKeyboard(this);
Ricardoricca answered 5/9, 2020 at 11:34 Comment(1)
I am getting this error now a days.. It was working previoulsy. Caused by: java.lang.ClassNotFoundException: Didn’t find class “net.yslibrary.android.keyboardvisibilityevent.KeyboardVisibilityEventListener” on path: DexPathList[[zip file “/data/app/~~iquO8KrlQd-nWuV82FYhTA==/com.vzw.sampleappforgyde-rMZn0uD205pWrw8PzNOQ0Q==/base.apk”],nativeLibraryDirectories=[/data/app/~~iquO8KrlQd-nWuV82FYhTA==/com.vzw.sampleappforgyde-rMZn0uD205pWrw8PzNOQ0Q==/lib/arm64, /system/lib64, /system/system_ext/lib64]]Langsyne
M
6

The below code is working for me,

mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (mainLayout != null) {
                int heightDiff = mainLayout.getRootView().getHeight() - mainLayout.getHeight();
                if (heightDiff > dpToPx(getActivity(), 200)) { 
                   //keyboard is open
                } else {
                   //keyboard is hide
                }
            }
        }
    });
Movable answered 12/3, 2019 at 14:59 Comment(1)
What is mainLayout? And heightDiff always 0 when the text filed the top of the view.Annuity
S
6

For use in Kotlin inside fragment, which is a common use case it is very easy with KeyboardVisibilityEvent library.

In build.gradle:

implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'

In Fragment:

activity?.let {
    KeyboardVisibilityEvent.setEventListener(it,object: KeyboardVisibilityEventListener {
        override fun onVisibilityChanged(isOpen: Boolean) {
            if (isOpen) Toast.makeText(context,"Keyboard is opened",Toast.LENGTH_SHORT).show()
            else Toast.makeText(context,"Keyboard is closed",Toast.LENGTH_SHORT).show()
        }
    })
}

Source and credits

Sazerac answered 4/1, 2021 at 9:7 Comment(2)
Detects when keyboard opens but not when it closes.Manvell
Have you tried it? For it me it worked. Try with if (!isOpen) {}Sazerac
P
5

You can use my Rx extension function (Kotlin).

/**
 * @return [Observable] to subscribe of keyboard visibility changes.
 */
fun AppCompatActivity.keyboardVisibilityChanges(): Observable<Boolean> {

    // flag indicates whether keyboard is open
    var isKeyboardOpen = false

    val notifier: BehaviorSubject<Boolean> = BehaviorSubject.create()

    // approximate keyboard height
    val approximateKeyboardHeight = dip(100)

    // device screen height
    val screenHeight: Int = getScreenHeight()

    val visibleDisplayFrame = Rect()

    val viewTreeObserver = window.decorView.viewTreeObserver

    val onDrawListener = ViewTreeObserver.OnDrawListener {

        window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)

        val keyboardHeight = screenHeight - (visibleDisplayFrame.bottom - visibleDisplayFrame.top)

        val keyboardOpen = keyboardHeight >= approximateKeyboardHeight

        val hasChanged = isKeyboardOpen xor keyboardOpen

        if (hasChanged) {
            isKeyboardOpen = keyboardOpen
            notifier.onNext(keyboardOpen)
        }
    }

    val lifeCycleObserver = object : GenericLifecycleObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event?) {
            if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                source.lifecycle.removeObserver(this)
                notifier.onComplete()
            }
        }
    }

    viewTreeObserver.addOnDrawListener(onDrawListener)
    lifecycle.addObserver(lifeCycleObserver)

    return notifier
            .doOnDispose {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                lifecycle.removeObserver(lifeCycleObserver)
            }
            .onTerminateDetach()
            .hide()
}

Example:

(context as AppCompatActivity)
                    .keyboardVisibilityChanges()
                    .subscribeBy { isKeyboardOpen ->
                        // your logic
                    }
Pathfinder answered 31/7, 2018 at 21:15 Comment(3)
Doesn't work for me. Can't find methods dip() and getScreenHeight()Ardra
@MarcinKunert it's just extension function which help you to convert pixels to dp, and get screen height. If you want, i can give you example of such functionsPathfinder
GenericLifecycleObserver is deprecated ? any solution?Function
U
5

in kotlin you can use this code in your activity

window.decorView.viewTreeObserver.addOnGlobalLayoutListener{
    val r = Rect()
    window.decorView.getWindowVisibleDisplayFrame(r)
 
        val height =window.decorView.height
        if(height - r.bottom>height*0.1399){
           //keyboard is open
      }else{
            //keyboard is close
      }
Undersigned answered 26/10, 2021 at 18:28 Comment(3)
why 0.139? can you tell more about this number?Dav
window.decorView.height: This represents the height of the entire window's decor view. r.bottom: This represents the bottom coordinate of the visible display frame. The visible display frame is the part of the window that is visible to the user, excluding any system bars like the status bar or navigation bar, and also excluding any portion covered by the keyboard when it's open. height * 0.1399: This calculates 13.99% of the height of the window's decor view.Undersigned
The condition height - r.bottom > height * 0.1399 essentially checks if the difference between the height of the entire window's decor view and the bottom coordinate of the visible display frame is greater than 13.99% of the height of the window's decor view.Undersigned
B
4

Insets are the only one official and proper answer. The simple inset listener works like a charm. Here is the code:

ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
    val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
    insets
}

Attention! This solution works only in case if you set edge-to-edge mode:

WindowCompat.setDecorFitsSystemWindows(window, false)

Please check official documentation about checking keyboard software visibility and proper implementation of edge-to-edge mode:

Blackcock answered 9/6, 2022 at 18:41 Comment(0)
D
3

This will work without any need to change your activity's android:windowSoftInputMode

step 1: extend EditText class and override these two:

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener;
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

step 2: create these two in your activity:

private void initKeyboard() {
    final AppEditText editText = findViewById(R.id.some_id);
    editText.setOnFocusChangeListener(new OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            setKeyboard(hasFocus);
        }
    });
    editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (event == null || event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                editText.clearFocus();
            }
            return false;
        }
    });
}

public void setKeyboard(boolean isShowing) {
    // do something
}

*** remember in order to make clearFocus work, you have to make parent or first child in the parent hierarchy focusable.

    setFocusableInTouchMode(true);
    setFocusable(true);
Daphene answered 2/6, 2018 at 17:38 Comment(0)
P
3

Check my Kotlin extension View.keyboardVisibilityChanges():

fun View.keyboardVisibilityChanges(): Flow<Boolean>{
    return onPreDrawFlow()
        .map { isKeyboardVisible() }
        .distinctUntilChanged()
}
fun View.onPreDrawFlow(): Flow<Unit> {
    return callbackFlow {
        val onPreDrawListener = ViewTreeObserver.OnPreDrawListener {
            trySendBlocking(Unit)
            true
        }
        viewTreeObserver.addOnPreDrawListener(onPreDrawListener)
        awaitClose {
            viewTreeObserver.removeOnPreDrawListener(onPreDrawListener)
        }
    }
}
fun View.isKeyboardVisible(): Boolean = ViewCompat.getRootWindowInsets(this)
    ?.isVisible(Type.ime())
    ?: false
Pathfinder answered 23/5, 2021 at 5:28 Comment(0)
W
2

If you can, try to extend EditText and override 'onKeyPreIme' method.

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener; //keep it for later usage
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            //you can define and use custom listener,
            //OR define custom R.id.<imeId>
            //OR check event.keyCode in listener impl
            //* I used editor action because of ButterKnife @
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

How can you extend it:

  1. Implement onFocus listening and declare 'onKeyboardShown'
  2. declare 'onKeyboardHidden'

I think, that recalculating of screen height is not 100% successfully as mentioned before. To be clear, overriding of 'onKeyPreIme' is not called on 'hide soft keyboard programatically' methods, BUT if you are doing it anywhere, you should do 'onKeyboardHidden' logic there and do not create a comprehensive solutions.

Word answered 16/7, 2017 at 21:11 Comment(0)
B
0

This is not working as desired...

... have seen many use size calculations to check ...

I wanted to determine if it was open or not and I found isAcceptingText()

so this really does not answer the question as it does not address opening or closing rather more like is open or closed so it is related code that may help others in various scenarios...

in an activity

    if (((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
    }

in a fragment

    if (((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");

    }
Brandt answered 9/8, 2014 at 8:38 Comment(0)
S
0
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mainactivity);
    attachKeyboardListeners();
    ....
    yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                yourEditText2.setVisibility(View.GONE);
                yourEditText3.setVisibility(View.GONE);
                yourEditText4.setVisibility(View.GONE);
                yourEditText5.setVisibility(View.GONE);
            } else {
                yourEditText2.setVisibility(View.VISIBLE);
                yourEditText3.setVisibility(View.VISIBLE);
                yourEditText4.setVisibility(View.VISIBLE);
                yourEditText5.setVisibility(VISIBLE);
            }
       }
    });
    }
}
Snowblind answered 11/9, 2014 at 7:51 Comment(3)
suppose I click on the edittext then it setOnFocusChangeListener listener will be called then I press back then it closes keyboard & but didn't click on other views, now again I click on same edittext which already have focus then what will happen ?Hoary
this is not my question. Please read my question again - I have Activity where there are 5 EditText. When user click on first EditText then soft keyboard open to enter some value in it. I want to set the some other View visibility to Gone when soft keyboard open when user click on first EditText and when soft keyboard close from same EditText on back press then I want to set the some other View visibility to visible. Is there any listener or callback or any hack when soft keyboard open on click on first EditText in Android?Hoary
When you press back then it dismiss the keyboard that time onfocus listener never call that's is I am looking not which you are suggestingHoary
R
0

Use this class,

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 ([email protected])
 */
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 ([email protected])
 */
@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();
        }
    }

}
}

In Android Manifest, android:windowSoftInputMode="adjustResize" is necessary.

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use the layout root
InputMethodManager im = (InputMethodManager)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()  {
    // Code here
}

@Override
public void onSoftKeyboardShow() {
    // Code here
}   
});

/*
Open or close the soft keyboard easily
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();

/* Prevent memory leaks:*/
@Override
public void onDestroy() {
    super.onDestroy();
    softKeyboard.unRegisterSoftKeyboardCallback();
}

P.S - Completely taken from here.

Riha answered 26/10, 2015 at 10:40 Comment(0)
M
0

For the case of adjustResize and FragmentActivity accepted solution from @Jaap doesn't work for me.

Here is my solution:

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    private int contentDiff;
    private int rootHeight;
    @Override
    public void onGlobalLayout() {
        View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
        if (rootHeight != mDrawerLayout.getRootView().getHeight()) {
            rootHeight = mDrawerLayout.getRootView().getHeight();
            contentDiff = rootHeight - contentView.getHeight();
            return;
        }
        int newContentDiff = rootHeight - contentView.getHeight();
        if (contentDiff != newContentDiff) {
            if (contentDiff < newContentDiff) {
                onShowKeyboard(newContentDiff - contentDiff);
            } else {
                onHideKeyboard();
            }
            contentDiff = newContentDiff;
        }
    }
};
Minus answered 23/3, 2017 at 11:1 Comment(0)
P
0

A different approach would be to check when the user stopped typing...

When a TextEdit is in focus (user is/was typing) you could hide the views (focus listener)

and use a Handler + Runnable and a text change listener to close the keyboard (regardless of its visibility) and show the views after some delay.

The main thing to look out for would be the delay you use, which would depend on the content of these TextEdits.

Handler timeoutHandler = new Handler();
Runnable typingRunnable = new Runnable() {
    public void run() {
        // current TextEdit
        View view = getCurrentFocus();

        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        // reset focus
        view.clearFocus();
        // close keyboard (whether its open or not)
        imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);

        // SET VIEWS VISIBLE
    }
};

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            // SET VIEWS GONE

            // reset handler
            timeoutHandler.removeCallbacks(typingRunnable);
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});

editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Reset Handler...
        timeoutHandler.removeCallbacks(typingRunnable);
    }

    @Override
    public void afterTextChanged(Editable s) {
        // Reset Handler Cont.
        if (editText.getText().toString().trim().length() > 0) {
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});
Pachydermatous answered 14/10, 2017 at 2:5 Comment(0)
A
0

This code works great nice

use this class for root view:

public class KeyboardConstraintLayout extends ConstraintLayout {

private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;

public KeyboardConstraintLayout(Context context) {
    super(context);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); //128dp
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (!isInEditMode()) {
        Activity activity = (Activity) getContext();
        @SuppressLint("DrawAllocation")
        Rect rect = new Rect();
        getWindowVisibleDisplayFrame(rect);

        int statusBarHeight = rect.top;
        int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;

        if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
            if (keyboardHeight > minKeyboardHeight) {
                if (!isShow) {
                    isShow = true;
                    keyboardListener.onKeyboardVisibility(true);
                }
            }else {
                if (isShow) {
                    isShow = false;
                    keyboardListener.onKeyboardVisibility(false);
                }
            }
        }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

public boolean isShowKeyboard() {
    return isShow;
}

public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
    this.targetEditText = targetEditText;
    this.keyboardListener = keyboardListener;
}

public interface KeyboardListener {
    void onKeyboardVisibility (boolean isVisible);
}

}

and set keyboard listener in activity or fragment:

        rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
        @Override
        public void onKeyboardVisibility(boolean isVisible) {

        }
    });
Analeptic answered 22/5, 2018 at 16:2 Comment(0)
P
0

Found an accurate way of telling whether or not a keyboard when using the 'adjustResize' Soft input mode (Kotlin code)

Define a couple of activity scope variables

private var activityHeight = 0
private var keyboardOpen = false

Write the following code in onCreate

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        ...

        /* Grab initial screen value */
        [email protected] {
            val displayFrame : Rect = Rect()
            [email protected](displayFrame)
            activityHeight = displayFrame.height()
        }

        /* Check for keyboard open/close */
        [email protected] { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
            val drawFrame : Rect = Rect()
            [email protected](drawFrame)
            val currentSize = drawFrame.height()

            keyboardOpen = currentSize < activityHeight
            Log.v("keyboard1","$keyboardOpen $currentSize - $activityHeight")
        }
}

You now have a boolean which accurately tracks whether or not the keyboard is open, do what you will

Preventer answered 1/12, 2020 at 2:22 Comment(0)
F
-1

Unfortunately I do not have a sufficiently high reputation to comment on Jaap van Hengstum's answer. But I read a few comments of people, having the problem that contentViewTop is always 0 and that onShowKeyboard(...) is always called.

I had the same issue and figured out the problem I had. I used an AppCompatActivity instead of a 'normal' Activity. In this case Window.ID_ANDROID_CONTENT refers to an ContentFrameLayout and not to the FrameLayout with the right top-value. In my case it was fine to use the 'normal' Activity, if you have to use another activity-type (I just tested the AppCompatActivity, maybe it's also an issue with other acitivy-types like the FragmentActivity), you have to access the FrameLayout, which is an ancestor of the ContentFrameLayout.

Formalism answered 6/3, 2016 at 13:17 Comment(0)
N
-1
private boolean isKeyboardShown = false;
private int prevContentHeight = 0;
private ViewGroup contentLayout;

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener =
        new ViewTreeObserver.OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        int contentHeight = contentLayout.getHeight();
        int rootViewHeight = contentLayout.getRootView().getHeight();

        if (contentHeight > 0) {

            if (!isKeyboardShown) {
                if (contentHeight < prevContentHeight) {
                    isKeyboardShown = true;
                    onShowKeyboard(rootViewHeight - contentHeight);
                }
            } else {
                if (contentHeight > prevContentHeight) {
                    isKeyboardShown = false;
                    onHideKeyboard();
                }
            }

            prevContentHeight = contentHeight;
        }
    }
};

I've modified the Jaap's accepted answer a bit. But in my case, there are few assumptions such as android:windowSoftInputMode=adjustResize and the keyboard does not show up at the beginning when the app starts. And also, I assume that the screen in regard matches the parent's height.

contentHeight > 0 this check provides me to know if the regarding screen is hidden or shown to apply keyboard event listening for this specific screen. Also I pass the layout view of the regarding screen in attachKeyboardListeners(<your layout view here>) in my main activity's onCreate() method. Every time when the height of the regarding screen changes, I save it to prevContentHeight variable to check later whether the keyboard is shown or hidden.

For me, so far it's been worked pretty well. I hope that it works for others too.

Nonsuit answered 3/8, 2017 at 8:21 Comment(0)
M
-1

You can handle keyboard visibility by overriding two methods in your Activity: onKeyUp() and onKeyDown() more information in this link: https://developer.android.com/training/keyboard-input/commands

Monopode answered 25/7, 2019 at 19:39 Comment(1)
The docs exactly specify that this functions should not be used in reference to the soft input keyboard.Epp
U
-1

at the first create a kotlin file and add these methods:

fun Activity.getRootView(): View {
    return findViewById<View>(android.R.id.content)
}
fun Context.convertDpToPx(dp: Float): Float {
    return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, 
            dp, 
            this.resources.displayMetrics
    )
}
fun Activity.isKeyboardOpen(): Boolean {
    val visibleBounds = Rect()
    this.getRootView().getWindowVisibleDisplayFrame(visibleBounds)
    val heightDiff = getRootView().height - visibleBounds.height()
    val marginOfError = Math.round(this.convertDpToPx(50F))
    return heightDiff > marginOfError
}

fun Activity.isKeyboardClosed(): Boolean {
    return !this.isKeyboardOpen()
}

then create a listener class for checking the keyboard is open or not :

class KeyboardEventListener(
        private val activity: AppCompatActivity,
        private val callback: (isOpen: Boolean) -> Unit
) : LifecycleObserver {
     private val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
     private var lastState: Boolean = activity.isKeyboardOpen()
      override fun onGlobalLayout() {
            val isOpen = activity.isKeyboardOpen()
            if (isOpen == lastState) {
                return
            } else {
                dispatchKeyboardEvent(isOpen)
                lastState = isOpen
            }
        }
    }
    init {
        // Dispatch the current state of the keyboard
        dispatchKeyboardEvent(activity.isKeyboardOpen())
        // Make the component lifecycle aware
        activity.lifecycle.addObserver(this)
        registerKeyboardListener()
    }
    private fun registerKeyboardListener() {
        activity.getRootView().viewTreeObserver.addOnGlobalLayoutListener(listener)
    }
    private fun dispatchKeyboardEvent(isOpen: Boolean) {
        when {
            isOpen  -> callback(true)
            !isOpen -> callback(false)
        }
    }
    @OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE)
    @CallSuper
    fun onLifecyclePause() {
        unregisterKeyboardListener()
    }
    private fun unregisterKeyboardListener() {
        activity.getRootView().viewTreeObserver.removeOnGlobalLayoutListener(listener)
    }
}

and use it like this :

override fun onResume() {
      super.onResume()
      KeyboardEventListener(this) { isOpen -> // handle event }
  }

I hope you find it useful.

Upsetting answered 18/12, 2020 at 11:27 Comment(0)
N
-1

Solution with extra property in Activity\Fragment, but without any hypothetical hardcoded heights (like 100 etc) . Just add OnGlobalLayoutListener to your root view and save its initial height before keyboard will be shown:

var firstLoad         = true
var contentFullWeight = 0

override fun onViewCreated(layoutView: View, savedInstanceState: Bundle?) {
    super.onViewCreated(layoutView, savedInstanceState)

    view?.viewTreeObserver?.addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener {

        if(firstLoad){
            contentFullWeight = view?.height!!
            firstLoad = false
        }

        if (view?.height!! < contentFullWeight) {
            Log.d("TEZT_KEYBOARD", ">> KBD OPENED")
        } else {
            Log.d("TEZT_KEYBOARD", ">> KBD closed")
        }
    })

}
Nidorf answered 24/12, 2020 at 0:59 Comment(0)
B
-1
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/addresses_confirm_root_view"
android:orientation="vertical">

<---In the xml root use the id--->

final LinearLayout activityRootView = view.findViewById(R.id.addresses_confirm_root_view); activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect r = new Rect(); //r will be populated with the coordinates of your view that area still visible. activityRootView.getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView().getHeight() - r.height();
            if (heightDiff > 0.25 * activityRootView.getRootView().getHeight()) {
                // if more than 25% of the screen, its probably a keyboard...
                onkeyboard();

            } else {
                //Keyboard not visible
                offkeyboard();
            }
        }
    });
Breccia answered 6/2, 2021 at 8:53 Comment(2)
Where does r and activityRootView come fromFerrer
Hi, r is Rect r = new Rect(); // its given in the above code , u just need to see it properly. activityRootView is nothing but the view of your layout init for eg View view = super.onCreateView(inflater, container, savedInstanceState); View activityRootView = view; hope i made it clear.Breccia
A
-1

I built the listener using this answer and my task LoopingTask, thanks by the way.

Very easy to implement using SoftKeyboardListener from Viper Pack android library.

Just install the library then add this to your code :

Lava.app.addSoftKeyboardListener(context, new Lava.SoftKeyboardListener() {
@Override
public void onSoftKeyboardShow(EditText focusedview) {
// when shows
}
@Override
public void onSoftKeyboardHide(EditText focusedview) {
// when hides
}
});
Dislaimer : adding a lot of listeners may slow/crash your app, you still can use Lava.app.removeSoftKeyboardListeners() to remove all the previous listeners. Dislaimer 2 : creating a SoftKeyboardListener directly in activity onCreate will add a new one every time the activity resume itself, so make sure to limit that using a boolean :
boolean isListenerAdded;
...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
if (!isListenerAdded) {
isListenerAdded = true;
// create your listener here
}
...
}
...

Or just remove all the previous ones :

...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
Lava.app.removeSoftKeyboardListeners();
// create your listener here
...
}
...

Lava.app.removeSoftKeyboardListeners() removes all previous SoftKeyboardListeners.

Augmenter answered 24/3, 2021 at 11:22 Comment(1)
Hello... Inspect // Watch ... Your answer is poorly elaborated. It is useful to insert an effective response, with codes and references. Concluding a practical and efficient solution. This platform is not just any forum. We are the largest help and support center for other programmers and developers in the world. Review the terms of the community and learn how to post;Sturm
H
-2

when keyboard show

rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight() 

is true,else hide

Hemia answered 23/3, 2017 at 3:48 Comment(0)
R
-2

check with the below code :

XML CODE :

<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorParent"
    style="@style/parentLayoutPaddingStyle"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  .................


</android.support.constraint.ConstraintLayout>

JAVA CODE :

//Global Variable
android.support.constraint.ConstraintLayout activityRootView;
boolean isKeyboardShowing = false;
private  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
android.support.constraint.ConstraintLayout.LayoutParams layoutParams;




 //onCreate or onViewAttached
    activityRootView = view.findViewById(R.id.coordinatorParent);
        onGlobalLayoutListener = onGlobalLayoutListener();
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);


  //outside oncreate
  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener() {
        return new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(r);
                int screenHeight = activityRootView.getRootView().getHeight();
                int keypadHeight = screenHeight - r.bottom;

                if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
                    if (!isKeyboardShowing) {  // keyboard is opened
                        isKeyboardShowing = true;
                        onKeyboardVisibilityChanged(true);
                   }
                }
                else {
                    if (isKeyboardShowing) {   // keyboard is closed
                        isKeyboardShowing = false;
                        onKeyboardVisibilityChanged(false);
                    }
                }
            }//ends here
        };

    }


    void onKeyboardVisibilityChanged(boolean value) {
        layoutParams = (android.support.constraint.ConstraintLayout.LayoutParams)topImg.getLayoutParams();

        if(value){
           int length = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, getResources().getDisplayMetrics());
            layoutParams.height= length;
            layoutParams.width = length;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }else{
            int length1 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 175, getResources().getDisplayMetrics());
            layoutParams.height= length1;
            layoutParams.width = length1;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }
    }


    @Override
    public void onDetach() {
        super.onDetach();
        if(onGlobalLayoutListener != null) {
            activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
        }
    }
Roaster answered 20/9, 2019 at 6:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.