Skip to content

Instantly share code, notes, and snippets.

@Piasy
Forked from felHR85/SoftKeyboard.java
Created June 6, 2015 04:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Piasy/2b79a25aa5ef360066f2 to your computer and use it in GitHub Desktop.
Save Piasy/2b79a25aa5ef360066f2 to your computer and use it in GitHub Desktop.
package com.younglive.livestreaming.utils.view;
/*
* Author: Felipe Herranz (felhr85@gmail.com)
* Contributors:Francesco Verheye (verheye.francesco@gmail.com)
* Israel Dominguez (dominguez.israel@gmail.com)
*/
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;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
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);
this.softKeyboardThread.start();
mHandler = new InnerHandler(this);
}
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<>();
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 InnerHandler mHandler;
private static class SoftKeyboardChangesThread extends Thread {
WeakReference<SoftKeyboard> mSoftKeyboardWeakReference;
private AtomicBoolean started;
private SoftKeyboardChanged mCallback;
public SoftKeyboardChangesThread(SoftKeyboard softKeyboard) {
started = new AtomicBoolean(true);
mSoftKeyboardWeakReference = new WeakReference<>(softKeyboard);
}
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();
}
}
if (mSoftKeyboardWeakReference.get() != null &&
mSoftKeyboardWeakReference.get().mHandler != null) {
int currentBottomLocation = mSoftKeyboardWeakReference.get().getLayoutCoordinates();
// There is some lag between open soft-keyboard function and when it really appears.
while(currentBottomLocation == mSoftKeyboardWeakReference.get().layoutBottom && started.get()) {
currentBottomLocation = mSoftKeyboardWeakReference.get().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 >= mSoftKeyboardWeakReference.get().layoutBottom && started.get()) {
currentBottomLocation = mSoftKeyboardWeakReference.get().getLayoutCoordinates();
}
// Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
while(currentBottomLocation != mSoftKeyboardWeakReference.get().layoutBottom && started.get()) {
synchronized(this) {
try {
wait(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
currentBottomLocation = mSoftKeyboardWeakReference.get().getLayoutCoordinates();
}
if(started.get()) {
mCallback.onSoftKeyboardHide();
}
// if keyboard has been opened clicking and EditText.
if(mSoftKeyboardWeakReference.get().isKeyboardShow && started.get()) {
mSoftKeyboardWeakReference.get().isKeyboardShow = false;
}
// if an EditText is focused, remove its focus (on UI thread)
if(started.get()) {
mSoftKeyboardWeakReference.get().mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
}
}
}
}
public void keyboardOpened()
{
synchronized(this)
{
notify();
}
}
public void stopThread()
{
synchronized(this)
{
started.set(false);
notify();
}
}
}
private static final class InnerHandler extends Handler {
private WeakReference<SoftKeyboard> mSoftKeyboardWeakReference;
private InnerHandler(SoftKeyboard softKeyboard) {
mSoftKeyboardWeakReference = new WeakReference<>(softKeyboard);
}
@Override
public void handleMessage(Message m) {
switch(m.what) {
case CLEAR_FOCUS:
if(mSoftKeyboardWeakReference.get() != null &&
mSoftKeyboardWeakReference.get().tempView != null) {
mSoftKeyboardWeakReference.get().tempView.clearFocus();
mSoftKeyboardWeakReference.get().tempView = null;
mSoftKeyboardWeakReference.clear();
}
break;
}
}
}
}
/*
* 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();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment