Added possibility to use canned animation for icons

Icons now move in a canned animation from the shelf position
to the icon.

Test: adb shell setprop debug.icon_opening_animations true && adb shell killall com.android.systemui
Bug: 32437839
Change-Id: I82b6de37ac7a179aeb5d16bd663d566c2f338b1a
This commit is contained in:
Selim Cinek
2016-11-22 16:38:51 -08:00
parent 7e7c6e2c8e
commit 2b549f4bd4
8 changed files with 280 additions and 112 deletions

View File

@@ -193,7 +193,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private View mChildAfterViewWhenDismissed;
private View mGroupParentWhenDismissed;
private boolean mRefocusOnDismiss;
private float mIconTransformationAmount;
private float mContentTransformationAmount;
private boolean mIconsVisible = true;
private boolean mAboveShelf;
private boolean mIsLastChild;
@@ -837,23 +837,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
/**
* Set how much this notification is transformed into an icon.
*
* @param iconTransformationAmount A value from 0 to 1 indicating how much we are transformed
* to an icon
* @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed
* to the content away
* @param isLastChild is this the last child in the list. If true, then the transformation is
* different since it's content fades out.
*/
public void setIconTransformationAmount(float iconTransformationAmount, boolean isLastChild) {
public void setContentTransformationAmount(float contentTransformationAmount,
boolean isLastChild) {
boolean changeTransformation = isLastChild != mIsLastChild;
changeTransformation |= mIconTransformationAmount != iconTransformationAmount;
changeTransformation |= mContentTransformationAmount != contentTransformationAmount;
mIsLastChild = isLastChild;
mIconTransformationAmount = iconTransformationAmount;
mContentTransformationAmount = contentTransformationAmount;
if (changeTransformation) {
updateContentTransformation();
boolean iconsVisible = mIconTransformationAmount == 0.0f;
if (iconsVisible != mIconsVisible) {
mIconsVisible = iconsVisible;
updateIconVisibilities();
}
}
}
/**
* Set the icons to be visible of this notification.
*/
public void setIconsVisible(boolean iconsVisible) {
if (iconsVisible != mIconsVisible) {
mIconsVisible = iconsVisible;
updateIconVisibilities();
}
}
@@ -864,9 +870,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private void updateContentTransformation() {
float contentAlpha;
float translationY = - mIconTransformationAmount * mIconTransformContentShift;
float translationY = -mContentTransformationAmount * mIconTransformContentShift;
if (mIsLastChild) {
contentAlpha = 1.0f - mIconTransformationAmount;
contentAlpha = 1.0f - mContentTransformationAmount;
contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f);
contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha);
translationY *= 0.4f;
@@ -1871,8 +1877,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
@Override
protected void onYTranslationAnimationFinished() {
super.onYTranslationAnimationFinished();
protected void onYTranslationAnimationFinished(View view) {
super.onYTranslationAnimationFinished(view);
if (mHeadsupDisappearRunning) {
setHeadsUpAnimatingAway(false);
}

View File

@@ -208,6 +208,12 @@ public class NotificationData {
expandedIcon = null;
throw new IconException("Couldn't create icon: " + ic);
}
expandedIcon.setOnVisibilityChangedListener(
newVisibility -> {
if (row != null) {
row.setIconsVisible(newVisibility != View.VISIBLE);
}
});
}
public void setIconTag(int key, Object tag) {

View File

@@ -18,7 +18,10 @@ package com.android.systemui.statusbar;
import android.content.Context;
import android.content.res.Configuration;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;
@@ -34,7 +37,6 @@ import com.android.systemui.statusbar.stack.ExpandableViewState;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackScrollState;
import java.util.ArrayList;
import java.util.WeakHashMap;
/**
@@ -43,10 +45,11 @@ import java.util.WeakHashMap;
*/
public class NotificationShelf extends ActivatableNotificationView {
private static final boolean USE_ANIMATIONS_WHEN_OPENING =
SystemProperties.getBoolean("debug.icon_opening_animations", true);
private ViewInvertHelper mViewInvertHelper;
private boolean mDark;
private NotificationIconContainer mShelfIcons;
private ArrayList<StatusBarIconView> mIcons = new ArrayList<>();
private ShelfState mShelfState;
private int[] mTmp = new int[2];
private boolean mHideBackground;
@@ -204,8 +207,6 @@ public class NotificationShelf extends ActivatableNotificationView {
continue;
}
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
StatusBarIconView icon = row.getEntry().expandedIcon;
NotificationIconContainer.IconState iconState = iconStates.get(icon);
float notificationClipEnd;
float shelfStart = getTranslationY();
boolean aboveShelf = row.getTranslationZ() > mAmbientState.getBaseZHeight();
@@ -223,8 +224,7 @@ public class NotificationShelf extends ActivatableNotificationView {
}
}
updateNotificationClipHeight(row, notificationClipEnd);
float inShelfAmount = updateIconAppearance(row, iconState, icon, expandAmount,
isLastChild);
float inShelfAmount = updateIconAppearance(row, expandAmount, isLastChild);
numViewsInShelf += inShelfAmount;
int ownColorUntinted = row.getBackgroundColorWithoutTint();
if (row.getTranslationY() >= getTranslationY() && mNotGoneIndex == -1) {
@@ -250,7 +250,7 @@ public class NotificationShelf extends ActivatableNotificationView {
}
mShelfIcons.calculateIconTranslations();
mShelfIcons.applyIconStates();
setVisibility(numViewsInShelf != 0.0f && mAmbientState.isShadeExpanded()
setVisibility(mAmbientState.isShadeExpanded()
? VISIBLE
: INVISIBLE);
boolean hideBackground = numViewsInShelf < 1.0f;
@@ -275,41 +275,103 @@ public class NotificationShelf extends ActivatableNotificationView {
/**
* @return the icon amount how much this notification is in the shelf;
*/
private float updateIconAppearance(ExpandableNotificationRow row,
NotificationIconContainer.IconState iconState, StatusBarIconView icon,
float expandAmount, boolean isLastChild) {
private float updateIconAppearance(ExpandableNotificationRow row, float expandAmount,
boolean isLastChild) {
// Let calculate how much the view is in the shelf
float viewStart = row.getTranslationY();
int transformHeight = row.getActualHeight() + mPaddingBetweenElements;
int fullHeight = row.getActualHeight() + mPaddingBetweenElements;
float iconTransformDistance = getIntrinsicHeight() * 1.5f;
if (isLastChild) {
transformHeight =
Math.min(transformHeight, row.getMinHeight() - getIntrinsicHeight());
fullHeight = Math.min(fullHeight, row.getMinHeight() - getIntrinsicHeight());
iconTransformDistance = Math.min(iconTransformDistance, row.getMinHeight()
- getIntrinsicHeight());
}
float viewEnd = viewStart + transformHeight;
float iconAppearAmount;
float yTranslation;
float alpha = 1.0f;
float viewEnd = viewStart + fullHeight;
float fullTransitionAmount;
float iconTransitonAmount;
if (viewEnd >= getTranslationY() && (mAmbientState.isShadeExpanded()
|| (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
if (viewStart < getTranslationY()) {
float linearAmount = (getTranslationY() - viewStart) / transformHeight;
float fullAmount = (getTranslationY() - viewStart) / fullHeight;
float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation(
linearAmount);
fullAmount);
interpolatedAmount = NotificationUtils.interpolate(
interpolatedAmount, linearAmount, expandAmount);
iconAppearAmount = 1.0f - interpolatedAmount;
interpolatedAmount, fullAmount, expandAmount);
fullTransitionAmount = 1.0f - interpolatedAmount;
iconTransitonAmount = (getTranslationY() - viewStart) / iconTransformDistance;
iconTransitonAmount = Math.min(1.0f, iconTransitonAmount);
iconTransitonAmount = 1.0f - iconTransitonAmount;
} else {
iconAppearAmount = 1.0f;
fullTransitionAmount = 1.0f;
iconTransitonAmount = 1.0f;
}
} else {
iconAppearAmount = 0.0f;
fullTransitionAmount = 0.0f;
iconTransitonAmount = 0.0f;
}
row.setContentTransformationAmount(iconTransitonAmount, isLastChild);
updateIconPositioning(row, iconTransitonAmount, fullTransitionAmount);
return fullTransitionAmount;
}
private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount,
float fullTransitionAmount) {
StatusBarIconView icon = row.getEntry().expandedIcon;
NotificationIconContainer.IconState iconState = getIconState(icon);
if (iconState == null) {
return;
}
float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f;
boolean isLastChild = isLastChild(row);
if (clampedAmount == iconTransitionAmount) {
iconState.keepClampedPosition = false;
}
if (clampedAmount == fullTransitionAmount) {
iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f;
}
float transitionAmount;
boolean needCannedAnimation = iconState.clampedAppearAmount == 1.0f
&& clampedAmount == 0.0f;
if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount) {
transitionAmount = iconTransitionAmount;
} else if (iconState.keepClampedPosition
&& iconState.clampedAppearAmount != clampedAmount) {
// We animated to the clamped amount but then decided to go the other way. Let's
// animate it to the new position
transitionAmount = iconTransitionAmount;
iconState.needsCannedAnimation = true;
iconState.keepClampedPosition = false;
} else if (needCannedAnimation || iconState.keepClampedPosition
|| iconState.iconAppearAmount == 1.0f) {
// We need to perform a canned animation since we crossed the treshhold
transitionAmount = clampedAmount;
iconState.keepClampedPosition = iconState.keepClampedPosition || needCannedAnimation;
iconState.needsCannedAnimation = needCannedAnimation;
} else {
transitionAmount = iconTransitionAmount;
}
iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING
|| iconState.useFullTransitionAmount
? fullTransitionAmount
: transitionAmount;
iconState.clampedAppearAmount = clampedAmount;
setIconTransformationAmount(row, transitionAmount);
}
private boolean isLastChild(ExpandableNotificationRow row) {
return row == mAmbientState.getLastVisibleBackgroundChild();
}
private void setIconTransformationAmount(ExpandableNotificationRow row,
float transitionAmount) {
StatusBarIconView icon = row.getEntry().expandedIcon;
NotificationIconContainer.IconState iconState = getIconState(icon);
// Lets now calculate how much of the transformation has already happened. This is different
// from the above, since we only start transforming when the view is already quite a bit
// pushed in.
View rowIcon = row.getNotificationIcon();
float notificationIconPosition = viewStart;
float notificationIconPosition = row.getTranslationY();
float notificationIconSize = 0.0f;
int iconTopPadding;
if (rowIcon != null) {
@@ -322,28 +384,18 @@ public class NotificationShelf extends ActivatableNotificationView {
float shelfIconPosition = getTranslationY() + icon.getTop();
shelfIconPosition += ((1.0f - icon.getIconScale()) * icon.getHeight()) / 2.0f;
float transitionDistance = getIntrinsicHeight() * 1.5f;
if (isLastChild) {
if (row == mAmbientState.getLastVisibleBackgroundChild()) {
transitionDistance = Math.min(transitionDistance, row.getMinHeight()
- getIntrinsicHeight());
}
float transformationStartPosition = getTranslationY() - transitionDistance;
float transitionAmount = 0.0f;
if (viewStart < transformationStartPosition
|| (!mAmbientState.isShadeExpanded()
&& (row.isPinned() || row.isHeadsUpAnimatingAway()))) {
// We simply place it on the icon of the notification
yTranslation = notificationIconPosition - shelfIconPosition;
} else {
transitionAmount = (viewStart - transformationStartPosition)
/ transitionDistance;
float startPosition = transformationStartPosition + iconTopPadding;
yTranslation = NotificationUtils.interpolate(
startPosition - shelfIconPosition, 0, transitionAmount);
// If we are merging into the shelf, lets make sure the shelf is at least on our height,
// otherwise the icons won't be visible.
setTranslationZ(Math.max(getTranslationZ(), row.getTranslationZ()));
}
float iconYTranslation = NotificationUtils.interpolate(
Math.min(notificationIconPosition, transformationStartPosition + iconTopPadding)
- shelfIconPosition,
0,
transitionAmount);
float shelfIconSize = icon.getHeight() * icon.getIconScale();
float alpha = 1.0f;
if (!row.isShowingIcon()) {
// The view currently doesn't have an icon, lets transform it in!
alpha = transitionAmount;
@@ -352,15 +404,12 @@ public class NotificationShelf extends ActivatableNotificationView {
// The notification size is different from the size in the shelf / statusbar
float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
transitionAmount);
row.setIconTransformationAmount(transitionAmount, isLastChild);
if (iconState != null) {
iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale();
iconState.scaleY = iconState.scaleX;
iconState.hidden = transitionAmount == 0.0f;
iconState.iconAppearAmount = iconAppearAmount;
iconState.alpha = alpha;
iconState.yTranslation = yTranslation;
icon.setVisibility(transitionAmount == 0.0f ? INVISIBLE : VISIBLE);
iconState.yTranslation = iconYTranslation;
if (row.isInShelf() && !row.isTransformingIntoShelf()) {
iconState.iconAppearAmount = 1.0f;
iconState.alpha = 1.0f;
@@ -369,7 +418,10 @@ public class NotificationShelf extends ActivatableNotificationView {
iconState.hidden = false;
}
}
return iconAppearAmount;
}
private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) {
return mShelfIcons.getIconState(icon);
}
private float getFullyClosedTranslation() {

View File

@@ -37,6 +37,7 @@ import android.util.FloatProperty;
import android.util.Log;
import android.util.Property;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
@@ -102,6 +103,7 @@ public class StatusBarIconView extends AnimatedImageView {
private ObjectAnimator mIconAppearAnimator;
private ObjectAnimator mDotAnimator;
private float mDotAppearAmount;
private OnVisibilityChangedListener mOnVisibilityChangedListener;
public StatusBarIconView(Context context, String slot, Notification notification) {
this(context, slot, notification, false);
@@ -525,7 +527,23 @@ public class StatusBarIconView extends AnimatedImageView {
invalidate();
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (mOnVisibilityChangedListener != null) {
mOnVisibilityChangedListener.onVisibilityChanged(visibility);
}
}
public float getDotAppearAmount() {
return mDotAppearAmount;
}
public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
mOnVisibilityChangedListener = listener;
}
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
}

View File

@@ -40,6 +40,7 @@ import java.util.WeakHashMap;
public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private static final String TAG = "NotificationIconContainer";
private static final boolean DEBUG = false;
private static final int CANNED_ANIMATION_DURATION = 100;
private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
@@ -49,6 +50,26 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}.setDuration(200);
private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha();
// TODO: add scale
@Override
public AnimationFilter getAnimationFilter() {
return mAnimationFilter;
}
}.setDuration(CANNED_ANIMATION_DURATION);
private static final AnimationProperties mTempProperties = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter();
// TODO: add scale
@Override
public AnimationFilter getAnimationFilter() {
return mAnimationFilter;
}
}.setDuration(CANNED_ANIMATION_DURATION);
private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
@@ -66,7 +87,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
private float mActualPaddingEnd = -1;
private float mActualPaddingStart = -1;
private boolean mChangingViewPositions;
private int mAnimationStartIndex = -1;
private int mAddAnimationStartIndex = -1;
private int mCannedAnimationStartIndex = -1;
public NotificationIconContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -121,7 +143,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
childState.applyToView(child);
}
}
mAnimationStartIndex = -1;
mAddAnimationStartIndex = -1;
mCannedAnimationStartIndex = -1;
}
@Override
@@ -133,10 +156,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
int childIndex = indexOfChild(child);
if (childIndex < getChildCount() - 1
&& mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) {
if (mAnimationStartIndex < 0) {
mAnimationStartIndex = childIndex;
if (mAddAnimationStartIndex < 0) {
mAddAnimationStartIndex = childIndex;
} else {
mAnimationStartIndex = Math.min(mAnimationStartIndex, childIndex);
mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
}
}
}
@@ -149,10 +172,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
&& child.getVisibility() == VISIBLE) {
int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX());
if (mAnimationStartIndex < 0) {
mAnimationStartIndex = animationStartIndex;
if (mAddAnimationStartIndex < 0) {
mAddAnimationStartIndex = animationStartIndex;
} else {
mAnimationStartIndex = Math.min(mAnimationStartIndex, animationStartIndex);
mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, animationStartIndex);
}
}
if (!mChangingViewPositions) {
@@ -305,28 +328,64 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
mChangingViewPositions = changingViewPositions;
}
public IconState getIconState(StatusBarIconView icon) {
return mIconStates.get(icon);
}
public class IconState extends ViewState {
public float iconAppearAmount = 1.0f;
public float clampedAppearAmount = 1.0f;
public int visibleState;
public boolean justAdded = true;
public boolean needsCannedAnimation;
public boolean keepClampedPosition;
public boolean useFullTransitionAmount;
@Override
public void applyToView(View view) {
if (view instanceof StatusBarIconView) {
StatusBarIconView icon = (StatusBarIconView) view;
AnimationProperties animationProperties = DOT_ANIMATION_PROPERTIES;
boolean animate = false;
AnimationProperties animationProperties = null;
if (justAdded) {
super.applyToView(icon);
icon.setAlpha(0.0f);
icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */);
animationProperties = ADD_ICON_PROPERTIES;
animate = true;
} else if (visibleState != icon.getVisibleState()) {
animationProperties = DOT_ANIMATION_PROPERTIES;
animate = true;
}
boolean animate = visibleState != icon.getVisibleState() || justAdded;
if (!animate && mAnimationStartIndex >= 0
if (!animate && mAddAnimationStartIndex >= 0
&& indexOfChild(view) >= mAddAnimationStartIndex
&& (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
|| visibleState != StatusBarIconView.STATE_HIDDEN)) {
int viewIndex = indexOfChild(view);
animate = viewIndex >= mAnimationStartIndex;
animationProperties = DOT_ANIMATION_PROPERTIES;
animate = true;
}
if (needsCannedAnimation) {
AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
animationFilter.reset();
animationFilter.combineFilter(ICON_ANIMATION_PROPERTIES.getAnimationFilter());
if (animationProperties != null) {
animationFilter.combineFilter(animationProperties.getAnimationFilter());
}
animationProperties = mTempProperties;
animationProperties.setDuration(CANNED_ANIMATION_DURATION);
animate = true;
mCannedAnimationStartIndex = indexOfChild(view);
}
if (!animate && mCannedAnimationStartIndex >= 0
&& indexOfChild(view) > mCannedAnimationStartIndex
&& (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
|| visibleState != StatusBarIconView.STATE_HIDDEN)) {
AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
animationFilter.reset();
animationFilter.animateX();
animationProperties = mTempProperties;
animationProperties.setDuration(CANNED_ANIMATION_DURATION);
animate = true;
}
icon.setVisibleState(visibleState);
if (animate) {
@@ -336,6 +395,13 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}
justAdded = false;
needsCannedAnimation = false;
}
protected void onYTranslationAnimationFinished(View view) {
if (hidden) {
view.setVisibility(INVISIBLE);
}
}
}
}

View File

@@ -120,7 +120,7 @@ public class AnimationFilter {
}
}
private void combineFilter(AnimationFilter filter) {
public void combineFilter(AnimationFilter filter) {
animateAlpha |= filter.animateAlpha;
animateX |= filter.animateX;
animateY |= filter.animateY;
@@ -134,7 +134,7 @@ public class AnimationFilter {
hasDelays |= filter.hasDelays;
}
private void reset() {
public void reset() {
animateAlpha = false;
animateX = false;
animateY = false;

View File

@@ -213,7 +213,7 @@ public class NotificationChildrenContainer extends ViewGroup {
mDividers.add(newIndex, divider);
updateGroupOverflow();
row.setIconTransformationAmount(0, false /* isLastChild */);
row.setContentTransformationAmount(0, false /* isLastChild */);
}
public void removeNotification(ExpandableNotificationRow row) {

View File

@@ -99,36 +99,6 @@ public class ViewState {
// don't do anything with it
return;
}
boolean becomesInvisible = this.alpha == 0.0f || this.hidden;
boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
if (animatingAlpha) {
updateAlphaAnimation(view);
} else if (view.getAlpha() != this.alpha) {
// apply layer type
boolean becomesFullyVisible = this.alpha == 1.0f;
boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
&& view.hasOverlappingRendering();
int layerType = view.getLayerType();
int newLayerType = newLayerTypeIsHardware
? View.LAYER_TYPE_HARDWARE
: View.LAYER_TYPE_NONE;
if (layerType != newLayerType) {
view.setLayerType(newLayerType, null);
}
// apply alpha
view.setAlpha(this.alpha);
}
// apply visibility
int oldVisibility = view.getVisibility();
int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
if (newVisibility != oldVisibility) {
if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
// We don't want views to change visibility when they are animating to GONE
view.setVisibility(newVisibility);
}
}
// apply xTranslation
boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X);
@@ -163,6 +133,53 @@ public class ViewState {
if (view.getScaleY() != this.scaleY) {
view.setScaleY(this.scaleY);
}
boolean becomesInvisible = this.alpha == 0.0f || (this.hidden && !isAnimating(view));
boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
if (animatingAlpha) {
updateAlphaAnimation(view);
} else if (view.getAlpha() != this.alpha) {
// apply layer type
boolean becomesFullyVisible = this.alpha == 1.0f;
boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
&& view.hasOverlappingRendering();
int layerType = view.getLayerType();
int newLayerType = newLayerTypeIsHardware
? View.LAYER_TYPE_HARDWARE
: View.LAYER_TYPE_NONE;
if (layerType != newLayerType) {
view.setLayerType(newLayerType, null);
}
// apply alpha
view.setAlpha(this.alpha);
}
// apply visibility
int oldVisibility = view.getVisibility();
int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
if (newVisibility != oldVisibility) {
if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
// We don't want views to change visibility when they are animating to GONE
view.setVisibility(newVisibility);
}
}
}
protected boolean isAnimating(View view) {
if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_X)) {
return true;
}
if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y)) {
return true;
}
if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z)) {
return true;
}
if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
return true;
}
return false;
}
private boolean isAnimating(View view, int tag) {
@@ -482,7 +499,7 @@ public class ViewState {
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
child.setTag(TAG_START_TRANSLATION_Y, null);
child.setTag(TAG_END_TRANSLATION_Y, null);
onYTranslationAnimationFinished();
onYTranslationAnimationFinished(child);
}
});
startAnimator(animator, listener);
@@ -491,7 +508,10 @@ public class ViewState {
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
}
protected void onYTranslationAnimationFinished() {
protected void onYTranslationAnimationFinished(View view) {
if (hidden) {
view.setVisibility(View.INVISIBLE);
}
}
protected void startAnimator(Animator animator, AnimatorListenerAdapter listener) {