mirror of
https://github.com/material-components/material-components-android.git
synced 2026-02-20 08:39:55 +08:00
Roll forward separating ExpandableWidget/TransformationWidget and ExpandableBehavior/ExpandableTransformationBehavior.
Removes onSaveInstanceState() and onRestoreInstanceState() from the ExpandableWidget interface. Those methods required all descendent subclass Views to declare those methods to be public, which is not ideal given how many FAB subclasses there are. Those interface methods were mostly used for their javadoc, which is now moved into the interface javadoc. PiperOrigin-RevId: 163440160
This commit is contained in:
parent
5694bee92b
commit
659b5d16bc
@ -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
|
||||
* <p>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<String, Bundle> extendableStates;
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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}.
|
||||
*
|
||||
* <p>When the expanded state changes, an event is dispatched so that other widgets may react via a
|
||||
* {@link android.support.design.widget.CoordinatorLayout.Behavior}.
|
||||
* <p>Implementations of this interface should create an instance of {@link ExpandableWidgetHelper}
|
||||
* and forward all calls to it.
|
||||
*
|
||||
* <p>The expanded state can saved across configuration changes by implementing {@link
|
||||
* #onSaveInstanceState} and {@link #onRestoreInstanceState}:
|
||||
*
|
||||
* <pre><code>
|
||||
* {@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"));
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
public interface ExpandableWidget {
|
||||
|
||||
/** Returns whether this widget is expanded. */
|
||||
/**
|
||||
* Returns whether this widget is expanded.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
|
||||
@ -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 <T extends View & ExpandableWidget> 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) {
|
||||
|
||||
@ -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<View> {
|
||||
|
||||
/** 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)}.
|
||||
*
|
||||
* <p>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<View> 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 extends ExpandableBehavior> T from(View view, Class<T> 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);
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
*
|
||||
* <p>Behaviors should override {@link #onCreateExpandedStateChangeAnimation(ExpandableWidget, View,
|
||||
* boolean)} to return an animation
|
||||
*/
|
||||
public abstract class ExpandableTransformationBehavior extends Behavior<View> {
|
||||
|
||||
/** 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)}.
|
||||
*
|
||||
* <p>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<View> {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Animator to be played for this expanded state change.
|
||||
*
|
||||
* <p>If the new {@code expanded} state is true, the {@code dependency} should be hidden and the
|
||||
* {@code child} should be shown.
|
||||
*
|
||||
* <p>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<View> 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<View> 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 extends ExpandableTransformationBehavior> T from(View view, Class<T> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Animator> 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;
|
||||
}
|
||||
|
||||
@ -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}.
|
||||
*
|
||||
* <p>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<Animator> 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user