AdjustResize with animation
android, android-animation, android-softkeyboard
Solution
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:
- a1: start below (behind) the SKB (about `4xbutton's height`) and up to `20dp` above it,
- a2: start at `20dp` and return to `0dp` (normal position).
And two others (b1, b2) in the second callback:
- b1: start where the button is up to `30dp` at top and go down to `30dp` outside the parent container,
- b2: finaly, from `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.
Problem
I have used android:windowSoftInputMode="adjustResize" in the manifest to prevent keyboard from hiding the activity button. It works, but when keyboard opens, my button moves up. It is a bit jumpy, I wanted to know - can I have any animation for that to transition smoothly? ``` <activity android:name=".Activity" android:screenOrientation="portrait" android:windowSoftInputMode="adjustResize"> </activity> ```