From 3af00cf10660c7fdc0582dc12361c13673d0c9bb Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Wed, 7 May 2014 17:27:26 +0200 Subject: [PATCH] Improved notification scroller animation logic When an animation was already running, the calculation of the new duration was wrong. We are now also starting the animation instantly instead of waiting for the next frame. Also improved the scrolling performance, which was lagging behind by one frame Change-Id: I25d6e6eedf33d94f2f90bdc39d863955c707370c --- .../stack/NotificationStackScrollLayout.java | 6 +- .../statusbar/stack/StackStateAnimator.java | 186 ++++++++++-------- 2 files changed, 102 insertions(+), 90 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index b72909f7970e3..f8aab80a621a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -695,7 +695,7 @@ public class NotificationStackScrollLayout extends ViewGroup // mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity()); // } } - requestChildrenUpdate(); + updateChildren(); } // Keep on drawing until the animation has finished. @@ -705,7 +705,7 @@ public class NotificationStackScrollLayout extends ViewGroup private void customScrollTo(int y) { mOwnScrollY = y; - requestChildrenUpdate(); + updateChildren(); } @Override @@ -721,7 +721,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (clampedY) { mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange()); } - requestChildrenUpdate(); + updateChildren(); } else { customScrollTo(scrollY); scrollTo(scrollX, mScrollY); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 3281b670c136c..2e700aa508cf0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -132,10 +132,10 @@ public class StackStateAnimator { ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); if (newDuration <= 0) { - if (previousAnimator == null) { - // no animation was running, but also no new animation should be performed, - // lets just apply the value - child.setActualHeight(viewState.height); + // no new animation needed, let's just apply the value + child.setActualHeight(viewState.height); + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); } return; } @@ -158,7 +158,7 @@ public class StackStateAnimator { child.setTag(TAG_END_HEIGHT, null); } }); - animator.start(); + startInstantly(animator); child.setTag(TAG_ANIMATOR_HEIGHT, animator); child.setTag(TAG_END_HEIGHT, viewState.height); } @@ -173,13 +173,13 @@ public class StackStateAnimator { ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); if (newDuration <= 0) { - if (previousAnimator == null) { - // no animation was running, but also no new animation should be performed, - // lets just apply the value - child.setAlpha(endAlpha); - if (endAlpha == 0) { - child.setVisibility(View.INVISIBLE); - } + // no new animation needed, let's just apply the value + child.setAlpha(endAlpha); + if (endAlpha == 0) { + child.setVisibility(View.INVISIBLE); + } + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); } return; } @@ -213,6 +213,7 @@ public class StackStateAnimator { mWasCancelled = false; } }); + animator.setDuration(newDuration); animator.addListener(getGlobalAnimationFinishedListener()); // remove the tag when the animation is finished animator.addListener(new AnimatorListenerAdapter() { @@ -221,11 +222,90 @@ public class StackStateAnimator { } }); - animator.start(); + startInstantly(animator); child.setTag(TAG_ANIMATOR_ALPHA, animator); child.setTag(TAG_END_ALPHA, endAlpha); } + private void startZTranslationAnimation(final ExpandableView child, + final StackScrollState.ViewState viewState, boolean hasNewEvents) { + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); + if (previousEndValue != null && previousEndValue == viewState.zTranslation) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); + if (newDuration <= 0) { + // no new animation needed, let's just apply the value + child.setTranslationZ(viewState.zTranslation); + + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); + } + return; + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, + child.getTranslationZ(), viewState.zTranslation); + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); + child.setTag(TAG_END_TRANSLATION_Z, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); + child.setTag(TAG_END_TRANSLATION_Z, viewState.zTranslation); + } + + private void startYTranslationAnimation(final ExpandableView child, + StackScrollState.ViewState viewState, boolean hasNewEvents) { + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); + if (previousEndValue != null && previousEndValue == viewState.yTranslation) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); + if (newDuration <= 0) { + // no new animation needed, let's just apply the value + child.setTranslationY(viewState.yTranslation); + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); + } + return; + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, + child.getTranslationY(), viewState.yTranslation); + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); + child.setTag(TAG_END_TRANSLATION_Y, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); + child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation); + } + + /** + * Start an animator instantly instead of waiting on the next synchronization frame + */ + private void startInstantly(ValueAnimator animator) { + animator.start(); + animator.setCurrentPlayTime(0); + } + /** * @return an adapter which ensures that onAnimationFinished is called once no animation is * running anymore @@ -259,75 +339,6 @@ public class StackStateAnimator { mWasCancelled = false; } }; - - } - - private void startZTranslationAnimation(final ExpandableView child, - final StackScrollState.ViewState viewState, boolean hasNewEvents) { - Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); - if (previousEndValue != null && previousEndValue == viewState.zTranslation) { - return; - } - ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); - long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); - if (newDuration <= 0) { - if (previousAnimator == null) { - // no animation was running, but also no new animation should be performed, - // lets just apply the value - child.setTranslationZ(viewState.zTranslation); - } - return; - } - - ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, - child.getTranslationZ(), viewState.zTranslation); - animator.setInterpolator(mFastOutSlowInInterpolator); - animator.addListener(getGlobalAnimationFinishedListener()); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); - child.setTag(TAG_END_TRANSLATION_Z, null); - } - }); - animator.start(); - child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); - child.setTag(TAG_END_TRANSLATION_Z, viewState.zTranslation); - } - - private void startYTranslationAnimation(final ExpandableView child, - StackScrollState.ViewState viewState, boolean hasNewEvents) { - Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); - if (previousEndValue != null && previousEndValue == viewState.yTranslation) { - return; - } - ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); - long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); - if (newDuration <= 0) { - if (previousAnimator == null) { - // no animation was running, but also no new animation should be performed, - // lets just apply the value - child.setTranslationY(viewState.yTranslation); - } - return; - } - - ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, - child.getTranslationY(), viewState.yTranslation); - animator.setInterpolator(mFastOutSlowInInterpolator); - animator.addListener(getGlobalAnimationFinishedListener()); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); - child.setTag(TAG_END_TRANSLATION_Y, null); - } - }); - animator.start(); - child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); - child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation); } private T getChildTag(View child, int tag) { @@ -343,18 +354,19 @@ public class StackStateAnimator { */ private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator, boolean hasNewEvents) { + long newDuration = ANIMATION_DURATION; if (previousAnimator != null) { - previousAnimator.cancel(); if (!hasNewEvents) { // This is only an update, no new event came in. lets just take the remaining // duration as the new duration - return (long) ((1.0f - previousAnimator.getAnimatedFraction()) * - previousAnimator.getDuration()); + newDuration = previousAnimator.getDuration() + - previousAnimator.getCurrentPlayTime(); } + previousAnimator.cancel(); } else if (!hasNewEvents){ - return 0; + newDuration = 0; } - return ANIMATION_DURATION; + return newDuration; } private void onAnimationFinished() {