diff --git a/lib/src/android/support/design/stateful/ExtendableSavedState.java b/lib/src/android/support/design/stateful/ExtendableSavedState.java index cb735c069..9ef4f01b8 100644 --- a/lib/src/android/support/design/stateful/ExtendableSavedState.java +++ b/lib/src/android/support/design/stateful/ExtendableSavedState.java @@ -16,23 +16,21 @@ package android.support.design.stateful; -import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; - import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.RestrictTo; import android.support.v4.util.SimpleArrayMap; import android.support.v4.view.AbsSavedState; /** - * Class for widgets that want to save and restore their own state in {@link + * SavedState for widgets that want to save and restore their own state in {@link * android.view.View#onSaveInstanceState()}. Supports widgets whose state is composed or delegated * out to multiple components. * - * @hide + *

Widgets with only composed or delegated state can directly instantiate this class and write to + * {@link #extendableStates}. Widgets with additional state should subclass ExtendableSavedState + * rather than trying to force the additional state into {@link #extendableStates}. */ -@RestrictTo(LIBRARY_GROUP) public class ExtendableSavedState extends AbsSavedState { public final SimpleArrayMap extendableStates; diff --git a/lib/src/android/support/design/widget/FloatingActionButton.java b/lib/src/android/support/design/widget/FloatingActionButton.java index 63e606ec2..98c9d69f5 100644 --- a/lib/src/android/support/design/widget/FloatingActionButton.java +++ b/lib/src/android/support/design/widget/FloatingActionButton.java @@ -38,7 +38,7 @@ import android.support.annotation.VisibleForTesting; import android.support.design.R; import android.support.design.stateful.ExtendableSavedState; import android.support.design.widget.FloatingActionButtonImpl.InternalVisibilityChangedListener; -import android.support.design.widget.expandable.ExpandableWidget; +import android.support.design.widget.expandable.ExpandableTransformationWidget; import android.support.design.widget.expandable.ExpandableWidgetHelper; import android.support.v4.view.ViewCompat; import android.support.v7.widget.AppCompatImageHelper; @@ -69,9 +69,11 @@ import java.util.List; * #setBackgroundTintList(ColorStateList)}. */ @CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class) -public class FloatingActionButton extends VisibilityAwareImageButton implements ExpandableWidget { +public class FloatingActionButton extends VisibilityAwareImageButton + implements ExpandableTransformationWidget { private static final String LOG_TAG = "FloatingActionButton"; + private static final String EXPANDABLE_WIDGET_HELPER_KEY = "expandableWidgetHelper"; /** Callback to be invoked when the visibility of a FloatingActionButton changes. */ public abstract static class OnVisibilityChangedListener { @@ -359,8 +361,8 @@ public class FloatingActionButton extends VisibilityAwareImageButton implements } @Override - public void setExpanded(boolean expanded) { - expandableWidgetHelper.setExpanded(expanded); + public boolean setExpanded(boolean expanded) { + return expandableWidgetHelper.setExpanded(expanded); } @Override @@ -506,7 +508,7 @@ public class FloatingActionButton extends VisibilityAwareImageButton implements ExtendableSavedState state = new ExtendableSavedState(superState); state.extendableStates.put( - "expandableWidgetHelper", expandableWidgetHelper.onSaveInstanceState()); + EXPANDABLE_WIDGET_HELPER_KEY, expandableWidgetHelper.onSaveInstanceState()); return state; } @@ -522,7 +524,7 @@ public class FloatingActionButton extends VisibilityAwareImageButton implements super.onRestoreInstanceState(ess.getSuperState()); expandableWidgetHelper.onRestoreInstanceState( - ess.extendableStates.get("expandableWidgetHelper")); + ess.extendableStates.get(EXPANDABLE_WIDGET_HELPER_KEY)); } /** diff --git a/lib/src/android/support/design/widget/expandable/ExpandableTransformationWidget.java b/lib/src/android/support/design/widget/expandable/ExpandableTransformationWidget.java new file mode 100644 index 000000000..24731bc14 --- /dev/null +++ b/lib/src/android/support/design/widget/expandable/ExpandableTransformationWidget.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.design.widget.expandable; + +import android.support.annotation.IdRes; + +/** + * An ExpandableWidget that visually transforms into another component when expanded. + */ +public interface ExpandableTransformationWidget extends ExpandableWidget { + + /** Returns the expanded component id hint. */ + @IdRes + int getExpandedComponentIdHint(); + + /** + * Sets the expanded component id hint, which may be used by a Behavior to determine whether it + * should handle this widget's state change. + */ + void setExpandedComponentIdHint(@IdRes int expandedComponentIdHint); +} diff --git a/lib/src/android/support/design/widget/expandable/ExpandableWidget.java b/lib/src/android/support/design/widget/expandable/ExpandableWidget.java index 34e3045d2..3bafd26c8 100644 --- a/lib/src/android/support/design/widget/expandable/ExpandableWidget.java +++ b/lib/src/android/support/design/widget/expandable/ExpandableWidget.java @@ -16,29 +16,60 @@ package android.support.design.widget.expandable; -import android.support.annotation.IdRes; - /** - * A widget that has expanded/collapsed state. + * A widget that has expanded/collapsed state. When the expanded state changes, {@link + * android.support.design.widget.CoordinatorLayout#dispatchDependentViewsChanged an event is + * dispatched} so that other widgets may react via a {@link + * android.support.design.widget.CoordinatorLayout.Behavior}. * - *

When the expanded state changes, an event is dispatched so that other widgets may react via a - * {@link android.support.design.widget.CoordinatorLayout.Behavior}. + *

Implementations of this interface should create an instance of {@link ExpandableWidgetHelper} + * and forward all calls to it. + * + *

The expanded state can saved across configuration changes by implementing {@link + * #onSaveInstanceState} and {@link #onRestoreInstanceState}: + * + *


+ * {@literal @}Override
+ * protected Parcelable onSaveInstanceState() {
+ *   Parcelable superState = super.onSaveInstanceState();
+ *   ExtendableSavedState state = new ExtendableSavedState(superState);
+ *
+ *   state.extendableStates.put(
+ *       "expandableWidgetHelper", expandableWidgetHelper.onSaveInstanceState());
+ *
+ *   return state;
+ * }
+ *
+ * {@literal @}Override
+ * protected void onRestoreInstanceState(Parcelable state) {
+ *   if (!(state instanceof ExtendableSavedState)) {
+ *     super.onRestoreInstanceState(state);
+ *     return;
+ *   }
+ *
+ *   ExtendableSavedState ess = (ExtendableSavedState) state;
+ *   super.onRestoreInstanceState(ess.getSuperState());
+ *
+ *   expandableWidgetHelper.onRestoreInstanceState(
+ *       ess.extendableStates.get("expandableWidgetHelper"));
+ * }
+ * 
*/ public interface ExpandableWidget { - /** Returns whether this widget is expanded. */ + /** + * Returns whether this widget is expanded. + * + *

Implementations should call {@link ExpandableWidgetHelper#isExpanded()}. + */ boolean isExpanded(); - /** Sets the expanded state on this widget. */ - void setExpanded(boolean expanded); - - /** Returns the expanded component id hint. */ - @IdRes - int getExpandedComponentIdHint(); - /** - * Sets the expanded component id hint, which may be used by a Behavior to determine whether it - * should handle this widget's state change. + * Sets the expanded state on this widget. + * + *

Implementations should call {@link ExpandableWidgetHelper#setExpanded(boolean)}. + * + * @return true if the expanded state changed as a result of this call. */ - void setExpandedComponentIdHint(@IdRes int expandedComponentIdHint); + boolean setExpanded(boolean expanded); } diff --git a/lib/src/android/support/design/widget/expandable/ExpandableWidgetHelper.java b/lib/src/android/support/design/widget/expandable/ExpandableWidgetHelper.java index ebb2c1745..0be36cba9 100644 --- a/lib/src/android/support/design/widget/expandable/ExpandableWidgetHelper.java +++ b/lib/src/android/support/design/widget/expandable/ExpandableWidgetHelper.java @@ -24,27 +24,30 @@ import android.view.View; import android.view.ViewParent; /** - * ExpandableWidgetHelper is a helper class for writing custom {@link ExpandableWidget - * ExpandableWidgets}. Please see each method's documentation when implementing your custom class. + * ExpandableWidgetHelper is a helper class for writing custom {@link ExpandableWidget}s and {@link + * ExpandableTransformationWidget}. Please see the interface documentation when implementing your + * custom class. */ public final class ExpandableWidgetHelper { private final View widget; - private boolean expanded; - @IdRes private int expandedComponentIdHint; + private boolean expanded = false; + @IdRes private int expandedComponentIdHint = 0; /** Call this from the constructor. */ - public ExpandableWidgetHelper(T widget) { - this.widget = widget; + public ExpandableWidgetHelper(ExpandableWidget widget) { + this.widget = (View) widget; } /** Call this from {@link ExpandableWidget#setExpanded(boolean)}. */ - public void setExpanded(boolean expanded) { + public boolean setExpanded(boolean expanded) { if (this.expanded != expanded) { this.expanded = expanded; dispatchExpandedStateChanged(); + return true; } + return false; } /** Call this from {@link ExpandableWidget#isExpanded()}. */ @@ -52,20 +55,7 @@ public final class ExpandableWidgetHelper { return expanded; } - /** Call this from {@link ExpandableWidget#setExpandedComponentIdHint(int)}. */ - public void setExpandedComponentIdHint(@IdRes int expandedComponentIdHint) { - this.expandedComponentIdHint = expandedComponentIdHint; - } - - /** Call this from {@link ExpandableWidget#getExpandedComponentIdHint()}. */ - @IdRes - public int getExpandedComponentIdHint() { - return expandedComponentIdHint; - } - - /** - * Call this from {@link View#onSaveInstanceState()} after your widget's custom state is saved. - */ + /** Call this from {@link View#onSaveInstanceState()}. */ public Bundle onSaveInstanceState() { Bundle state = new Bundle(); state.putBoolean("expanded", expanded); @@ -74,10 +64,7 @@ public final class ExpandableWidgetHelper { return state; } - /** - * Call this from {@link View#onRestoreInstanceState(Parcelable)} after your widget's custom state - * is restored. - */ + /** Call this from {@link View#onRestoreInstanceState(Parcelable)}. */ public void onRestoreInstanceState(Bundle state) { expanded = state.getBoolean("expanded", false); expandedComponentIdHint = state.getInt("expandedComponentIdHint", 0); @@ -87,6 +74,17 @@ public final class ExpandableWidgetHelper { } } + /** Call this from {@link ExpandableTransformationWidget#setExpandedComponentIdHint(int)}. */ + public void setExpandedComponentIdHint(@IdRes int expandedComponentIdHint) { + this.expandedComponentIdHint = expandedComponentIdHint; + } + + /** Call this from {@link ExpandableTransformationWidget#getExpandedComponentIdHint()}. */ + @IdRes + public int getExpandedComponentIdHint() { + return expandedComponentIdHint; + } + private void dispatchExpandedStateChanged() { ViewParent parent = widget.getParent(); if (parent instanceof CoordinatorLayout) { diff --git a/lib/src/android/support/design/widget/transformation/ExpandableBehavior.java b/lib/src/android/support/design/widget/transformation/ExpandableBehavior.java new file mode 100644 index 000000000..2827e16b0 --- /dev/null +++ b/lib/src/android/support/design/widget/transformation/ExpandableBehavior.java @@ -0,0 +1,146 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.support.design.widget.transformation; + +import android.content.Context; +import android.support.annotation.CallSuper; +import android.support.annotation.IntDef; +import android.support.annotation.Nullable; +import android.support.design.widget.CoordinatorLayout; +import android.support.design.widget.CoordinatorLayout.Behavior; +import android.support.design.widget.expandable.ExpandableWidget; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * Base Behavior for views that can react to an {@link ExpandableWidget}'s {@link + * ExpandableWidget#setExpanded(boolean)} state changes. + */ +public abstract class ExpandableBehavior extends Behavior { + + /** Unknown expanded state. */ + private static final int STATE_UNKNOWN = 0; + /** Expanded state. */ + private static final int STATE_EXPANDED = 1; + /** Collapsed state. */ + private static final int STATE_COLLAPSED = 2; + + @IntDef({STATE_UNKNOWN, STATE_EXPANDED, STATE_COLLAPSED}) + @Retention(RetentionPolicy.SOURCE) + private @interface State {} + + /** + * The current expanded state of this behavior. This state follows the expanded state of the + * {@link ExpandableWidget} dependency, and is updated in {@link #onLayoutChild(CoordinatorLayout, + * View, int)} and {@link #onDependentViewChanged(CoordinatorLayout, View, View)}. + * + *

This state may be {@link #STATE_UNKNOWN} before either of those callbacks have been invoked. + */ + @State private int currentState = STATE_UNKNOWN; + + public ExpandableBehavior() {} + + public ExpandableBehavior(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public abstract boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency); + + /** + * Reacts to a change in expanded state. This callback is guaranteed to be called only once even + * if {@link ExpandableWidget#setExpanded(boolean)} is called multiple times with the same value. + * Upon configuration change, this callback is called with {@code animated} set to false. + * + * @param dependency the {@link ExpandableWidget} dependency containing the new expanded state. + * @param child the view that should react to the change in expanded state. + * @param expanded the new expanded state. + * @param animated true if {@link ExpandableWidget#setExpanded(boolean)} was called, false if + * restoring from a configuration change. + * @return true if the Behavior changed the child view's size or position, false otherwise. + */ + protected abstract boolean onExpandedStateChange( + ExpandableWidget dependency, View child, boolean expanded, boolean animated); + + @CallSuper + @Override + public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) { + if (!ViewCompat.isLaidOut(child)) { + ExpandableWidget dep = findExpandableWidget(parent, child); + if (dep != null) { + currentState = dep.isExpanded() ? STATE_EXPANDED : STATE_COLLAPSED; + onExpandedStateChange(dep, child, dep.isExpanded(), false); + } + } + + return false; + } + + @CallSuper + @Override + public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { + ExpandableWidget dep = (ExpandableWidget) dependency; + boolean expanded = dep.isExpanded(); + if (didStateChange(expanded)) { + currentState = dep.isExpanded() ? STATE_EXPANDED : STATE_COLLAPSED; + return onExpandedStateChange(dep, child, dep.isExpanded(), true); + } + + return false; + } + + @Nullable + protected ExpandableWidget findExpandableWidget(CoordinatorLayout parent, View child) { + List dependencies = parent.getDependencies(child); + for (int i = 0, size = dependencies.size(); i < size; i++) { + View dependency = dependencies.get(i); + if (layoutDependsOn(parent, child, dependency)) { + return (ExpandableWidget) dependency; + } + } + return null; + } + + private boolean didStateChange(boolean expanded) { + return (expanded && ((currentState == STATE_UNKNOWN) || (currentState == STATE_COLLAPSED))) + || (!expanded && ((currentState == STATE_UNKNOWN) || (currentState == STATE_EXPANDED))); + } + + /** + * A utility function to get the {@link ExpandableBehavior} attached to the {@code view}. + * + * @param view The {@link View} that the {@link ExpandableBehavior} is attached to. + * @param klass The expected {@link Class} of the attached {@link ExpandableBehavior}. + * @return The {@link ExpandableBehavior} attached to the {@code view}. + */ + public static T from(View view, Class klass) { + ViewGroup.LayoutParams params = view.getLayoutParams(); + if (!(params instanceof CoordinatorLayout.LayoutParams)) { + throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); + } + CoordinatorLayout.Behavior behavior = + ((CoordinatorLayout.LayoutParams) params).getBehavior(); + if (!(behavior instanceof ExpandableBehavior)) { + throw new IllegalArgumentException("The view is not associated with ExpandableBehavior"); + } + return klass.cast(behavior); + } +} diff --git a/lib/src/android/support/design/widget/transformation/ExpandableTransformationBehavior.java b/lib/src/android/support/design/widget/transformation/ExpandableTransformationBehavior.java index 7ee6357a3..e031fa228 100644 --- a/lib/src/android/support/design/widget/transformation/ExpandableTransformationBehavior.java +++ b/lib/src/android/support/design/widget/transformation/ExpandableTransformationBehavior.java @@ -19,44 +19,22 @@ package android.support.design.widget.transformation; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; -import android.support.annotation.IntDef; +import android.support.annotation.CallSuper; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.design.widget.CoordinatorLayout; -import android.support.design.widget.CoordinatorLayout.Behavior; import android.support.design.widget.expandable.ExpandableWidget; -import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; -import android.view.ViewGroup; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.List; /** - * Base Behavior for views that a {@link ExpandableWidget} can transform into. Reacts to {@link - * ExpandableWidget#setExpanded(boolean)} state changes with an animation. + * Base Behavior for views that can react to an {@link ExpandableWidget}'s {@link + * ExpandableWidget#setExpanded(boolean)} state changes by transforming the ExpandableWidget into + * itself. + * + *

Behaviors should override {@link #onCreateExpandedStateChangeAnimation(ExpandableWidget, View, + * boolean)} to return an animation */ -public abstract class ExpandableTransformationBehavior extends Behavior { - - /** Unknown expanded state. */ - private static final int STATE_UNKNOWN = 0; - /** Expanded state. */ - private static final int STATE_EXPANDED = 1; - /** Collapsed state. */ - private static final int STATE_COLLAPSED = 2; - - @IntDef({STATE_UNKNOWN, STATE_EXPANDED, STATE_COLLAPSED}) - @Retention(RetentionPolicy.SOURCE) - private @interface State {} - - /** - * The current expanded state of this behavior. This state follows the expanded state of the - * {@link ExpandableWidget} dependency, and is updated in {@link #onLayoutChild(CoordinatorLayout, - * View, int)} and {@link #onDependentViewChanged(CoordinatorLayout, View, View)}. - * - *

This state may be {@link #STATE_UNKNOWN} before either of those callbacks have been invoked. - */ - @State private int currentState = STATE_UNKNOWN; +public abstract class ExpandableTransformationBehavior extends ExpandableBehavior { @Nullable private Animator currentAnimation; @@ -66,109 +44,46 @@ public abstract class ExpandableTransformationBehavior extends Behavior { super(context, attrs); } + /** + * Creates an Animator to be played for this expanded state change. + * + *

If the new {@code expanded} state is true, the {@code dependency} should be hidden and the + * {@code child} should be shown. + * + *

If the new {@code expanded} state is false, the {@code dependency} should be shown and the + * {@code child} should be hidden. + * + * @param dependency the {@link ExpandableWidget} dependency containing the new expanded state. + * @param child the view that should react to the change in expanded state. + * @param expanded the new expanded state. + */ + @NonNull + protected abstract Animator onCreateExpandedStateChangeAnimation( + ExpandableWidget dependency, View child, boolean expanded); + + @CallSuper @Override - public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { - return dependency instanceof ExpandableWidget; - } - - @Override - public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) { - if (!ViewCompat.isLaidOut(child)) { - List dependencies = parent.getDependencies(child); - ExpandableWidget dependency = findExpandableWidget(dependencies); - jumpToState(dependency, child); - } - - return false; - } - - @Override - public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { - ExpandableWidget dep = (ExpandableWidget) dependency; - boolean expanded = dep.isExpanded(); - if (!didStateChange(expanded)) { - return false; - } - + protected boolean onExpandedStateChange( + ExpandableWidget dependency, View child, boolean expanded, boolean animated) { if (currentAnimation != null) { currentAnimation.cancel(); } - currentState = expanded ? STATE_EXPANDED : STATE_COLLAPSED; - currentAnimation = createAnimation(dep, child); + currentAnimation = onCreateExpandedStateChangeAnimation(dependency, child, expanded); currentAnimation.addListener( new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - if (expanded) { - child.setVisibility(View.VISIBLE); - } - } - @Override public void onAnimationEnd(Animator animation) { - if (!expanded) { - child.setVisibility(View.INVISIBLE); - } currentAnimation = null; } }); + currentAnimation.start(); + if (!animated) { + // Synchronously end the animation, jumping to the end state. + currentAnimation.end(); + } + return true; } - - protected void jumpToState(ExpandableWidget dep, View child) { - boolean expanded = dep.isExpanded(); - if (!didStateChange(expanded)) { - return; - } - - currentState = expanded ? STATE_EXPANDED : STATE_COLLAPSED; - - if (expanded) { - child.setVisibility(View.VISIBLE); - } else { - child.setVisibility(View.INVISIBLE); - } - } - - protected abstract Animator createAnimation(ExpandableWidget dep, View child); - - private ExpandableWidget findExpandableWidget(List dependencies) { - for (int i = 0, size = dependencies.size(); i < size; i++) { - View dependency = dependencies.get(i); - if (dependency instanceof ExpandableWidget) { - return (ExpandableWidget) dependency; - } - } - return null; - } - - private boolean didStateChange(boolean expanded) { - return (expanded && ((currentState == STATE_UNKNOWN) || (currentState == STATE_COLLAPSED))) - || (!expanded && ((currentState == STATE_UNKNOWN) || (currentState == STATE_EXPANDED))); - } - - /** - * A utility function to get the {@link ExpandableTransformationBehavior} attached to the {@code - * view}. - * - * @param view The {@link View} that the {@link ExpandableTransformationBehavior} is attached to. - * @param klass The expected {@link Class} of the attached {@link - * ExpandableTransformationBehavior}. - * @return The {@link ExpandableTransformationBehavior} attached to the {@code view}. - */ - public static T from(View view, Class klass) { - ViewGroup.LayoutParams params = view.getLayoutParams(); - if (!(params instanceof CoordinatorLayout.LayoutParams)) { - throw new IllegalArgumentException("The view is not a child of CoordinatorLayout"); - } - CoordinatorLayout.Behavior behavior = - ((CoordinatorLayout.LayoutParams) params).getBehavior(); - if (!(behavior instanceof ExpandableTransformationBehavior)) { - throw new IllegalArgumentException( - "The view is not associated with ExpandableTransformationBehavior"); - } - return klass.cast(view); - } } diff --git a/lib/src/android/support/design/widget/transformation/FabTransformationScrimBehavior.java b/lib/src/android/support/design/widget/transformation/FabTransformationScrimBehavior.java index 044e51099..12641f8c0 100644 --- a/lib/src/android/support/design/widget/transformation/FabTransformationScrimBehavior.java +++ b/lib/src/android/support/design/widget/transformation/FabTransformationScrimBehavior.java @@ -17,9 +17,11 @@ package android.support.design.widget.transformation; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.support.annotation.NonNull; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.expandable.ExpandableWidget; @@ -30,8 +32,8 @@ import java.util.ArrayList; import java.util.List; /** - * Behavior that should be attached to a scrim that should appear when a {@link ExpandableWidget} is - * {@link ExpandableWidget#setExpanded(boolean)} expanded}. + * Behavior that should be attached to a scrim that should appear when a {@link + * FloatingActionButton} is {@link FloatingActionButton#setExpanded(boolean)} expanded}. */ public class FabTransformationScrimBehavior extends ExpandableTransformationBehavior { @@ -44,8 +46,7 @@ public class FabTransformationScrimBehavior extends ExpandableTransformationBeha @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { - return super.layoutDependsOn(parent, child, dependency) - && dependency instanceof FloatingActionButton; + return dependency instanceof FloatingActionButton; } @Override @@ -54,17 +55,35 @@ public class FabTransformationScrimBehavior extends ExpandableTransformationBeha return super.onTouchEvent(parent, child, ev); } + @NonNull @Override - protected Animator createAnimation(ExpandableWidget dep, View child) { + protected Animator onCreateExpandedStateChangeAnimation( + ExpandableWidget dep, View child, boolean expanded) { List animations = new ArrayList<>(); - if (dep.isExpanded()) { + if (expanded) { animations.add(ObjectAnimator.ofFloat(child, View.ALPHA, 0f, 1f)); } else { animations.add(ObjectAnimator.ofFloat(child, View.ALPHA, 1f, 0f)); } AnimatorSet set = new AnimatorSet(); + set.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + if (expanded) { + child.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!expanded) { + child.setVisibility(View.INVISIBLE); + } + } + }); set.playTogether(animations); return set; } diff --git a/lib/src/android/support/design/widget/transformation/FabTransformationSheetBehavior.java b/lib/src/android/support/design/widget/transformation/FabTransformationSheetBehavior.java index e5f826fca..55dcca20b 100644 --- a/lib/src/android/support/design/widget/transformation/FabTransformationSheetBehavior.java +++ b/lib/src/android/support/design/widget/transformation/FabTransformationSheetBehavior.java @@ -32,8 +32,8 @@ import java.util.ArrayList; import java.util.List; /** - * Behavior that should be attached to any sheet that should appear when a {@link ExpandableWidget} - * is {@link ExpandableWidget#setExpanded(boolean)} expanded}. + * Behavior that should be attached to any sheet that should appear when a {@link + * FloatingActionButton} is {@link FloatingActionButton#setExpanded(boolean)} expanded}. * *

A sheet usually has some width and height that's smaller than the screen, has an elevation, * and may have a scrim underneath. @@ -49,8 +49,7 @@ public class FabTransformationSheetBehavior extends ExpandableTransformationBeha @Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { - return super.layoutDependsOn(parent, child, dependency) - && dependency instanceof FloatingActionButton; + return dependency instanceof FloatingActionButton; } @Override @@ -62,32 +61,18 @@ public class FabTransformationSheetBehavior extends ExpandableTransformationBeha } } + @NonNull @Override - protected void jumpToState(ExpandableWidget dep, View child) { - super.jumpToState(dep, child); - - View dependency = (View) dep; - if (dep.isExpanded()) { - // A bug exists in 4.4.4 where setVisibility() did not invalidate the view. - dependency.setAlpha(0f); - dependency.setVisibility(View.INVISIBLE); - } else { - // A bug exists in 4.4.4 where setVisibility() did not invalidate the view. - dependency.setAlpha(1f); - dependency.setVisibility(View.VISIBLE); - } - } - - @Override - protected Animator createAnimation(ExpandableWidget dep, View child) { + protected Animator onCreateExpandedStateChangeAnimation( + ExpandableWidget dependency, View child, boolean expanded) { List animations = new ArrayList<>(); - View dependency = (View) dep; - if (dep.isExpanded()) { - animations.add(ObjectAnimator.ofFloat(dependency, View.ALPHA, 1f, 0f)); + View dep = (View) dependency; + if (expanded) { + animations.add(ObjectAnimator.ofFloat(dep, View.ALPHA, 1f, 0f)); animations.add(ObjectAnimator.ofFloat(child, View.ALPHA, 0f, 1f)); } else { - animations.add(ObjectAnimator.ofFloat(dependency, View.ALPHA, 0f, 1f)); + animations.add(ObjectAnimator.ofFloat(dep, View.ALPHA, 0f, 1f)); animations.add(ObjectAnimator.ofFloat(child, View.ALPHA, 1f, 0f)); } @@ -97,19 +82,23 @@ public class FabTransformationSheetBehavior extends ExpandableTransformationBeha new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - if (!dep.isExpanded()) { + if (expanded) { + child.setVisibility(View.VISIBLE); + } else { // A bug exists in 4.4.4 where setVisibility() did not invalidate the view. - dependency.setAlpha(1f); - dependency.setVisibility(View.VISIBLE); + dep.setAlpha(1f); + dep.setVisibility(View.VISIBLE); } } @Override public void onAnimationEnd(Animator animation) { - if (dep.isExpanded()) { + if (expanded) { // A bug exists in 4.4.4 where setVisibility() did not invalidate the view. - dependency.setAlpha(0f); - dependency.setVisibility(View.INVISIBLE); + dep.setAlpha(0f); + dep.setVisibility(View.INVISIBLE); + } else { + child.setVisibility(View.INVISIBLE); } } });