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> ```

Original source

Related problems