I am stuck with status bar hiding. Let me explain I am Implementing activity and also place manifest file in android:windowSoftInputMode="adjustResize" for keyboard place below edittext, it is working fine but i want also hide navigation bar, so i am using "Theme.Holo.Light.NoActionBar.Fullscreen" theme. But when keyboard appearing app navigation bar also scrolling. How can i restrict scrolling navigation bar and hide notification bar when keyboard appearing.
I think this is help you => http://developer.android.com/reference/android/view/View.html#setSystemUiVisibility(int)
=>setSystemUiVisibility(newVis); //this method help you
=> this is class for information and better understand.
public static class Content extends ScrollView
implements View.OnSystemUiVisibilityChangeListener, View.OnClickListener {
TextView mText;
TextView mTitleView;
SeekBar mSeekView;
boolean mNavVisible;
int mBaseSystemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| SYSTEM_UI_FLAG_LAYOUT_STABLE;
int mLastSystemUiVis;
Runnable mNavHider = new Runnable() {
#Override public void run() {
setNavVisibility(false);
}
};
public Content(Context context, AttributeSet attrs) {
super(context, attrs);
mText = new TextView(context);
mText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
mText.setText(context.getString(R.string.alert_dialog_two_buttons2ultra_msg));
mText.setClickable(false);
mText.setOnClickListener(this);
mText.setTextIsSelectable(true);
addView(mText, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
setOnSystemUiVisibilityChangeListener(this);
}
public void init(TextView title, SeekBar seek) {
// This called by the containing activity to supply the surrounding
// state of the content browser that it will interact with.
mTitleView = title;
mSeekView = seek;
setNavVisibility(true);
}
#Override public void onSystemUiVisibilityChange(int visibility) {
// Detect when we go out of low-profile mode, to also go out
// of full screen. We only do this when the low profile mode
// is changing from its last state, and turning off.
int diff = mLastSystemUiVis ^ visibility;
mLastSystemUiVis = visibility;
if ((diff&SYSTEM_UI_FLAG_LOW_PROFILE) != 0
&& (visibility&SYSTEM_UI_FLAG_LOW_PROFILE) == 0) {
setNavVisibility(true);
}
}
#Override protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
// When we become visible, we show our navigation elements briefly
// before hiding them.
setNavVisibility(true);
getHandler().postDelayed(mNavHider, 2000);
}
#Override protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// When the user scrolls, we hide navigation elements.
setNavVisibility(false);
}
#Override public void onClick(View v) {
// When the user clicks, we toggle the visibility of navigation elements.
int curVis = getSystemUiVisibility();
setNavVisibility((curVis&SYSTEM_UI_FLAG_LOW_PROFILE) != 0);
}
void setBaseSystemUiVisibility(int visibility) {
mBaseSystemUiVisibility = visibility;
}
void setNavVisibility(boolean visible) {
int newVis = mBaseSystemUiVisibility;
if (!visible) {
newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN;
}
final boolean changed = newVis == getSystemUiVisibility();
// Unschedule any pending event to hide navigation if we are
// changing the visibility, or making the UI visible.
if (changed || visible) {
Handler h = getHandler();
if (h != null) {
h.removeCallbacks(mNavHider);
}
}
// Set the new desired visibility.
setSystemUiVisibility(newVis);
mTitleView.setVisibility(visible ? VISIBLE : INVISIBLE);
mSeekView.setVisibility(visible ? VISIBLE : INVISIBLE);
}
}
I have a layout like the one below and I would like to have the following behavior when the user taps on the EditText:
Keyboard should overlay the "LinearLayout aligned to the bottom";
EditText should be visible on the screen, not overlaid by the keyboard;
The toolbar should be visible on the screen;
My layout
Expected behaviour
Actual result with windowSoftInputMode = adjustPan
Requirements not met:
Toolbar should be visible on screen;
Actual result with windowSoftInputMode = adjustResize
Requirements not met:
Keyboard should overlay the "LinearLayout aligned to the bottom";
Actual result with windowSoftInputMode = adjustNothing
Requirements not met:
EditText should be visible on screen, not overlaid by the keyboard;
Has anyone faced the same issue and achieved the requirements?
I am not sure that there is any out of box solution to make it works as you expect.
However, you can achieve the specified behavior by using windowSoftInputMode = adjustNothing
and setting focus listener to edit text, and scroll the scroll view, when edit text gets focus.
If it is not clear how to implement scrolling I can add an example.
I think there is no very simple way to achieve this. but there is a project here that has a SizeNotifierRelativeLayout then pops up when keyboard gets open.
Change the Parent layout to Relative layout
public class SizeNotifierRelativeLayout extends RelativeLayout {
private Rect rect = new Rect();
public SizeNotifierRelativeLayoutDelegate delegate;
public abstract interface SizeNotifierRelativeLayoutDelegate {
public abstract void onSizeChanged(int keyboardHeight);
}
public SizeNotifierRelativeLayout(Context context) {
super(context);
}
public SizeNotifierRelativeLayout(android.content.Context context, android.util.AttributeSet attrs) {
super(context, attrs);
}
public SizeNotifierRelativeLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Calculate the soft keyboard height and report back to listener
* #param changed
* #param l
* #param t
* #param r
* #param b
*/
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (delegate != null) {
View rootView = this.getRootView();
int usableViewHeight = rootView.getHeight() - AndroidUtilities.statusBarHeight - AndroidUtilities.getViewInset(rootView);
this.getWindowVisibleDisplayFrame(rect);
int keyboardHeight = usableViewHeight - (rect.bottom - rect.top);
delegate.onSizeChanged(keyboardHeight);
}
}
Next on your Activity or Fragment include this codes
private SizeNotifierRelativeLayout sizeNotifierRelativeLayout;
View rootView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootView = view.findViewById(R.id.root);
sizeNotifierRelativeLayout = (SizeNotifierRelativeLayout)
view.findViewById(R.id.chat_layout);
sizeNotifierRelativeLayout.delegate = this;
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int screenHeight = rootView.getRootView().getHeight();
int keypadHeight = screenHeight - r.bottom;
Log.d("Height", "keypadHeight = " + keypadHeight);
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
if (sizeNotifierRelativeLayout != null) {
sizeNotifierRelativeLayout.setPadding(0, 0, 0, keypadHeight - 200);
}
} else {
// keyboard is closed
if (sizeNotifierRelativeLayout != null) {
sizeNotifierRelativeLayout.post(new Runnable() {
public void run() {
if (sizeNotifierRelativeLayout != null) {
sizeNotifierRelativeLayout.setPadding(0, 0, 0, 0);
}
}
});
}
try {
}
} catch (NullPointerException e) {
e.printStackTrace();
System.out.print(e.getMessage());
}
}
}
});
}
I did the following to resolve this issue.
AndroidManifest file activity config:
<activity
android:name=".chat.app.activity.ChatPageActivity"
android:configChanges="orientation|screenLayout"
android:screenOrientation="portrait"
android:theme="#style/Theme.AppCompat.Light.NoActionBar"
android:windowSoftInputMode="stateAlwaysHidden|adjustResize">
try this. it will scroll your edittext to focus mode and open the keyboard for it.
EditText editText = (EditText) findViewById(R.id.myTextViewId);
editText.requestFocus();
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
I have an Activity where there are 5 EditTexts. 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?
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);
}
}
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
}
}
});
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...
}
}
});
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
}
}
}
});
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()
}
})
}
Source and credits
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
}
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
Insets are the only one official and proper 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
}
Attention! This solution works only in case if you set edge-to-edge mode:
WindowCompat.setDecorFitsSystemWindows(window, false)
Please check official documentation about checking keyboard software visibility and proper implementation of edge-to-edge mode:
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);
}
}
});
}
}
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.
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);
}
}
at the first create a kotlin file and add these methods:
fun Activity.getRootView(): View {
return findViewById<View>(android.R.id.content)
}
fun Context.convertDpToPx(dp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
this.resources.displayMetrics
)
}
fun Activity.isKeyboardOpen(): Boolean {
val visibleBounds = Rect()
this.getRootView().getWindowVisibleDisplayFrame(visibleBounds)
val heightDiff = getRootView().height - visibleBounds.height()
val marginOfError = Math.round(this.convertDpToPx(50F))
return heightDiff > marginOfError
}
fun Activity.isKeyboardClosed(): Boolean {
return !this.isKeyboardOpen()
}
then create a listener class for checking the keyboard is open or not :
class KeyboardEventListener(
private val activity: AppCompatActivity,
private val callback: (isOpen: Boolean) -> Unit
) : LifecycleObserver {
private val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
private var lastState: Boolean = activity.isKeyboardOpen()
override fun onGlobalLayout() {
val isOpen = activity.isKeyboardOpen()
if (isOpen == lastState) {
return
} else {
dispatchKeyboardEvent(isOpen)
lastState = isOpen
}
}
}
init {
// Dispatch the current state of the keyboard
dispatchKeyboardEvent(activity.isKeyboardOpen())
// Make the component lifecycle aware
activity.lifecycle.addObserver(this)
registerKeyboardListener()
}
private fun registerKeyboardListener() {
activity.getRootView().viewTreeObserver.addOnGlobalLayoutListener(listener)
}
private fun dispatchKeyboardEvent(isOpen: Boolean) {
when {
isOpen -> callback(true)
!isOpen -> callback(false)
}
}
#OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE)
#CallSuper
fun onLifecyclePause() {
unregisterKeyboardListener()
}
private fun unregisterKeyboardListener() {
activity.getRootView().viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
and use it like this :
override fun onResume() {
super.onResume()
KeyboardEventListener(this) { isOpen -> // handle event }
}
I hope you find it useful.
Solution with extra property in Activity\Fragment, but without any hypothetical hardcoded heights (like 100 etc) .
Just add OnGlobalLayoutListener to your root view and save its initial height before keyboard will be shown:
var firstLoad = true
var contentFullWeight = 0
override fun onViewCreated(layoutView: View, savedInstanceState: Bundle?) {
super.onViewCreated(layoutView, savedInstanceState)
view?.viewTreeObserver?.addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener {
if(firstLoad){
contentFullWeight = view?.height!!
firstLoad = false
}
if (view?.height!! < contentFullWeight) {
Log.d("TEZT_KEYBOARD", ">> KBD OPENED")
} else {
Log.d("TEZT_KEYBOARD", ">> KBD closed")
}
})
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/addresses_confirm_root_view"
android:orientation="vertical">
<---In the xml root use the id--->
final LinearLayout activityRootView = view.findViewById(R.id.addresses_confirm_root_view);
activityRootView.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.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - r.height();
if (heightDiff > 0.25 * activityRootView.getRootView().getHeight()) {
// if more than 25% of the screen, its probably a keyboard...
onkeyboard();
} else {
//Keyboard not visible
offkeyboard();
}
}
});