Android Edittext, Soft Keyboard Show/Hide Event

How to capture the virtual keyboard show/hide event in Android?

2020 Update

This is now possible:

On Android 11, you can do

view.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback {
override fun onEnd(animation: WindowInsetsAnimation) {
super.onEnd(animation)
val showingKeyboard = view.rootWindowInsets.isVisible(WindowInsets.Type.ime())
// now use the boolean for something
}
})

You can also listen to the animation of showing/hiding the keyboard and do a corresponding transition.

I recommend reading Android 11 preview and the corresponding documentation

Before Android 11

However, this work has not been made available in a Compat version, so you need to resort to hacks.

You can get the window insets and if the bottom insets are bigger than some value you find to be reasonably good (by experimentation), you can consider that to be showing the keyboard. This is not great and can fail in some cases, but there is no framework support for that.

This is a good answer on this exact question https://stackoverflow.com/a/36259261/372076. Alternatively, here's a page giving some different approaches to achieve this pre Android 11:

https://developer.salesforce.com/docs/atlas.en-us.noversion.service_sdk_android.meta/service_sdk_android/android_detecting_keyboard.htm



Note

This solution will not work for soft keyboards and
onConfigurationChanged will not be called for soft (virtual)
keyboards.


You've got to handle configuration changes yourself.

http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

Sample:

// from the link above
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);


// Checks whether a hardware keyboard is available
if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
} else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
}
}

Then just change the visibility of some views, update a field, and change your layout file.

Android EditText, soft keyboard show/hide event?

Hi I'd used following workaround:

As far as my content view is a subclass of LinearLayout (could be any other view or view group), I'd overridden onMeasure method lilke following:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();

if (actualHeight > proposedheight){
// Keyboard is shown
} else {
// Keyboard is hidden
}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

This workaround helped me to hide some controls when keyboard is showing and bring back otherwise.

Hope this would be useful.

Soft keyboard open and close listener in an activity in Android

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);
}
}

How to make Android Soft keyboard showing or hiding listener better

To know if the keyboard is hidden or visible, I use this code:

 view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
view.getWindowVisibleDisplayFrame(r);
if (view.getRootView().getHeight() - (r.bottom - r.top) > 500) { // if more than 100 pixels, its probably a keyboard...
onKeyboardShow();
} else {
onKeyboardHidden();
}
}
});

onKeyboardShow & onKeyboardHidden functions are my own which then do what's needed.

How to get the hide keyboard event

In this code, i listening to editText. If user close the keyboard, onBackPressed() running.

 var isFirst: Boolean = true

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

editText.getViewTreeObserver().addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener {
if (isKeyboardOpen()) {
if (isFirst) {
isFirst = false
}
} else {
if (!isFirst) {
onBackPressed()
}
}
})

}

private fun isKeyboardOpen(): Boolean {
val r = Rect()
editText.getWindowVisibleDisplayFrame(r)
val screenHeight = editText.getRootView().getHeight()
// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
val keypadHeight = screenHeight - r.bottom
// Log.d("TAG", "keypadHeight = $keypadHeight")

if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
Log.d("TAG", "keypad is open")
} else {
// keyboard is closed
Log.d("TAG", "keypad is close")
}
return (keypadHeight > screenHeight * 0.15)
}

override fun onBackPressed() {
// my code
Log.d("TAG", "keypad is onBackPressed")
finish()
}

Show/Hide Soft Keyboard event in Fragment

Sadly but true - android do not have native on software keyboard show event.

One the way handle fact that keyboard is hidden is to check entered symbols and back button press (for example textEdit will receive even back button) - but it is not flexible enough solution.

Another the possible solutions is:
Override onMeasure in activity and then notify observers (pattern Observer) - for example fragments. Fragment should subscribe and unsubscribe himself onPause onResume events. Something like that for activity code:

private class DialogActivityLayout extends LinearLayout {

public DialogActivityLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.activity_dialog, this);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int proposedHeight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();

/* Layout loaded */
if (actualHeight == 0 || proposedHeight == actualHeight) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}

if (proposedHeight > actualHeight) {
DialogActivity.this.onKeyboardHide();
} else {
DialogActivity.this.onKeyboardShow();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

I'm not sure but as I remember it works only for LinearLayout and of course activity should have adjustResize flag set (programmatically or in manifeset)

Another (I thing better approach) is to as here with globalTree observer

SoftKeyboard open and close listener in an activity in Android?

How to close/hide the Android soft keyboard programmatically?

To help clarify this madness, I'd like to begin by apologizing on behalf of all Android users for Google's downright ridiculous treatment of the soft keyboard. The reason there are so many answers, each different, for the same simple question is that this API, like many others in Android, is horribly designed. I can think of no polite way to state it.

I want to hide the keyboard. I expect to provide Android with the following statement: Keyboard.hide(). The end. Thank you very much. But Android has a problem. You must use the InputMethodManager to hide the keyboard. OK, fine, this is Android's API to the keyboard. BUT! You are required to have a Context in order to get access to the IMM. Now we have a problem. I may want to hide the keyboard from a static or utility class that has no use or need for any Context. or And FAR worse, the IMM requires that you specify what View (or even worse, what Window) you want to hide the keyboard FROM.

This is what makes hiding the keyboard so challenging. Dear Google: When I'm looking up the recipe for a cake, there is no RecipeProvider on Earth that would refuse to provide me with the recipe unless I first answer WHO the cake will be eaten by AND where it will be eaten!!

This sad story ends with the ugly truth: to hide the Android keyboard, you will be required to provide 2 forms of identification: a Context and either a View or a Window.

I have created a static utility method that can do the job VERY solidly, provided you call it from an Activity.

public static void hideKeyboard(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
//Find the currently focused view, so we can grab the correct window token from it.
View view = activity.getCurrentFocus();
//If no view currently has focus, create a new one, just so we can grab a window token from it
if (view == null) {
view = new View(activity);
}
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

Be aware that this utility method ONLY works when called from an Activity! The above method calls getCurrentFocus of the target Activity to fetch the proper window token.

But suppose you want to hide the keyboard from an EditText hosted in a DialogFragment? You can't use the method above for that:

hideKeyboard(getActivity()); //won't work

This won't work because you'll be passing a reference to the Fragment's host Activity, which will have no focused control while the Fragment is shown! Wow! So, for hiding the keyboard from fragments, I resort to the lower-level, more common, and uglier:

public static void hideKeyboardFrom(Context context, View view) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

Below is some additional information gleaned from more time wasted chasing this solution:

About windowSoftInputMode

There's yet another point of contention to be aware of. By default, Android will automatically assign initial focus to the first EditText or focusable control in your Activity. It naturally follows that the InputMethod (typically the soft keyboard) will respond to the focus event by showing itself. The windowSoftInputMode attribute in AndroidManifest.xml, when set to stateAlwaysHidden, instructs the keyboard to ignore this automatically-assigned initial focus.

<activity
android:name=".MyActivity"
android:windowSoftInputMode="stateAlwaysHidden"/>

Almost unbelievably, it appears to do nothing to prevent the keyboard from opening when you touch the control (unless focusable="false" and/or focusableInTouchMode="false" are assigned to the control). Apparently, the windowSoftInputMode setting applies only to automatic focus events, not to focus events triggered by touch events.

Therefore, stateAlwaysHidden is VERY poorly named indeed. It should perhaps be called ignoreInitialFocus instead.


UPDATE: More ways to get a window token

If there is no focused view (e.g. can happen if you just changed fragments), there are other views that will supply a useful window token.

These are alternatives for the above code if (view == null) view = new View(activity); These don't refer explicitly to your activity.

Inside a fragment class:

view = getView().getRootView().getWindowToken();

Given a fragment fragment as a parameter:

view = fragment.getView().getRootView().getWindowToken();

Starting from your content body:

view = findViewById(android.R.id.content).getRootView().getWindowToken();

UPDATE 2: Clear focus to avoid showing keyboard again if you open the app from the background

Add this line to the end of the method:

view.clearFocus();

How to hide Soft Keyboard when activity starts

If you don't want to use xml, make a Kotlin Extension to hide keyboard

// In onResume, call this
myView.hideKeyboard()

fun View.hideKeyboard() {
val inputMethodManager = context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(windowToken, 0)
}

Alternatives based on use case:

fun Fragment.hideKeyboard() {
view?.let { activity?.hideKeyboard(it) }
}

fun Activity.hideKeyboard() {
// Calls Context.hideKeyboard
hideKeyboard(currentFocus ?: View(this))
}

fun Context.hideKeyboard(view: View) {
view.hideKeyboard()
}

How to show soft keyboard

fun Context.showKeyboard() { // Or View.showKeyboard()
val inputMethodManager = context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.toggleSoftInput(SHOW_FORCED, HIDE_IMPLICIT_ONLY)
}

Simpler method when simultaneously requesting focus on an edittext

myEdittext.focus()

fun View.focus() {
requestFocus()
showKeyboard()
}

Bonus simplification:

Remove requirement for ever using getSystemService: Splitties Library

// Simplifies above solution to just
inputMethodManager.hideSoftInputFromWindow(windowToken, 0)


Related Topics



Leave a reply



Submit