Merge \"Fix FloatingActionButton hide() and show()\" into nyc-support-24.1-dev

am: dfc69f69f1

Change-Id: I1d26458467f4fcd154c179c3ce5e2810d0ae69e5

GitOrigin-RevId=49886f03824b46ae35cd649fddc85d26d1da7372

PiperOrigin-RevId: 140564038
This commit is contained in:
Chris Banes 2016-11-30 05:03:05 +00:00 committed by Travis Collins
parent 554c03b354
commit 2ca8076b0a
3 changed files with 158 additions and 52 deletions

View File

@ -25,7 +25,11 @@ import android.view.View;
class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
private boolean mIsHiding;
private static final int ANIM_STATE_NONE = 0;
private static final int ANIM_STATE_HIDING = 1;
private static final int ANIM_STATE_SHOWING = 2;
private int mAnimState = ANIM_STATE_NONE;
private float mRotation;
FloatingActionButtonIcs(VisibilityAwareImageButton view,
@ -50,22 +54,16 @@ class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
@Override
void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
if (mIsHiding || mView.getVisibility() != View.VISIBLE) {
// A hide animation is in progress, or we're already hidden. Skip the call
if (listener != null) {
listener.onHidden();
}
if (isOrWillBeHidden()) {
// We either are or will soon be hidden, skip the call
return;
}
if (!ViewCompat.isLaidOut(mView) || mView.isInEditMode()) {
// If the view isn't laid out, or we're in the editor, don't run the animation
mView.internalSetVisibility(View.GONE, fromUser);
if (listener != null) {
listener.onHidden();
}
} else {
mView.animate().cancel();
mView.animate().cancel();
if (shouldAnimateVisibilityChange()) {
mAnimState = ANIM_STATE_HIDING;
mView.animate()
.scaleX(0f)
.scaleY(0f)
@ -77,20 +75,19 @@ class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
@Override
public void onAnimationStart(Animator animation) {
mIsHiding = true;
mCancelled = false;
mView.internalSetVisibility(View.VISIBLE, fromUser);
mCancelled = false;
}
@Override
public void onAnimationCancel(Animator animation) {
mIsHiding = false;
mCancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
mIsHiding = false;
mAnimState = ANIM_STATE_NONE;
if (!mCancelled) {
mView.internalSetVisibility(View.GONE, fromUser);
if (listener != null) {
@ -99,51 +96,89 @@ class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
}
}
});
} else {
// If the view isn't laid out, or we're in the editor, don't run the animation
mView.internalSetVisibility(View.GONE, fromUser);
if (listener != null) {
listener.onHidden();
}
}
}
@Override
void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
if (mIsHiding || mView.getVisibility() != View.VISIBLE) {
if (ViewCompat.isLaidOut(mView) && !mView.isInEditMode()) {
mView.animate().cancel();
if (mView.getVisibility() != View.VISIBLE) {
// If the view isn't visible currently, we'll animate it from a single pixel
mView.setAlpha(0f);
mView.setScaleY(0f);
mView.setScaleX(0f);
}
mView.animate()
.scaleX(1f)
.scaleY(1f)
.alpha(1f)
.setDuration(SHOW_HIDE_ANIM_DURATION)
.setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mView.internalSetVisibility(View.VISIBLE, fromUser);
}
if (isOrWillBeShown()) {
// We either are or will soon be visible, skip the call
return;
}
@Override
public void onAnimationEnd(Animator animation) {
if (listener != null) {
listener.onShown();
}
mView.animate().cancel();
if (shouldAnimateVisibilityChange()) {
mAnimState = ANIM_STATE_SHOWING;
if (mView.getVisibility() != View.VISIBLE) {
// If the view isn't visible currently, we'll animate it from a single pixel
mView.setAlpha(0f);
mView.setScaleY(0f);
mView.setScaleX(0f);
}
mView.animate()
.scaleX(1f)
.scaleY(1f)
.alpha(1f)
.setDuration(SHOW_HIDE_ANIM_DURATION)
.setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mView.internalSetVisibility(View.VISIBLE, fromUser);
}
@Override
public void onAnimationEnd(Animator animation) {
mAnimState = ANIM_STATE_NONE;
if (listener != null) {
listener.onShown();
}
});
} else {
mView.internalSetVisibility(View.VISIBLE, fromUser);
mView.setAlpha(1f);
mView.setScaleY(1f);
mView.setScaleX(1f);
if (listener != null) {
listener.onShown();
}
}
});
} else {
mView.internalSetVisibility(View.VISIBLE, fromUser);
mView.setAlpha(1f);
mView.setScaleY(1f);
mView.setScaleX(1f);
if (listener != null) {
listener.onShown();
}
}
}
private boolean isOrWillBeShown() {
if (mView.getVisibility() != View.VISIBLE) {
// If we not currently visible, return true if we're animating to be shown
return mAnimState == ANIM_STATE_SHOWING;
} else {
// Otherwise if we're visible, return true if we're not animating to be hidden
return mAnimState != ANIM_STATE_HIDING;
}
}
private boolean isOrWillBeHidden() {
if (mView.getVisibility() == View.VISIBLE) {
// If we currently visible, return true if we're animating to be hidden
return mAnimState == ANIM_STATE_HIDING;
} else {
// Otherwise if we're not visible, return true if we're not animating to be shown
return mAnimState != ANIM_STATE_SHOWING;
}
}
private boolean shouldAnimateVisibilityChange() {
return ViewCompat.isLaidOut(mView) && !mView.isInEditMode();
}
private void updateFromViewRotation() {
if (Build.VERSION.SDK_INT == 19) {
// KitKat seems to have an issue with views which are rotated with angles which are

View File

@ -129,4 +129,54 @@ public class FloatingActionButtonActions {
};
}
public static ViewAction hideThenShow(final int animDuration) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(FloatingActionButton.class);
}
@Override
public String getDescription() {
return "Calls hide() then show()";
}
@Override
public void perform(UiController uiController, View view) {
uiController.loopMainThreadUntilIdle();
FloatingActionButton fab = (FloatingActionButton) view;
fab.hide();
fab.show();
uiController.loopMainThreadForAtLeast(animDuration + 100);
}
};
}
public static ViewAction showThenHide(final int animDuration) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(FloatingActionButton.class);
}
@Override
public String getDescription() {
return "Calls show() then hide()";
}
@Override
public void perform(UiController uiController, View view) {
uiController.loopMainThreadUntilIdle();
FloatingActionButton fab = (FloatingActionButton) view;
fab.show();
fab.hide();
uiController.loopMainThreadForAtLeast(animDuration + 50);
}
};
}
}

View File

@ -16,15 +16,19 @@
package android.support.design.widget;
import static android.support.design.testutils.FloatingActionButtonActions.hideThenShow;
import static android.support.design.testutils.FloatingActionButtonActions.setBackgroundTintColor;
import static android.support.design.testutils.FloatingActionButtonActions.setImageResource;
import static android.support.design.testutils.FloatingActionButtonActions.setLayoutGravity;
import static android.support.design.testutils.FloatingActionButtonActions.setSize;
import static android.support.design.testutils.FloatingActionButtonActions.showThenHide;
import static android.support.design.testutils.TestUtilsMatchers.withFabBackgroundFill;
import static android.support.design.testutils.TestUtilsMatchers.withFabContentAreaOnMargins;
import static android.support.design.testutils.TestUtilsMatchers.withFabContentHeight;
import static android.support.design.widget.DesignViewActions.setVisibility;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import android.graphics.Color;
@ -32,6 +36,7 @@ import android.support.design.test.R;
import android.support.design.testutils.TestUtils;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.Gravity;
import android.view.View;
import org.junit.Test;
@ -114,4 +119,20 @@ public class FloatingActionButtonTest
.check(matches(withFabContentAreaOnMargins(Gravity.RIGHT | Gravity.BOTTOM)));
}
@Test
public void testHideShow() {
onView(withId(R.id.fab_standard))
.perform(setVisibility(View.VISIBLE))
.perform(hideThenShow(FloatingActionButtonImpl.SHOW_HIDE_ANIM_DURATION))
.check(matches(isDisplayed()));
}
@Test
public void testShowHide() {
onView(withId(R.id.fab_standard))
.perform(setVisibility(View.GONE))
.perform(showThenHide(FloatingActionButtonImpl.SHOW_HIDE_ANIM_DURATION))
.check(matches(not(isDisplayed())));
}
}