-
-
Save felHR85/6070f643d25f5a0b3674 to your computer and use it in GitHub Desktop.
/* | |
* 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(); | |
} |
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
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
thank you very much. it resolved my hard issues.
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.
@arshu-dev did you finally get the solution?
It works. Thank you! 💯
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
Here is Kotlin friendly change :)
https://gist.github.com/stasbar/095cb359846c225156fb15b3cc1eed86
Your code was not worked for me :'(
SoftKeyboard.SoftKeyboardChanged()
this method wasn't called ??
I use redmi note 4x, API 24
Guys it doen't work don't spent time on that
thank you very much.
Is there a Kotlin version of this?
@ https://gist.github.com/EtienneHanser
still not answer :(