Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animate Window Layout Change

I've got a DialogFragment, normally centered on the screen, that I'm trying to move out of the way of the on screen keyboard, if any should appear, because it's not a good UX experience for the keyboard to cover over parts of the window when there's perfectly unused screen real estate farther up.

Suppose I've solved the problem of detecting the keyboard appearing or disappearing, e.g. How to check visibility of software keyboard in Android? .

Currently I go to move the window out of the way by doing something like this:

...
final WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
params.gravity = Gravity.TOP;
params.verticalMargin = .1f;  //or whatever
dialog.getWindow().setAttributes(params);
...

This works fine, but the window suddenly jerks into place, which isn't a pleasant UX experience. The window in question has a successful enter and exit animation - and these even work appropriately after the window layout change. How can I further animate the window between WindowManager.LayoutParams changes?

(I'd prefer if possible to keep working in terms of layout {of the} / {within the} http://developer.android.com/reference/android/view/Window.html rather than, say, forcing the DialogFragment into my activity's layout and animating it from within there).

like image 351
jdowdell Avatar asked Oct 15 '14 04:10

jdowdell


1 Answers

I didn't have time to wait for the bounty to expire, so I coded the below stop gap until I can get a better solution. In case it helps anybody else, or gives them an idea for a bounty-worthy answer, this is what I did. However, I suspect its inefficient as all hell, since I assume it forces a window relayout every animation frame rather than just panning a bitmap across the screen. This isn't all of it of course, but is the critical bit:

// Not shown: setting currentVerticalMargin, targetVerticalMargin, or calling this method
private synchronized void restartVerticalMarginAnimator() {
    if (verticalMarginAnimator != null) {
        return;
    }
    final Dialog dialog = this.getDialog();
    if (dialog == null) {
        return;
    }
    final WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
    verticalMarginAnimator = new TimeAnimator();
    verticalMarginAnimator.setTimeListener(new TimeListener() {
        @Override
        public void onTimeUpdate(TimeAnimator a, long totalTime, long deltaTime) {
            float stretch = targetVerticalMargin - currentVerticalMargin;
            float distance = WINDOW_ANIMATION_SPEED * deltaTime / 1000L;
            boolean finished = false;

            // Adjust distance so it's capped at "going all the way to target" and no further,
            // and has the right sign if we're animating upward.
            if (distance > Math.abs(stretch)) {
                distance = stretch;
                finished = true;
            } else if (stretch < 0) {
                distance *= -1f;
            }

            // Move.
            currentVerticalMargin += distance;
            if (finished) {
                verticalMarginAnimator.end();
                verticalMarginAnimator = null;
            }
            params.verticalMargin = currentVerticalMargin;
            dialog.getWindow().setAttributes(params);
        }
    });
    verticalMarginAnimator.start();
}
like image 88
jdowdell Avatar answered Nov 01 '22 13:11

jdowdell