When the soft keyboard opens I want a scroll view to scroll down to the bottom.
For this I can use: fullScroll(View.FOCUS_DOWN);
But how do I fire that command after the soft keyboard opening event triggers?
When the soft keyboard opens I want a scroll view to scroll down to the bottom.
For this I can use: fullScroll(View.FOCUS_DOWN);
But how do I fire that command after the soft keyboard opening event triggers?
Per this post and this post on android-developers it doesn't seem like it's possible to do what you want. You may want to re-examine your use case for what you're doing. Maybe one of the softInputMode
flags will work for you.
Here is my solution:
1/ A simple interface
public interface KeyboardVisibilityListener {
void onKeyboardVisibilityChanged(boolean keyboardVisible);
}
2/ A utility method (put it where you want, for instance in a class named KeyboardUtil
)
public static void setKeyboardVisibilityListener(Activity activity, KeyboardVisibilityListener keyboardVisibilityListener) {
View contentView = activity.findViewById(android.R.id.content);
contentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
private int mPreviousHeight;
@Override
public void onGlobalLayout() {
int newHeight = contentView.getHeight();
if (mPreviousHeight != 0) {
if (mPreviousHeight > newHeight) {
// Height decreased: keyboard was shown
keyboardVisibilityListener.onKeyboardVisibilityChanged(true);
} else if (mPreviousHeight < newHeight) {
// Height increased: keyboard was hidden
keyboardVisibilityListener.onKeyboardVisibilityChanged(false);
} else {
// No change
}
}
mPreviousHeight = newHeight;
}
});
}
3/ Use from an Activity this way (a good place is in onCreate):
KeyboardUtil.setKeyboardVisibilityListener(this, mKeyboardVisibilityListener);
watching the date , possibly you have a solution for your question, otherwise:
Here is the same response i made to another question related : Is there a way to tell if the soft-keyboard is shown?
but i copy full response here to avoid dead links:
Please check Configuration Changes for your Activity
This for your AndroidManifest.xml
and this for your Activity class http://developer.android.com/reference/android/app/Activity.html#onConfigurationChanged(android.content.res.Configuration)
You will need to @Override the public method onConfigurationChanged(android.content.res.Configuration) of your Activity to be able to handle, for example, this values:
hardKeyboardHidden,
keyboard,
keyboardHidden
For all possible values check http://developer.android.com/reference/android/content/res/Configuration.html
You will see there something like this:
HARDKEYBOARDHIDDEN_NO
HARDKEYBOARDHIDDEN_UNDEFINED
HARDKEYBOARDHIDDEN_YES
KEYBOARDHIDDEN_NO
KEYBOARDHIDDEN_UNDEFINED
KEYBOARDHIDDEN_YES
KEYBOARD_12KEY
KEYBOARD_NOKEYS
KEYBOARD_QWERTY
KEYBOARD_UNDEFINED
Also there you will be able to read something like this:
public int hardKeyboardHidden A flag indicating whether the hard keyboard has been hidden.
public int keyboard The kind of keyboard attached to the device.
public int keyboardHidden A flag indicating whether any keyboard is available.
UPDATE:
Here is a specific sample of what i´m talking about:
http://developer.android.com/guide/topics/resources/runtime-changes.html
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
// 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();
}
}
I hope this help you
The only way I was able to work around this is by setting my activity's android:windowSoftInputMode="adjustResize" and then embed a custom "detector view" in the layout to handle a container size change and propagate that as a custom event (via a Listener) for soft keyboard on/off.
The following post describes an approach to implementing it: EditText does not trigger changes when back is pressed
This works for me
parent.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
boolean someHasFocus = false;
if(host.hasFocus())
someHasFocus = true;
if(folder.hasFocus())
someHasFocus = true;
if(user.hasFocus())
someHasFocus = true;
if(pass.hasFocus())
someHasFocus = true;
if(someHasFocus){
if(bottom>oldBottom){
// Keyboard Close
viewToHide.setVisibility(View.VISIBLE);
}else if(bottom<oldBottom){
// Keyboard Open
viewToHide.setVisibility(View.GONE);
}
}else{
// show
viewToHide.setVisibility(View.VISIBLE);
}
}
});
Where parent is the main layout, viewToHide is the view that shows or hides when the keyboard is shown, and host,folder,user and pass are the EditText of my form.
And this in the manifest
android:windowSoftInputMode="stateHidden|adjustResize"
Hope this help
Here is my solution. It doesn't need android:windowSoftInputMode="adjustResize"
public abstract class KeyboardActivity extends Activity {
public static final int MIN_KEYBOARD_SIZE = 100;
private Window mRootWindow;
private View mRootView;
private int mKeyboardHeight = -1;
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
public int height;
public void onGlobalLayout() {
Rect r = new Rect();
View view = mRootWindow.getDecorView();
view.getWindowVisibleDisplayFrame(r);
if (height != r.height()) {
int diff = height - r.height();
height = r.height();
if (Math.abs(diff) > MIN_KEYBOARD_SIZE) {
int diff = height - r.height();
if (height != 0 && Math.abs(diff) > MIN_KEYBOARD_SIZE) {
mKeyboardHeight = Math.abs(diff);
if (diff > 0) {
onKeyboardOpen();
} else {
onKeyboardClosed();
}
}
height = r.height();
}
}
};
protected abstract void onKeyboardClosed();
protected abstract void onKeyboardOpen();
/**
* Should return keyboard height, if keyboard was shown at least once;
* @return keyboard height or -1
*/
protected int getKeyboardHeight() {
return mKeyboardHeight;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRootWindow = getWindow();
mRootView = mRootWindow.getDecorView().findViewById(android.R.id.content);
}
@Override
protected void onStart() {
super.onStart();
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
@Override
protected void onStop() {
super.onStop();
mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
}
}
Then I've just extend my activity from this activity and override onKeyboardClosed/onKeyboardOpen methods.
for this what i used to do same:
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();
}
}
}
}
and in your Activity
or fragment
call this method in onCreate()
private void hideAndShowKeyBOrd() {
InputMethodManager im = (InputMethodManager) getActivity().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() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
}
});
}
@Override
public void onSoftKeyboardShow() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (viewV.getVisibility() == View.VISIBLE) {
viewV.setVisibility(View.GONE);
}
}
});
}
});
}
enjoy your code:)
@BoD's answer works fine if I remove the following line.
if (mPreviousHeight != 0) {
/* other code is same, because
mPreviousHeight is 0 when it comes first */
}