Instantly share code, notes, and snippets.

Embed
What would you like to do?
A solution to catch show/hide soft keyboard events in Android http://felhr85.net/2014/05/04/catch-soft-keyboard-showhidden-events-in-android/
/*
* Author: Felipe Herranz (felhr85@gmail.com)
* Contributors:Francesco Verheye (verheye.francesco@gmail.com)
* Israel Dominguez (dominguez.israel@gmail.com)
*/
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
public class SoftKeyboard implements View.OnFocusChangeListener
{
private static final int CLEAR_FOCUS = 0;
private ViewGroup layout;
private int layoutBottom;
private InputMethodManager im;
private int[] coords;
private boolean isKeyboardShow;
private SoftKeyboardChangesThread softKeyboardThread;
private List<EditText> editTextList;
private View tempView; // reference to a focused EditText
public SoftKeyboard(ViewGroup layout, InputMethodManager im)
{
this.layout = layout;
keyboardHideByDefault();
initEditTexts(layout);
this.im = im;
this.coords = new int[2];
this.isKeyboardShow = false;
this.softKeyboardThread = new SoftKeyboardChangesThread();
this.softKeyboardThread.start();
}
public void openSoftKeyboard()
{
if(!isKeyboardShow)
{
layoutBottom = getLayoutCoordinates();
im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
softKeyboardThread.keyboardOpened();
isKeyboardShow = true;
}
}
public void closeSoftKeyboard()
{
if(isKeyboardShow)
{
im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
isKeyboardShow = false;
}
}
public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback)
{
softKeyboardThread.setCallback(mCallback);
}
public void unRegisterSoftKeyboardCallback()
{
softKeyboardThread.stopThread();
}
public interface SoftKeyboardChanged
{
public void onSoftKeyboardHide();
public void onSoftKeyboardShow();
}
private int getLayoutCoordinates()
{
layout.getLocationOnScreen(coords);
return coords[1] + layout.getHeight();
}
private void keyboardHideByDefault()
{
layout.setFocusable(true);
layout.setFocusableInTouchMode(true);
}
/*
* InitEditTexts now handles EditTexts in nested views
* Thanks to Francesco Verheye (verheye.francesco@gmail.com)
*/
private void initEditTexts(ViewGroup viewgroup)
{
if(editTextList == null)
editTextList = new ArrayList<EditText>();
int childCount = viewgroup.getChildCount();
for(int i=0; i<= childCount-1;i++)
{
View v = viewgroup.getChildAt(i);
if(v instanceof ViewGroup)
{
initEditTexts((ViewGroup) v);
}
if(v instanceof EditText)
{
EditText editText = (EditText) v;
editText.setOnFocusChangeListener(this);
editText.setCursorVisible(true);
editTextList.add(editText);
}
}
}
/*
* OnFocusChange does update tempView correctly now when keyboard is still shown
* Thanks to Israel Dominguez (dominguez.israel@gmail.com)
*/
@Override
public void onFocusChange(View v, boolean hasFocus)
{
if(hasFocus)
{
tempView = v;
if(!isKeyboardShow)
{
layoutBottom = getLayoutCoordinates();
softKeyboardThread.keyboardOpened();
isKeyboardShow = true;
}
}
}
// This handler will clear focus of selected EditText
private final Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message m)
{
switch(m.what)
{
case CLEAR_FOCUS:
if(tempView != null)
{
tempView.clearFocus();
tempView = null;
}
break;
}
}
};
private class SoftKeyboardChangesThread extends Thread
{
private AtomicBoolean started;
private SoftKeyboardChanged mCallback;
public SoftKeyboardChangesThread()
{
started = new AtomicBoolean(true);
}
public void setCallback(SoftKeyboardChanged mCallback)
{
this.mCallback = mCallback;
}
@Override
public void run()
{
while(started.get())
{
// Wait until keyboard is requested to open
synchronized(this)
{
try
{
wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
int currentBottomLocation = getLayoutCoordinates();
// There is some lag between open soft-keyboard function and when it really appears.
while(currentBottomLocation == layoutBottom && started.get())
{
currentBottomLocation = getLayoutCoordinates();
}
if(started.get())
mCallback.onSoftKeyboardShow();
// When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
// and at some moment equals layoutBottom.
// That broke the previous logic, so I added this new loop to handle this.
while(currentBottomLocation >= layoutBottom && started.get())
{
currentBottomLocation = getLayoutCoordinates();
}
// Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
while(currentBottomLocation != layoutBottom && started.get())
{
synchronized(this)
{
try
{
wait(500);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
currentBottomLocation = getLayoutCoordinates();
}
if(started.get())
mCallback.onSoftKeyboardHide();
// if keyboard has been opened clicking and EditText.
if(isKeyboardShow && started.get())
isKeyboardShow = false;
// if an EditText is focused, remove its focus (on UI thread)
if(started.get())
mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
}
}
public void keyboardOpened()
{
synchronized(this)
{
notify();
}
}
public void stopThread()
{
synchronized(this)
{
started.set(false);
notify();
}
}
}
}
/*
* Android Manifest: android:windowSoftInputMode="adjustResize"
*/
/*
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();
}
@igorepst

This comment has been minimized.

igorepst commented Jan 4, 2015

Hi, thank you for this great code. However, could you describe, please, what is the purpose of editText.setCursorVisible(false); and why it was uncommented on 7 Oct?

@felHR85

This comment has been minimized.

Owner

felHR85 commented Jan 4, 2015

Hi! @igorepst
I had some problems with the blue cursor (Nexus 5 and 7 at least). When keyboard appears cursor starts to move around the screen weirdly and eventually disappears so I explicitly hid it. Other guys who sent me some improvements commented that line but I decided to uncomment it because I am not sure if this issue only applies to some devices.

@igorepst

This comment has been minimized.

igorepst commented Jan 5, 2015

Understood, thank you :)
Indeed, I see the blue cursor jumping on Nexus 4

@felHR85

This comment has been minimized.

Owner

felHR85 commented Jan 5, 2015

@igorepst I came up with a quick solution to the blue cursor problem. I added a 500 ms wait operation (probably it could be less time) in line 217 and It seems to work. Could you give it a try? :)

@igorepst

This comment has been minimized.

igorepst commented Jan 6, 2015

@felHR85 Looks really good. Will forward this cursor's bug fix to our QA team.
Yesterday I've tried to enable the cursor after 500 millis in various places in the code, but hadn't succeeded.
Thank you!

@felHR85

This comment has been minimized.

Owner

felHR85 commented Jan 6, 2015

Thanks!! @igorepst

@neutronstein

This comment has been minimized.

neutronstein commented Feb 23, 2015

Hi. Thank for this gist. I have a problem: Keyboard get closed automatically on landscape. Can you fix it? Thanks.

@felHR85

This comment has been minimized.

Owner

felHR85 commented Feb 23, 2015

Hi @neutronstein. I am going to check it out as soon as possible.

@Dave005

This comment has been minimized.

Dave005 commented Feb 23, 2015

@felHR85 nice implementation. I just tried it out and noticed something, your initEditText method traverse your layout for editTexts, but it will miss dynamical added EditTexts after the initialization, or am i missing something ?
I would say a quick fix would be changing the initEditTexts from private to public and adding a line like:
if(editTextList != null)
editTextList.clear()
for preventing double adding textfields

also, i can't see where editTextList is actually used.

And also thank you, you saved me a lot of work ;)

@felHR85

This comment has been minimized.

Owner

felHR85 commented Feb 26, 2015

Hi! @Dave005
Yes, it is adding editText during initialization.
I will try to check it out your ideas this weekend.
Thank you!

@dashhunds

This comment has been minimized.

dashhunds commented Mar 4, 2015

Any clue why the keyboardcallback isn't working anymore once you rotate your screen?

@felHR85

This comment has been minimized.

Owner

felHR85 commented Mar 7, 2015

@neutronstein I finally managed to find what was happening in landscape mode. Basically, for some reason, currentBottomLocation keeps the same value of layoutBottom, so it quits from the loop in line 211 when it shouldn't. The easiest way to solve it is adding this option for each EditText in your layout:
android:imeOptions="flagNoExtractUi

With that option keyboard does not fill the whole screen (as by default does ) but it works as usual.

@neutronstein

This comment has been minimized.

neutronstein commented Mar 8, 2015

Thanks @felHR85

@adc-msingla

This comment has been minimized.

adc-msingla commented Mar 29, 2015

I tried using this code and i am setting the some imageview to visible , invisible in show hide callbacks. But it throws the exception "Only the original thread that created a view hierarchy can touch its views" . It seems from the code that you are creating a new thread and its not returning on main thread. Can you please check that and update

@felHR85

This comment has been minimized.

Owner

felHR85 commented Mar 30, 2015

Hi @adc-msingla
Show/Hide callbacks are not in UI thread because they are called from a thread which is periodically checking the layout to find when keyboard is shown or not. Try this:

@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
                ...
            }
        });

}   
@ghost

This comment has been minimized.

ghost commented Apr 7, 2015

This code not working at all. When I open up the soft keyboard then the code running down the onSoftKeyboardShow and then running down the onSoftKeyboardHide too. Tested on Android 5.1 Nexus 6.

When I just close the soft kb. nothing happening..

@felHR85

This comment has been minimized.

Owner

felHR85 commented Apr 7, 2015

Hi @lacasrac. Is your app running on landscape?

@DenisMondon

This comment has been minimized.

DenisMondon commented Apr 14, 2015

Same as @lacasrac, it calls onSoftKeyboardShow then onSoftKeyboardHide but it does not hide the keyboard oO (on portrait and landscape)
5.0.2 Nexus 7

@UsherBaby

This comment has been minimized.

UsherBaby commented May 11, 2015

Does it work when android:windowSoftInputMode="adjustPan" ?

@ahmedDiva

This comment has been minimized.

ahmedDiva commented May 14, 2015

Thanks for this gist, but this code doesn't work, on Samsung Galaxy s5 kitkat and others, same as @lacasrac ... portrait and landscape orientation.

@Rainer-Lang

This comment has been minimized.

Rainer-Lang commented Jun 23, 2015

Any news about the problems? Maybe solved?

@fsznigir

This comment has been minimized.

fsznigir commented Jul 9, 2015

I have strange problem. I have got two Edittext in view. When I select the first one everything works fine, but when I selected the second one I get hide event

@eladhackim

This comment has been minimized.

eladhackim commented Jul 13, 2015

Hi, I have a weird issue where if i change some views visibly to GONE in onSoftKeyboardShow - than onSoftKyboardHide gets called (without keyboard being closed). any idea how can I use this to hide some views while keyboard is shown, and than show them when keyboard is hidden?

@felHR85

This comment has been minimized.

Owner

felHR85 commented Jul 13, 2015

Hi @eladhackim and others. I've abandoned this for some months but it is time to check all the issues. I will spend time this weekend and probably I will release a little example app with the code in github.
Felipe

@EtienneHanser

This comment has been minimized.

EtienneHanser commented Oct 4, 2015

Thank you very much for this solution.

Is there any way to detect when the keyboard finished opening? I'm trying to smooth scrool to the last position of a reclycler view when the keyboard opens. Sometimes the scroll is done before the keyboard is fully opened. Since the Recyclerview gets resized by keyboard opening, it doesn't show the last item anymore :(

@kienntk

This comment has been minimized.

kienntk commented Oct 15, 2015

@felHR85: I have same question like @UsherBaby: Does it work when android:windowSoftInputMode="adjustPan" ?, bro

@thalissondev

This comment has been minimized.

thalissondev commented Oct 16, 2015

Thank you. Work for me.

@EtienneHanser

This comment has been minimized.

EtienneHanser commented Oct 31, 2015

@felHR85

This comment has been minimized.

Owner

felHR85 commented Nov 2, 2015

Sorry! @EtienneHanser and many others with troubles with this snippet but I got too busy at this moment. This is really a hack (in both good and bad ways) so in some cases probably won't work because I soon realized that there are many differences in the way Android draws (for example the EditText displays the keyboard and the redraw is different than other cases, that was corrected).

I didn't try with the RecyclerView yet (I just started to use them recently!) but it would be nice if you can send me a little example to give it a look. Maybe it is not difficult and can be done quickly!

Of course, I encouraged everybody to add, modify and submit changes (gist doesn't have pull requests but you can send me an email with your versions and I will check it out).

Thank you guys.

Felipe

@cakrabirawa

This comment has been minimized.

cakrabirawa commented Feb 24, 2016

Hi, I try implements your code in my project and i have some error. My Phone Galaxy s4 Mini Kitkat

this is my error:

02-24 07:47:54.133 5911-6578/gramedia.com.gracom E/AndroidRuntime: FATAL EXCEPTION: Thread-2061
Process: gramedia.com.gracom, PID: 5911
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.(Handler.java:200)
at android.os.Handler.(Handler.java:114)
at android.widget.Toast$TN.(Toast.java:457)
at android.widget.Toast.(Toast.java:119)
at android.widget.Toast.makeText(Toast.java:286)
at gramedia.com.gracom.ActivityCheckIn$1.onSoftKeyboardShow(ActivityCheckIn.java:116)
at gramedia.com.gracom.SoftKeyboard$SoftKeyboardChangesThread.run(SoftKeyboard.java:201)

could you help me, whats wrong in my code ?

in my activity i wrote this attribute: android:windowSoftInputMode="adjustResize"

thank you very much.

sorry my english not good

@AnswerZhao

This comment has been minimized.

AnswerZhao commented Apr 28, 2016

thank you very much. it resolved my hard issues.

@arshu-dev

This comment has been minimized.

arshu-dev commented May 16, 2016

This does not work properly with fragments. Keyboard show method is being called but the Hide method is not being called. For Activity it is working fine.

@wsf5918

This comment has been minimized.

wsf5918 commented Nov 12, 2016

@arshu-dev did you finally get the solution?

@BoxResin

This comment has been minimized.

BoxResin commented Jan 1, 2018

It works. Thank you! 💯

@shraddhaGadesha815

This comment has been minimized.

shraddhaGadesha815 commented Jan 11, 2018

I have edittext in listview items .After back press when one item's edittext has focus and keypad is open ,method hidekeypad is not calling.
This class I want to use in listview but it is not working pls help for same

@stasbar

This comment has been minimized.

stasbar commented Aug 2, 2018

@thanhthein

This comment has been minimized.

thanhthein commented Sep 10, 2018

Your code was not worked for me :'(
SoftKeyboard.SoftKeyboardChanged()
this method wasn't called ??

I use redmi note 4x, API 24

@steam0111

This comment has been minimized.

steam0111 commented Oct 5, 2018

Guys it doen't work don't spent time on that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment