I have used android:windowSoftInputMode=\"adjustResize\" in the manifest to prevent keyboard from hiding the activity button. It works, but when keyboard opens,
The method setSoftInputMode(int)
cannot be overrided, its implementation is inside Window
class and I don't think that's possible to replace the current window by your own. You cannot manage this from WindowManager
too.
You could create a listener on a ViewGroup
and catch the modification's layout when the SoftKeyboard
is opening and closing. Indeed, when the SKB shows up, the container's layout redraws and changes its height. From this event, you could manage to set an Animation
on views child and make a smooth effect.
Edit: The solution by using a GlobalLayoutListener on the rootview is also possible instead of (the solution presented below:) creating a custom viewgroup class.
You have to create your own viewgroup and make it as a parent container in the layout. It'll implement an interface which will be handled in the UI class (Activity
, Fragment
, whatever). I found this blog to detect the events on SKB for (~)all versions. According to it, here's the viewgroup's class to handle the height's changes:
public class ContainerViewHandler extends RelativeLayout {
private boolean isKeyboardShown;
private onKeyboardStateChange listener;
public ContainerViewHandler(Context context) {
super(context);
}
public ContainerViewHandler(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ContainerViewHandler(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setKeyboardStateListener(onKeyboardStateChange listener) {
this.listener = listener;
}
// Callbacks
public interface onKeyboardStateChange {
void onKeyboardShow();
void onKeyboardHide();
}
@Override
public boolean dispatchKeyEventPreIme(@NonNull KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
// Keyboard is hiding
if (isKeyboardShown) {
isKeyboardShown = false;
listener.onKeyboardHide();
}
}
return super.dispatchKeyEventPreIme(event);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int proposedHeight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();
if (actualHeight > proposedHeight) {
// Keyboard is showing
if (!isKeyboardShown) {
isKeyboardShown = true;
listener.onKeyboardShow();
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Now, you've to add this viewgroup in layout (eg <com.package.name.ContainerViewHandler .../>
). Then, you must implement the above interface setKeyboardStateListener
in the activity, like the following:
ContainerViewHandler containerView =
(ContainerViewHandler) findViewById(R.id.container_view);
containerView.setKeyboardStateListener(new ContainerHandler.onKeyboardStateChange() {
@Override
public void onKeyboardShow() {
Log.v("onKeyboardShow()", "SoftKeyboard is showing. Hello!");
}
@Override
public void onKeyboardHide() {
Log.v("onKeyboardHide()", "SoftKeyboard is hiding, Bye bye!");
}
});
Thus, you can manage different animations to handle and prevent the button to "jump" directly above the SKB. To test this out, I try to reproduce a bounce effect:
This how my implementation looks like:
containerView.setKeyboardStateListener(new ContainerViewHandler.onKeyboardStateChange() {
@Override
public void onKeyboardShow() {
setAnimationUp();
}
@Override
public void onKeyboardHide() {
setAnimationDown();
}
});
private void setAnimationUp() {
footerButton.setVisibility(View.GONE);
float dpY = AppUtils.convertPxToDp(20, this); // custom conversion method
Animation a1 = new TranslateAnimation(0, 0, footerButton.getHeight() * 4, -(dpY));
a1.setDuration(250);
a1.setFillAfter(true);
final Animation a2 = new TranslateAnimation(0, 0, -(dpY), 0);
a2.setDuration(320);
a2.setFillAfter(true);
a1.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
footerButton.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animation animation) {
footerButton.startAnimation(a2);
}
@Override
public void onAnimationRepeat(Animation animation) { }
});
footerButton.startAnimation(a1);
}
private void setAnimationDown() {
float dpY = AppUtils.convertPxToDp(30, this); // custom conversion method
Animation b1 = new TranslateAnimation(0, 0, -(dpY), dpY);
b1.setDuration(300);
b1.setFillAfter(true);
final Animation b2 = new TranslateAnimation(0, 0, dpY, 0);
b2.setDuration(320);
b2.setFillAfter(true);
b1.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) { }
@Override
public void onAnimationEnd(Animation animation) {
footerButton.startAnimation(b2);
}
@Override
public void onAnimationRepeat(Animation animation) { }
});
footerButton.startAnimation(b1);
}
I begin by setting two animations (a1, a2) in the first callback:
4xbutton's height
) and up to 20dp
above it, 20dp
and return to 0dp
(normal position).And two others (b1, b2) in the second callback:
30dp
at top and go down to 30dp
outside the parent container, 30dp
out to 0dp
(the initial position). PS: don't forget to use adjustResize
in the Manifest and to make the content (eg edittexts in my tests) above
the footer button which has alignParentBottom
to true.