I have an Activity
where there are 5 EditText
s. When the user clicks on the first EditText
, the soft keyboard opens to enter some value in it. I want to set some other View
's visibility to Gone
when the soft keyboard opens and also when the user clicks on the first EditText
and also when the soft keyboard closes from the same EditText
on the back button press. Then I want to set some other View
's visibility to visible.
Is there any listener or callback or any hack for when the soft keyboard opens from a click on the first EditText
in Android?
trying to detect the virtual keyboard height in Android.
X
and Y
location falls on/over the keyboard => SecurityException
=> decrement Y
and try again => until no exception is thrown => current Y
value is the keyboard height. 2. if the keyboard is not open => no SecurityException
.
SecurityException
is thrown => keyboard is open. Else, keyboard is closed.
EditText
not other EditText
. How I can distinguish this ?
Piece of cake with the awesome KeyboardVisibilityEvent library
KeyboardVisibilityEvent.setEventListener(
getActivity(),
new KeyboardVisibilityEventListener() {
@Override
public void onVisibilityChanged(boolean isOpen) {
// Ah... at last. do your thing :)
}
});
Credits for Yasuhiro SHIMIZU
This only works when android:windowSoftInputMode
of your activity is set to adjustResize
in the manifest. You can use a layout listener to see if the root layout of your activity is resized by the keyboard.
I use something like the following base class for my activities:
public class BaseActivity extends Activity {
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);
if(heightDiff <= contentViewTop){
onHideKeyboard();
Intent intent = new Intent("KeyboardWillHide");
broadcastManager.sendBroadcast(intent);
} else {
int keyboardHeight = heightDiff - contentViewTop;
onShowKeyboard(keyboardHeight);
Intent intent = new Intent("KeyboardWillShow");
intent.putExtra("KeyboardHeight", keyboardHeight);
broadcastManager.sendBroadcast(intent);
}
}
};
private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;
protected void onShowKeyboard(int keyboardHeight) {}
protected void onHideKeyboard() {}
protected void attachKeyboardListeners() {
if (keyboardListenersAttached) {
return;
}
rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
keyboardListenersAttached = true;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (keyboardListenersAttached) {
rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
}
}
}
The following example activity uses this to hide a view when the keyboard is shown and show it again when the keyboard is hidden.
The xml layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
>
<!-- omitted for brevity -->
</ScrollView>
<LinearLayout android:id="@+id/bottomContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<!-- omitted for brevity -->
</LinearLayout>
</LinearLayout>
And the activity:
public class TestActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
attachKeyboardListeners();
}
@Override
protected void onShowKeyboard(int keyboardHeight) {
// do things when keyboard is shown
bottomContainer.setVisibility(View.GONE);
}
@Override
protected void onHideKeyboard() {
// do things when keyboard is hidden
bottomContainer.setVisibility(View.VISIBLE);
}
}
rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
from?
As Vikram pointed out in the comments, detecting whether the softkeyboard is shown or has disappeared is only possible with some ugly hacks.
Maybe it is enough to set a focus listener on the edittext:
yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
//got focus
} else {
//lost focus
}
}
});
setOnFocusChangeListener
listener will be called then I press back then it closes keyboard & but didn't click on other views, now again I click on same edittext which already have focus then what will happen ?
onFocusChange()
will not be called.
For Activity:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) {
//enter your code here
}else{
//enter code for hid
}
}
});
For Fragment:
view = inflater.inflate(R.layout.live_chat_fragment, null);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
view.getWindowVisibleDisplayFrame(r);
int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...
}
}
});
android:windowSoftInputMode="adjustResize"
in manifest?
Jaap's answer won't work for AppCompatActivity. Instead get the height of the Status Bar and Navigation bar etc and compare to your app's window size.
Like so:
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// navigation bar height
int navigationBarHeight = 0;
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
}
// status bar height
int statusBarHeight = 0;
resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
// display window size for the app layout
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
// screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard
int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height());
if (keyboardHeight <= 0) {
onHideKeyboard();
} else {
onShowKeyboard(keyboardHeight);
}
}
};
You can try it:
private void initKeyBoardListener() {
// Минимальное значение клавиатуры.
// Threshold for minimal keyboard height.
final int MIN_KEYBOARD_HEIGHT_PX = 150;
// Окно верхнего уровня view.
// Top-level window decor view.
final View decorView = getWindow().getDecorView();
// Регистрируем глобальный слушатель. Register global layout listener.
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// Видимый прямоугольник внутри окна.
// Retrieve visible rectangle inside window.
private final Rect windowVisibleDisplayFrame = new Rect();
private int lastVisibleDecorViewHeight;
@Override
public void onGlobalLayout() {
decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();
if (lastVisibleDecorViewHeight != 0) {
if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
Log.d("Pasha", "SHOW");
} else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
Log.d("Pasha", "HIDE");
}
}
// Сохраняем текущую высоту view до следующего вызова.
// Save current decor view height for the next call.
lastVisibleDecorViewHeight = visibleDecorViewHeight;
}
});
}
I am late but I just found a very convenient dependency out there. Using it you can check the visibility of the keyboard as well as make the keyboard "Hide" and Show Whenever you want with a single Line of Code.
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'
And then you simply use this code segment to check the keyboard visibility.
KeyboardVisibilityEvent.setEventListener(this, new KeyboardVisibilityEventListener() {
@Override
public void onVisibilityChanged(boolean isOpen) {
if (isOpen)
Toast.makeText(MainActivity.this, "keyboard opened",Toast.LENGTH_SHORT).show();
else
Toast.makeText(MainActivity.this, "keyboard hidden", Toast.LENGTH_SHORT).show();
}
});
Then if you want to Hide/Show keyboard at any point of time then you can just write one of these single lines to achieve it.
UIUtil.showKeyboard(this,edittext_to_be_focused);
UIUtil.hideKeyboard(this);
The below code is working for me,
mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (mainLayout != null) {
int heightDiff = mainLayout.getRootView().getHeight() - mainLayout.getHeight();
if (heightDiff > dpToPx(getActivity(), 200)) {
//keyboard is open
} else {
//keyboard is hide
}
}
}
});
You can use my Rx extension function (Kotlin).
/**
* @return [Observable] to subscribe of keyboard visibility changes.
*/
fun AppCompatActivity.keyboardVisibilityChanges(): Observable<Boolean> {
// flag indicates whether keyboard is open
var isKeyboardOpen = false
val notifier: BehaviorSubject<Boolean> = BehaviorSubject.create()
// approximate keyboard height
val approximateKeyboardHeight = dip(100)
// device screen height
val screenHeight: Int = getScreenHeight()
val visibleDisplayFrame = Rect()
val viewTreeObserver = window.decorView.viewTreeObserver
val onDrawListener = ViewTreeObserver.OnDrawListener {
window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)
val keyboardHeight = screenHeight - (visibleDisplayFrame.bottom - visibleDisplayFrame.top)
val keyboardOpen = keyboardHeight >= approximateKeyboardHeight
val hasChanged = isKeyboardOpen xor keyboardOpen
if (hasChanged) {
isKeyboardOpen = keyboardOpen
notifier.onNext(keyboardOpen)
}
}
val lifeCycleObserver = object : GenericLifecycleObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event?) {
if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
viewTreeObserver.removeOnDrawListener(onDrawListener)
source.lifecycle.removeObserver(this)
notifier.onComplete()
}
}
}
viewTreeObserver.addOnDrawListener(onDrawListener)
lifecycle.addObserver(lifeCycleObserver)
return notifier
.doOnDispose {
viewTreeObserver.removeOnDrawListener(onDrawListener)
lifecycle.removeObserver(lifeCycleObserver)
}
.onTerminateDetach()
.hide()
}
Example:
(context as AppCompatActivity)
.keyboardVisibilityChanges()
.subscribeBy { isKeyboardOpen ->
// your logic
}
dip()
and getScreenHeight()
For use in Kotlin inside fragment, which is a common use case it is very easy with KeyboardVisibilityEvent
library.
In build.gradle:
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'
In Fragment:
activity?.let {
KeyboardVisibilityEvent.setEventListener(it,object: KeyboardVisibilityEventListener {
override fun onVisibilityChanged(isOpen: Boolean) {
if (isOpen) Toast.makeText(context,"Keyboard is opened",Toast.LENGTH_SHORT).show()
else Toast.makeText(context,"Keyboard is closed",Toast.LENGTH_SHORT).show()
}
})
}
if (!isOpen) {}
in kotlin you can use this code in your activity
window.decorView.viewTreeObserver.addOnGlobalLayoutListener{
val r = Rect()
window.decorView.getWindowVisibleDisplayFrame(r)
val height =window.decorView.height
if(height - r.bottom>height*0.1399){
//keyboard is open
}else{
//keyboard is close
}
If you can, try to extend EditText and override 'onKeyPreIme' method.
@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
mEditorListener = listener; //keep it for later usage
super.setOnEditorActionListener(listener);
}
@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
if (mEditorListener != null) {
//you can define and use custom listener,
//OR define custom R.id.<imeId>
//OR check event.keyCode in listener impl
//* I used editor action because of ButterKnife @
mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
}
}
return super.onKeyPreIme(keyCode, event);
}
How can you extend it:
Implement onFocus listening and declare 'onKeyboardShown' declare 'onKeyboardHidden'
I think, that recalculating of screen height is not 100% successfully as mentioned before. To be clear, overriding of 'onKeyPreIme' is not called on 'hide soft keyboard programatically' methods, BUT if you are doing it anywhere, you should do 'onKeyboardHidden' logic there and do not create a comprehensive solutions.
This will work without any need to change your activity's android:windowSoftInputMode
step 1: extend EditText class and override these two:
@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
mEditorListener = listener;
super.setOnEditorActionListener(listener);
}
@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
if (mEditorListener != null) {
mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
}
}
return super.onKeyPreIme(keyCode, event);
}
step 2: create these two in your activity:
private void initKeyboard() {
final AppEditText editText = findViewById(R.id.some_id);
editText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
setKeyboard(hasFocus);
}
});
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (event == null || event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
editText.clearFocus();
}
return false;
}
});
}
public void setKeyboard(boolean isShowing) {
// do something
}
*** remember in order to make clearFocus
work, you have to make parent or first child in the parent hierarchy focusable.
setFocusableInTouchMode(true);
setFocusable(true);
Check my Kotlin extension View.keyboardVisibilityChanges()
:
fun View.keyboardVisibilityChanges(): Flow<Boolean>{
return onPreDrawFlow()
.map { isKeyboardVisible() }
.distinctUntilChanged()
}
fun View.onPreDrawFlow(): Flow<Unit> {
return callbackFlow {
val onPreDrawListener = ViewTreeObserver.OnPreDrawListener {
trySendBlocking(Unit)
true
}
viewTreeObserver.addOnPreDrawListener(onPreDrawListener)
awaitClose {
viewTreeObserver.removeOnPreDrawListener(onPreDrawListener)
}
}
}
fun View.isKeyboardVisible(): Boolean = ViewCompat.getRootWindowInsets(this)
?.isVisible(Type.ime())
?: false
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainactivity);
attachKeyboardListeners();
....
yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
yourEditText2.setVisibility(View.GONE);
yourEditText3.setVisibility(View.GONE);
yourEditText4.setVisibility(View.GONE);
yourEditText5.setVisibility(View.GONE);
} else {
yourEditText2.setVisibility(View.VISIBLE);
yourEditText3.setVisibility(View.VISIBLE);
yourEditText4.setVisibility(View.VISIBLE);
yourEditText5.setVisibility(VISIBLE);
}
}
});
}
}
setOnFocusChangeListener
listener will be called then I press back then it closes keyboard & but didn't click on other views, now again I click on same edittext which already have focus then what will happen ?
onfocus
listener never call that's is I am looking not which you are suggesting
Use this class,
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();
}
}
}
}
In Android Manifest
, android:windowSoftInputMode="adjustResize"
is necessary.
/*
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();
}
P.S - Completely taken from here.
For the case of adjustResize
and FragmentActivity accepted solution from @Jaap doesn't work for me.
Here is my solution:
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
private int contentDiff;
private int rootHeight;
@Override
public void onGlobalLayout() {
View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
if (rootHeight != mDrawerLayout.getRootView().getHeight()) {
rootHeight = mDrawerLayout.getRootView().getHeight();
contentDiff = rootHeight - contentView.getHeight();
return;
}
int newContentDiff = rootHeight - contentView.getHeight();
if (contentDiff != newContentDiff) {
if (contentDiff < newContentDiff) {
onShowKeyboard(newContentDiff - contentDiff);
} else {
onHideKeyboard();
}
contentDiff = newContentDiff;
}
}
};
A different approach would be to check when the user stopped typing...
When a TextEdit is in focus (user is/was typing) you could hide the views (focus listener)
and use a Handler + Runnable and a text change listener to close the keyboard (regardless of its visibility) and show the views after some delay.
The main thing to look out for would be the delay you use, which would depend on the content of these TextEdits.
Handler timeoutHandler = new Handler();
Runnable typingRunnable = new Runnable() {
public void run() {
// current TextEdit
View view = getCurrentFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// reset focus
view.clearFocus();
// close keyboard (whether its open or not)
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
// SET VIEWS VISIBLE
}
};
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// SET VIEWS GONE
// reset handler
timeoutHandler.removeCallbacks(typingRunnable);
timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
}
}
});
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Reset Handler...
timeoutHandler.removeCallbacks(typingRunnable);
}
@Override
public void afterTextChanged(Editable s) {
// Reset Handler Cont.
if (editText.getText().toString().trim().length() > 0) {
timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
}
}
});
This code works great nice
use this class for root view:
public class KeyboardConstraintLayout extends ConstraintLayout {
private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;
public KeyboardConstraintLayout(Context context) {
super(context);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); //128dp
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!isInEditMode()) {
Activity activity = (Activity) getContext();
@SuppressLint("DrawAllocation")
Rect rect = new Rect();
getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;
if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
if (keyboardHeight > minKeyboardHeight) {
if (!isShow) {
isShow = true;
keyboardListener.onKeyboardVisibility(true);
}
}else {
if (isShow) {
isShow = false;
keyboardListener.onKeyboardVisibility(false);
}
}
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public boolean isShowKeyboard() {
return isShow;
}
public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
this.targetEditText = targetEditText;
this.keyboardListener = keyboardListener;
}
public interface KeyboardListener {
void onKeyboardVisibility (boolean isVisible);
}
}
and set keyboard listener in activity or fragment:
rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
@Override
public void onKeyboardVisibility(boolean isVisible) {
}
});
This is not working as desired...
... have seen many use size calculations to check ...
I wanted to determine if it was open or not and I found isAcceptingText()
so this really does not answer the question as it does not address opening or closing rather more like is open or closed so it is related code that may help others in various scenarios...
in an activity
if (((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
} else {
Log.d(TAG,"Software Keyboard was not shown");
}
in a fragment
if (((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
} else {
Log.d(TAG,"Software Keyboard was not shown");
}
You can handle keyboard visibility by overriding two methods in your Activity: onKeyUp()
and onKeyDown()
more information in this link: https://developer.android.com/training/keyboard-input/commands
Found an accurate way of telling whether or not a keyboard when using the 'adjustResize' Soft input mode (Kotlin code)
Define a couple of activity scope variables
private var activityHeight = 0
private var keyboardOpen = false
Write the following code in onCreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
/* Grab initial screen value */
this@ActivityMain.window.decorView.doOnNextLayout {
val displayFrame : Rect = Rect()
this@ActivityMain.window.decorView.getWindowVisibleDisplayFrame(displayFrame)
activityHeight = displayFrame.height()
}
/* Check for keyboard open/close */
this@ActivityMain.window.decorView.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
val drawFrame : Rect = Rect()
this@ActivityMain.window.decorView.getWindowVisibleDisplayFrame(drawFrame)
val currentSize = drawFrame.height()
keyboardOpen = currentSize < activityHeight
Log.v("keyboard1","$keyboardOpen $currentSize - $activityHeight")
}
}
You now have a boolean which accurately tracks whether or not the keyboard is open, do what you will
Unfortunately I do not have a sufficiently high reputation to comment on Jaap van Hengstum's answer. But I read a few comments of people, having the problem that contentViewTop
is always 0
and that onShowKeyboard(...)
is always called.
I had the same issue and figured out the problem I had. I used an AppCompatActivity
instead of a 'normal' Activity
. In this case Window.ID_ANDROID_CONTENT
refers to an ContentFrameLayout
and not to the FrameLayout
with the right top-value. In my case it was fine to use the 'normal' Activity
, if you have to use another activity-type (I just tested the AppCompatActivity
, maybe it's also an issue with other acitivy-types like the FragmentActivity
), you have to access the FrameLayout
, which is an ancestor of the ContentFrameLayout
.
when keyboard show
rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight()
is true,else hide
private boolean isKeyboardShown = false;
private int prevContentHeight = 0;
private ViewGroup contentLayout;
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener =
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int contentHeight = contentLayout.getHeight();
int rootViewHeight = contentLayout.getRootView().getHeight();
if (contentHeight > 0) {
if (!isKeyboardShown) {
if (contentHeight < prevContentHeight) {
isKeyboardShown = true;
onShowKeyboard(rootViewHeight - contentHeight);
}
} else {
if (contentHeight > prevContentHeight) {
isKeyboardShown = false;
onHideKeyboard();
}
}
prevContentHeight = contentHeight;
}
}
};
I've modified the Jaap's accepted answer a bit. But in my case, there are few assumptions such as android:windowSoftInputMode=adjustResize
and the keyboard does not show up at the beginning when the app starts. And also, I assume that the screen in regard matches the parent's height.
contentHeight > 0
this check provides me to know if the regarding screen is hidden or shown to apply keyboard event listening for this specific screen. Also I pass the layout view of the regarding screen in attachKeyboardListeners(<your layout view here>)
in my main activity's onCreate()
method. Every time when the height of the regarding screen changes, I save it to prevContentHeight
variable to check later whether the keyboard is shown or hidden.
For me, so far it's been worked pretty well. I hope that it works for others too.
"Jaap van Hengstum"'s answer is working for me, but there is no need to set "android:windowSoftInputMode" as he just said!
I've made it smaller(it now just detects what I want, actually an event on showing and hiding of keyboard):
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
if(heightDiff <= contentViewTop){
onHideKeyboard();
} else {
onShowKeyboard();
}
}
};
private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;
protected void onShowKeyboard() {}
protected void onHideKeyboard() {}
protected void attachKeyboardListeners() {
if (keyboardListenersAttached) {
return;
}
rootLayout = (ViewGroup) findViewById(R.id.CommentsActivity);
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
keyboardListenersAttached = true;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (keyboardListenersAttached) {
rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
}
}
and just don't forget to add this
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
attachKeyboardListeners();}
check with the below code :
XML CODE :
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinatorParent"
style="@style/parentLayoutPaddingStyle"
android:layout_width="match_parent"
android:layout_height="match_parent">
.................
</android.support.constraint.ConstraintLayout>
JAVA CODE :
//Global Variable
android.support.constraint.ConstraintLayout activityRootView;
boolean isKeyboardShowing = false;
private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
android.support.constraint.ConstraintLayout.LayoutParams layoutParams;
//onCreate or onViewAttached
activityRootView = view.findViewById(R.id.coordinatorParent);
onGlobalLayoutListener = onGlobalLayoutListener();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
//outside oncreate
ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener() {
return new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
int screenHeight = activityRootView.getRootView().getHeight();
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
if (!isKeyboardShowing) { // keyboard is opened
isKeyboardShowing = true;
onKeyboardVisibilityChanged(true);
}
}
else {
if (isKeyboardShowing) { // keyboard is closed
isKeyboardShowing = false;
onKeyboardVisibilityChanged(false);
}
}
}//ends here
};
}
void onKeyboardVisibilityChanged(boolean value) {
layoutParams = (android.support.constraint.ConstraintLayout.LayoutParams)topImg.getLayoutParams();
if(value){
int length = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, getResources().getDisplayMetrics());
layoutParams.height= length;
layoutParams.width = length;
topImg.setLayoutParams(layoutParams);
Log.i("keyboard " ,""+ value);
}else{
int length1 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 175, getResources().getDisplayMetrics());
layoutParams.height= length1;
layoutParams.width = length1;
topImg.setLayoutParams(layoutParams);
Log.i("keyboard " ,""+ value);
}
}
@Override
public void onDetach() {
super.onDetach();
if(onGlobalLayoutListener != null) {
activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
There is a keyboard dismissed listener.
Class SearchEditText
is derived from android.widget.EditText
class. There is an interface SearchEditText.OnKeyboardDismissListener
in this class. You can look docmentation:
https://developer.android.com/reference/androidx/leanback/widget/SearchEditText
Note. Before use SearchEditText you need to set up Gradle dependencies in the build.gradle (:app):
implementation 'androidx.leanback:leanback:1.1.0-alpha05'
Maybe someone will come in handy.
Detailed response:
import androidx.appcompat.app.AppCompatActivity;
import androidx.leanback.widget.SearchEditText;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity
implements SearchEditText.OnKeyboardDismissListener {
SearchEditText searchEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
searchEditText = findViewById(R.id.search_edit_text);
searchEditText.setOnKeyboardDismissListener(this);
}
/**
* Method invoked when the keyboard is dismissed.
*/
@Override
public void onKeyboardDismiss() {
Toast.makeText(this, "The listener worked", Toast.LENGTH_LONG).show();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.leanback.widget.SearchEditText
android:id="@+id/search_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20sp"
android:focusableInTouchMode="true"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Note: listener works with:
android:windowSoftInputMode="adjustPan"
android:windowSoftInputMode="adjustResize"
I built the listener using this answer and my task LoopingTask, thanks by the way.
Very easy to implement using SoftKeyboardListener from Viper Pack android library.
Just install the library then add this to your code :
Lava.app.addSoftKeyboardListener(context, new Lava.SoftKeyboardListener() {
@Override
public void onSoftKeyboardShow(EditText focusedview) {
// when shows
}
@Override
public void onSoftKeyboardHide(EditText focusedview) {
// when hides
}
});
Lava.app.removeSoftKeyboardListeners()
onCreate
boolean isListenerAdded;
...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
if (!isListenerAdded) {
isListenerAdded = true;
// create your listener here
}
...
}
...
Or just remove all the previous ones :
...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
Lava.app.removeSoftKeyboardListeners();
// create your listener here
...
}
...
Lava.app.removeSoftKeyboardListeners()
removes all previous SoftKeyboardListeners.
In my case the 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2' library is not being worked for some reasons with a scrollview. It might be because of broken calculations of view height in the scrollview. Insets are the answer. The simple inset listener works like a charm. Here is the code:
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
insets
}
Success story sharing