How to capture the "virtual keyboard show/hide" event in Android?
Asked Answered
K

21

256

I would like to alter the layout based on whether the virtual keyboard is shown or not. I've searched the API and various blogs but can't seem to find anything useful.

Is it possible?

Thanks!

Kizzykjersti answered 30/11, 2010 at 9:47 Comment(1)
possible duplicate of Android EditText, soft keyboard show/hide event?Cheater
E
82

2020 Update

This is now possible:

On Android 11, you can do

view.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback {
    override fun onEnd(animation: WindowInsetsAnimation) {
        super.onEnd(animation)
        val showingKeyboard = view.rootWindowInsets.isVisible(WindowInsets.Type.ime())
        // now use the boolean for something
    }
})

You can also listen to the animation of showing/hiding the keyboard and do a corresponding transition.

I recommend reading Android 11 preview and the corresponding documentation

Before Android 11

However, this work has not been made available in a Compat version, so you need to resort to hacks.

You can get the window insets and if the bottom insets are bigger than some value you find to be reasonably good (by experimentation), you can consider that to be showing the keyboard. This is not great and can fail in some cases, but there is no framework support for that.

This is a good answer on this exact question https://mcmap.net/q/109497/-how-to-capture-the-quot-virtual-keyboard-show-hide-quot-event-in-android. Alternatively, here's a page giving some different approaches to achieve this pre Android 11:

https://developer.salesforce.com/docs/atlas.en-us.noversion.service_sdk_android.meta/service_sdk_android/android_detecting_keyboard.htm


Note

This solution will not work for soft keyboards and onConfigurationChanged will not be called for soft (virtual) keyboards.


You've got to handle configuration changes yourself.

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

Sample:

// from the link above
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    
    // 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();
    }
}

Then just change the visibility of some views, update a field, and change your layout file.

Enneagon answered 6/12, 2010 at 10:56 Comment(12)
@shiami try newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO ~ChrisEconah
This only works if you've registered the activity to listen to the configChanges you want in the AndroidManifest.Goudy
please update your answer and tell that it doesn't work for soft keyboard. I wasted my half day trying your code. And then saw these comments.Rale
does this work in a fragment as well? I have tried this solution but onConfigurationChanged() does not seem to be called at all.Buxom
yes it works in fragment..just need to declare the configChanges attribute in the activity in the manifest and override onConfigurationChanged() in the fragment class.Buxom
I have tried this with android:configChanges="orientation|keyboardHidden" in AndroidManifest.xml and onConfigurationChanged() indeed does not get fired. This is within android.support.v7.app.ActionBarActivityExterminatory
This is not working for "virtual" keyboards which was the original question.Welltimed
Backing with shiami,Shiri Hrw,brummfondel, this solution doesnot work for soft keyboard. Also, per Muxa,onConfigurationChanged() doesnot even be called on softkeyboard launch.Biron
Well, the question was about the SOFT KEYBOARD, why is the accepted answer about an hardware keyboard? -1 !Occlusive
lets notice that Android sdk21 (lolipop) hasn't method to handle that callback it even if You use WIndowInsectsCompact, works only following decorView.viewTreeObserver.addOnGlobalLayoutListener(...)Wallflower
For devices prior to Android 11 androidx.core:core-ktx:1.5.0 provides compatibity solution: ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { _, insets -> val isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) insets } Make sure to remove listener properlyDeglutinate
Object is not abstract and does not implement abstract base class member public abstract fun onProgress(p0: WindowInsets, p1: (Mutable)List<WindowInsetsAnimation!>): WindowInsets defined in android.view.WindowInsetsAnimation.CallbackAsyndeton
J
60

I did this way:

Add OnKeyboardVisibilityListener interface.

public interface OnKeyboardVisibilityListener {
    void onVisibilityChanged(boolean visible);
}

HomeActivity.java:

public class HomeActivity extends Activity implements OnKeyboardVisibilityListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sign_up);
    // Other stuff...
    setKeyboardVisibilityListener(this);
}

private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
    final View parentView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
    parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private boolean alreadyOpen;
        private final int defaultKeyboardHeightDP = 100;
        private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
        private final Rect rect = new Rect();

        @Override
        public void onGlobalLayout() {
            int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
            parentView.getWindowVisibleDisplayFrame(rect);
            int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == alreadyOpen) {
                Log.i("Keyboard state", "Ignoring global layout change...");
                return;
            }
            alreadyOpen = isShown;
            onKeyboardVisibilityListener.onVisibilityChanged(isShown);
        }
    });
}


@Override
public void onVisibilityChanged(boolean visible) {
    Toast.makeText(HomeActivity.this, visible ? "Keyboard is active" : "Keyboard is Inactive", Toast.LENGTH_SHORT).show();
  }
}

Hope this would help you.

Jacktar answered 28/3, 2016 at 9:13 Comment(7)
Thanks, worked for me! If you want just adjust your RecyclerView, see solution here: https://mcmap.net/q/111401/-android-recyclerview-not-resizing-when-softinput-keyboard-appearsWheeze
Perfect reusable implementation, worked into Activity or Fragment, thanksRepresentationalism
@DavidPapirov, you pasted a link to a RecyclerView, but not mentioned about it here.Ebonize
I tried all other solutions but at the end only this worked. thanks!Pugh
Note : If in manifest we specify adjustPan or adjustResize then only it works otherwise it doesn't work.Pintle
one more improvement could be removing of global layout listener in onDestroy of activity. Something like this: if (layoutListener != null) { final View parentView = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0); parentView.getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener); }Geochronology
@Hiren Patel This is excellent - thank you! Can I ask you where "100" comes from for defaultKeyboardHeightDP? Is this the minimum keyboard height for any android device? And what about the additional 48 for devices >= Lollipop? Where does this data come from? Thank you! <3Mcfarland
W
59

This may not be the most effective solution. But this worked for me every time... I call this function where ever i need to listen to the softKeyboard.

boolean isOpened = false;

public void setListenerToRootView() {
    final View activityRootView = getWindow().getDecorView().findViewById(android.R.id.content);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
            if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.
                Toast.makeText(getApplicationContext(), "Gotcha!!! softKeyboardup", 0).show();

                if (isOpened == false) {
                    //Do two things, make the view top visible and the editText smaller
                }
                isOpened = true;
            } else if (isOpened == true) {
                Toast.makeText(getApplicationContext(), "softkeyborad Down!!!", 0).show();
                isOpened = false;
            }
        }
    });
}

Note: This approach will cause issues if the user uses a floating keyboard.

Westering answered 26/8, 2013 at 13:53 Comment(18)
I tried this in a list fragment populated by a cursor loader and custom adapter, and an alphabet indexer section scroller. There is a search view widget in the action bar that opens up the soft keyboard. I call this function in onActivityCreated(). The problem i face is that onGlobalLayout() keeps getting triggered dozens of times with a height difference of exactly 146 when i scroll using the vertical scrollbar( the keyboard had never even popped up), when tested on a Samsung note 2. Not sure if you are getting the same behaviour, but i can't use this solution.Buxom
addOnGlobalLayoutListener?Rubella
Works, but i have bad feeling about small keyboard like MinimumDepressor
can you elaborate? @KirillKulakovWestering
@Westering see: play.google.com/store/apps/…Depressor
okay, i will have a look at it. @KirillKulakov, thanks for notifying.Westering
Now, that is a real concern though.Westering
Thanks buddy.. Perfectly working on this function about edittext soft keyboard auto maintain either show or hide :).Naturalize
i don't think that this will work for floating keyboards, like samsung'sTeeming
I havent tried it . But what is this floating keyboard that you are talking about?Westering
Not working for fragments - always says it is showingMendive
This smells like a memory leak. You are adding a listener to a global object, who will hold on to you and never let you go.Gotcher
if scrollview is a root container, this listener always return upKerakerala
This one also won't work for Activities set with android:windowSoftInputMode="adjustPan", or adjustResize with a fullscreen window, as the layout is never resized.Grovergroves
This only works with adjustResize. For adjustPan, the heightDiff never changes.Erickson
Very helpful!! But need to adjust the condition i.e. (heightDiff > 100) based on your requirements..Tumble
Thanks you so much for this code. It is really very very helpful.Oleoresin
why are you comparing a boolean?Dumortierite
M
38

If you want to handle show/hide of IMM (virtual) keyboard window from your Activity, you'll need to subclass your layout and override onMesure method(so that you can determine the measured width and the measured height of your layout). After that set subclassed layout as main view for your Activity by setContentView(). Now you'll be able to handle IMM show/hide window events. If this sounds complicated, it's not that really. Here's the code:

main.xml

   <?xml version="1.0" encoding="utf-8"?>
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal" >
        <EditText
             android:id="@+id/SearchText" 
             android:text="" 
             android:inputType="text"
             android:layout_width="fill_parent"
             android:layout_height="34dip"
             android:singleLine="True"
             />
        <Button
             android:id="@+id/Search" 
             android:layout_width="60dip"
             android:layout_height="34dip"
             android:gravity = "center"
             />
    </LinearLayout>

Now inside your Activity declare subclass for your layout (main.xml)

    public class MainSearchLayout extends LinearLayout {

    public MainSearchLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");

        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight){
            // Keyboard is shown

        } else {
            // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

You can see from the code that we inflate layout for our Activity in subclass constructor

inflater.inflate(R.layout.main, this);

And now just set content view of subclassed layout for our Activity.

public class MainActivity extends Activity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MainSearchLayout searchLayout = new MainSearchLayout(this, null);

        setContentView(searchLayout);
    }

    // rest of the Activity code and subclassed layout...

}
Mobility answered 14/9, 2011 at 22:9 Comment(6)
I need to investigate further but I have my doubts as to whether this would work in my case for small dialog on a large screen device for which the layout measurements would not be affected by presence of a keyboard.Obsequent
It doesn't work for android:windowSoftInputMode="adjustPan". I wanted that my screen shouldn't get shrinked after soft keyboard appears. Can you please tell any fix so that it works even for adjustPanRale
This is not working,it always goes to the else portion here if (actualHeight > proposedheight){ // Keyboard is shown } else { // Keyboard is hidden }Cedar
You can also use a Custom View with that same idea, follows an example gist.github.com/juliomarcos/8ca307cd7eca607c8547Oldtimer
This solution is working great for me. Tested it on 5" and old 3" devices and it does exactly what it's supposed to do. I only use the "keyboard is shown" part and it fires when the softkeys are done popping up in portrait mode; exactly as expected. That means there are no odd events and normal EditText behaviour in landscape (fullscreen text field) is not handled by this.Onrush
Won't work for Activities set with android:windowSoftInputMode="adjustPan", or adjustResize with a fullscreen window, as the layout is never resized.Grovergroves
E
29

Like @amalBit's answer, register a listener to global layout and calculate the difference of dectorView's visible bottom and its proposed bottom, if the difference is bigger than some value(guessed IME's height), we think IME is up:

    final EditText edit = (EditText) findViewById(R.id.edittext);
    edit.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (keyboardShown(edit.getRootView())) {
                Log.d("keyboard", "keyboard UP");
            } else {
                Log.d("keyboard", "keyboard Down");
            }
        }
    });

private boolean keyboardShown(View rootView) {

    final int softKeyboardHeight = 100;
    Rect r = new Rect();
    rootView.getWindowVisibleDisplayFrame(r);
    DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
    int heightDiff = rootView.getBottom() - r.bottom;
    return heightDiff > softKeyboardHeight * dm.density;
}

the height threshold 100 is the guessed minimum height of IME.

This works for both adjustPan and adjustResize.

Erickson answered 11/10, 2015 at 7:57 Comment(2)
I am just about to pull my hair!! You saved my hair ;)Torse
It s the only good answer here, it works on soft keyboard perfect, thank youHelper
A
23

Based on the Code from Nebojsa Tomcic I've developed the following RelativeLayout-Subclass:

import java.util.ArrayList;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

public class KeyboardDetectorRelativeLayout extends RelativeLayout {

    public interface IKeyboardChanged {
        void onKeyboardShown();
        void onKeyboardHidden();
    }

    private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();

    public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardDetectorRelativeLayout(Context context) {
        super(context);
    }

    public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.add(listener);
    }

    public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.remove(listener);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight) {
            notifyKeyboardShown();
        } else if (actualHeight < proposedheight) {
            notifyKeyboardHidden();
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void notifyKeyboardHidden() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardHidden();
        }
    }

    private void notifyKeyboardShown() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardShown();
        }
    }

}

This works quite fine... Mark, that this solution will just work when Soft Input Mode of your Activity is set to "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"

Antoinetteanton answered 15/11, 2011 at 10:40 Comment(3)
It doesn't work for android:windowSoftInputMode="adjustPan". I wanted that my screen shouldn't get shrinked after soft keyboard appears. Can you please tell any fix so that it works even for adjustPanRale
This one also won't work for Activities set with android:windowSoftInputMode="adjustPan", or adjustResize with a fullscreen window, as the layout is never resized.Grovergroves
it triger quite a few times.Almanac
A
16

Pre-android 11 solution:

As androidx.core 1.5.0 is released, this is what i do to listen to keyboard show/hide event in pre-android 11 devices.

gradle:

implementation "androidx.core:core-ktx:1.5.0"

fragment:

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val view  = activity?.window?.decorView ?: return
        ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
            val showingKeyboard = insets.isVisible(WindowInsetsCompat.Type.ime())
            if(showingKeyboard){
                //do something
            }
            insets
        }
    }

make sure you remove the listener when the view destroy to avoid memory leak. This solution also only works when the software input mode is adjustResize, setOnApplyWindowInsetsListener won't trigger if it is adjustPan, if anyone has an idea on how to make it work with adjustPan, please share.

Note that according to the doc,

* When running on devices with API Level 29 and before, the returned value is an
* approximation based on the information available. This is especially true for the {@link
* Type#ime IME} type, which currently only works when running on devices with SDK level 23
* and above.
*

insets.isVisible(ime) should only work on devices with SDK level above 23

Aberdare answered 10/6, 2021 at 10:45 Comment(6)
ViewCompat.setOnApplyWindowInsetsListener changes color of system navigation controls!Asyndeton
https://mcmap.net/q/111402/-viewcompat-setonapplywindowinsetslistener-changes-background-of-system-navigation-bar/7767664Asyndeton
Here it says API-21, but I cannot find the doc-note listed so I'm not sure it refers to the same: developer.android.com/reference/androidx/core/view/…Stanislaus
This will work on Android 11 as well.Dumbhead
How to remove the listener?Liaison
Thanks for view = activity?.window?.decorView!Ebonize
D
12

Nebojsa's solution almost worked for me. When I clicked inside a multi-line EditText it knew the keyboard was displayed, but when I started typing inside the EditText, the actualHeight and proposedHeight were still the same so it did not know they keyboard was still displayed. I made a slight modification to store the max height and it works fine. Here is the revised subclass:

public class CheckinLayout extends RelativeLayout {

    private int largestHeight;

    public CheckinLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.checkin, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        largestHeight = Math.max(largestHeight, getHeight());

        if (largestHeight > proposedheight)
            // Keyboard is shown
        else
            // Keyboard is hidden

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
Dolce answered 11/1, 2012 at 21:44 Comment(0)
J
11

I solve this by overriding onKeyPreIme(int keyCode, KeyEvent event) in my custom EditText.

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        //keyboard will be hidden
    }
}
Jurisdiction answered 10/2, 2014 at 14:54 Comment(3)
How to use it in Fragment or Activity?@qbaitFatling
It doesn't work, it can be called only when I leave the page in my case.Steib
this is method from EditText, see this answer: https://mcmap.net/q/36355/-detecting-when-user-has-dismissed-the-soft-keyboardHirohito
D
10

Not sure if anyone post this. Found this solution simple to use!. The SoftKeyboard class is on gist.github.com. But while keyboard popup/hide event callback we need a handler to properly do things on UI:

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
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
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });
    }

    @Override
    public void onSoftKeyboardShow() 
    {
        // Code here
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });

    }   
});
Dahna answered 30/12, 2014 at 2:17 Comment(1)
here is the Git to get SoftkeyBoard "gist.github.com/felHR85/…"Illustrator
H
4

I have sort of a hack to do this. Although there doesn't seem to be a way to detect when the soft keyboard has shown or hidden, you can in fact detect when it is about to be shown or hidden by setting an OnFocusChangeListener on the EditText that you're listening to.

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            //hasFocus tells us whether soft keyboard is about to show
        }
    });

NOTE: One thing to be aware of with this hack is that this callback is fired immediately when the EditText gains or loses focus. This will actually fire right before the soft keyboard shows or hides. The best way I've found to do something after the keyboard shows or hides is to use a Handler and delay something ~ 400ms, like so:

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            new Handler().postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        //do work here
                    }
                }, 400);
        }
    });
Heterocyclic answered 26/7, 2015 at 21:12 Comment(2)
It doesn't work, otherwise. OnFocusChangeListener only tells whether EditText has focus after the state changed. But the IME may be hidden when EditText has focus, how to detect this case?Steib
This is the simplest solution. @DysaniazzZ, to detect when the IME is hidden with the "back" key, override onKeyPreIme on the EditText and watch for keyCode==KEYCODE_BACKAft
D
3

Sander ,I believe you are trying to show the view blocked by the soft keyboard. Try this http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html.

Duct answered 3/12, 2010 at 14:38 Comment(1)
The first trackback on this URL points at RussenReaktor's weblog which mentions adding android:windowSoftInputMode="adjustPan" to the Activity's manifest. This worked great for me.Dewittdewlap
F
3

In spite of what the most upvoted solution on this page says, there is a ViewCompat version of setWindowInsetsAnimationCallback that works all the way to Android 21.

So now, the approach in that solution works all the way back to 21.

Source: https://developer.android.com/reference/androidx/core/view/ViewCompat#setWindowInsetsAnimationCallback(android.view.View,androidx.core.view.WindowInsetsAnimationCompat.Callback)

Forfar answered 1/9, 2022 at 22:12 Comment(0)
Z
2

I have solve the problem on single line textview back coding.

package com.helpingdoc;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

public class MainSearchLayout extends LinearLayout {
    int hieght = 0;
    public MainSearchLayout(Context context, AttributeSet attributeSet) {

        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");
       if(getHeight()>hieght){
           hieght = getHeight();
       }
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();
        System.out.println("....hieght = "+ hieght);
        System.out.println("....actualhieght = "+ actualHeight);
        System.out.println("....proposedheight = "+ proposedheight);
        if (actualHeight > proposedheight){
            // Keyboard is shown


        } else if(actualHeight<proposedheight){
            // Keyboard is hidden

        }

        if(proposedheight == hieght){
             // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}
Zaria answered 24/6, 2013 at 7:9 Comment(2)
It doesn't work for android:windowSoftInputMode="adjustPan". I wanted that my screen shouldn't get shrinked after soft keyboard appears. Can you please tell any fix so that it works even for adjustPanRale
When function hide/show then this listener method is calling twice or thrice. I don't what is exactly the problem.Hembree
B
2

You can also check for first DecorView's child bottom padding. It will be set to non-zero value when keyboard is shown.

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    View view = getRootView();
    if (view != null && (view = ((ViewGroup) view).getChildAt(0)) != null) {
        setKeyboardVisible(view.getPaddingBottom() > 0);
    }
    super.onLayout(changed, left, top, right, bottom);
}
Bindery answered 10/12, 2013 at 12:33 Comment(0)
R
2

The above answer of @Filipkowicz's works fine in Android API < 30. Since Android API 30 we should use setWindowInsetsAnimationCallback. So below answer combines the both method in order to work API 21 - 30.

private fun isKeyboardVisible(insets: WindowInsets): Boolean {
    val insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets)
    val systemWindow = insetsCompat.systemWindowInsets
    val rootStable = insetsCompat.stableInsets
    if (systemWindow.bottom > rootStable.bottom) {
        // This handles the adjustResize case on < API 30, since
        // systemWindow.bottom is probably going to be the IME
        return true
    }
    return false
}

@JvmStatic
@BindingAdapter("goneWhenKeyboardVisible")
fun View.goneWhenKeyboardVisible() {
    if (isRPlus()) {
        setWindowInsetsAnimationCallback(object :
            WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
            override fun onProgress(
                insets: WindowInsets,
                runningAnimations: MutableList<WindowInsetsAnimation>
            ): WindowInsets {
                return insets
            }

            override fun onStart(
                animation: WindowInsetsAnimation,
                bounds: WindowInsetsAnimation.Bounds
            ): WindowInsetsAnimation.Bounds {
                if (isVisible)
                    isVisible = !rootWindowInsets.isVisible(WindowInsets.Type.ime())
                return super.onStart(animation, bounds)
            }

            override fun onEnd(animation: WindowInsetsAnimation) {
                super.onEnd(animation)
                if (!isVisible)
                    isVisible = !rootWindowInsets.isVisible(WindowInsets.Type.ime())
            }
        })
    } else {
        setOnApplyWindowInsetsListener { _, insets ->
            isVisible = !isKeyboardVisible(insets)
            insets
        }
    }
}
Raccoon answered 28/5, 2021 at 6:49 Comment(0)
K
1

Hide|Show events for keyboard can be listened through simple hack in OnGlobalLayoutListener :

 final View activityRootView = findViewById(R.id.top_root);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();

                if (heightDiff > 100) {
                    // keyboard is up
                } else {
                    // keyboard is down
                }
            }
        });

Here activityRootView is your Activity's root view.

Keef answered 11/8, 2015 at 8:38 Comment(1)
my heightDiff is 160 on the start and 742 with kbd, so I had have to introduce and set initialHeightDiff on the startGuardafui
I
1

For Kotlin users, this is inspired from this answer in the comments, you can create an extension:

import android.graphics.Rect
import android.view.View
import android.widget.TextView

const val SOFT_KEYBOARD_HEIGHT = 100

fun TextView.addOnKeyboardVisibilityListener(
    onKeyboardShown: () -> Unit,
    onKeyboardHidden: () -> Unit,
) {
    viewTreeObserver.addOnGlobalLayoutListener {
        if(rootView.isKeyboardShown()) {
            onKeyboardShown()
        } else {
            onKeyboardHidden()
        }
    }
}

fun View.isKeyboardShown(): Boolean =
    Rect().let { rect ->
        rootView.getWindowVisibleDisplayFrame(rect)
        rect
    }.let {
        rootView.bottom - it.bottom
    }.let { heightDiff ->
        heightDiff > SOFT_KEYBOARD_HEIGHT * rootView.resources.displayMetrics.density
    }

and you can use it as:

editText.addOnKeyboardVisibilityListener(
    onKeyboardShown = {
        // TODO
    },
    onKeyboardHidden = {
        // TODO
    }
)
Idleman answered 22/2, 2023 at 16:34 Comment(0)
P
0

using viewTreeObserver for easily get the keyboard event.

layout_parent.viewTreeObserver.addOnGlobalLayoutListener {
            val r = Rect()
            layout_parent.getWindowVisibleDisplayFrame(r)
            if (layout_parent.rootView.height - (r.bottom - r.top) > 100) { // if more than 100 pixels, its probably a keyboard...
                Log.e("TAG:", "keyboard open")
            } else {
                Log.e("TAG:", "keyboard close")
            }
        }

** layout_parent is your view like edit_text.parent

Paraglider answered 22/1, 2020 at 13:3 Comment(0)
X
0

what I did is created simple binding to hide view when keyboard is visible. Solution is based on current AndroidX implementation for WindowInsetsCompat which is still in beta (androidx core 1.5) - source

private fun isKeyboardVisible(insets: WindowInsets): Boolean {
    val insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets)
    val systemWindow = insetsCompat.systemWindowInsets
    val rootStable = insetsCompat.stableInsets
    if (systemWindow.bottom > rootStable.bottom) {
        // This handles the adjustResize case on < API 30, since
        // systemWindow.bottom is probably going to be the IME
        return true
    }
    return false
}

@BindingAdapter("goneWhenKeyboardVisible")
fun View.goneWhenKeyboardVisible(enabled: Boolean) {
    if (enabled) {
        setOnApplyWindowInsetsListener { view, insets ->
            visibility = if (isKeyboardVisible(insets)) GONE else VISIBLE
            insets
        }
    } else {
        setOnApplyWindowInsetsListener(null)
        visibility = VISIBLE
    }
}

usage:

<FrameLayout
                android:id="@+id/bottom_toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:goneWhenKeyboardVisible="@{true}"
                />
Xavier answered 12/1, 2021 at 11:2 Comment(1)
it returns false all the timeAsyndeton
G
-2

Nebojsa Tomcic's answer wasn't helpful for me. I have RelativeLayout with TextView and AutoCompleteTextView inside it. I need to scroll the TextView to the bottom when the keyboard is showed and when it's hidden. To accomplish this I overrode onLayout method and it works fine for me.

public class ExtendedLayout extends RelativeLayout
{
    public ExtendedLayout(Context context, AttributeSet attributeSet)
    {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);

        if (changed)
        {
            int scrollEnd = (textView.getLineCount() - textView.getHeight() /
                textView.getLineHeight()) * textView.getLineHeight();
            textView.scrollTo(0, scrollEnd);
        }
    }
}
Good answered 22/4, 2012 at 7:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.