Skip to content

Instantly share code, notes, and snippets.

@felHR85
Last active February 17, 2024 23:11
Show Gist options
  • Save felHR85/6070f643d25f5a0b3674 to your computer and use it in GitHub Desktop.
Save felHR85/6070f643d25f5a0b3674 to your computer and use it in GitHub Desktop.
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 ([email protected])
* Contributors:Francesco Verheye ([email protected])
* Israel Dominguez ([email protected])
*/
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();
}
}
}
}
/*
* 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();
}
@kienntk
Copy link

kienntk commented Oct 15, 2015

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

@thalissondev-xx
Copy link

Thank you. Work for me.

@EtienneHanser
Copy link

@felHR85
Copy link
Author

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
Copy link

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
Copy link

thank you very much. it resolved my hard issues.

@arshu-dev
Copy link

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.

@ManBali
Copy link

ManBali commented Nov 12, 2016

@arshu-dev did you finally get the solution?

@BoxResin
Copy link

BoxResin commented Jan 1, 2018

It works. Thank you! 💯

@shraddhaGadesha815
Copy link

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

@stanbar
Copy link

stanbar commented Aug 2, 2018

@thanhthein
Copy link

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

I use redmi note 4x, API 24

@steam0111
Copy link

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

@Prayha
Copy link

Prayha commented Jul 22, 2020

thank you very much.

@amoizesmail
Copy link

Is there a Kotlin version of this?

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