Merge "Fix Snackbar sw600dp gravity" into lmp-mr1-ub-dev
diff --git a/design/api/current.txt b/design/api/current.txt
index 217ba99..9dc6e20 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -170,7 +170,9 @@
ctor public FloatingActionButton(android.content.Context);
ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+ method public void hide();
method public void setRippleColor(int);
+ method public void show();
}
public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
diff --git a/design/base/android/support/design/widget/FloatingActionButtonImpl.java b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
index 6c154d4..7fcb147 100644
--- a/design/base/android/support/design/widget/FloatingActionButtonImpl.java
+++ b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
@@ -26,6 +26,8 @@
abstract class FloatingActionButtonImpl {
+ static final int SHOW_HIDE_ANIM_DURATION = 200;
+
static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
android.R.attr.state_enabled};
static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
@@ -57,6 +59,10 @@
abstract void jumpDrawableToCurrentState();
+ abstract void hide();
+
+ abstract void show();
+
Drawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
final Resources resources = mView.getResources();
CircularBorderDrawable borderDrawable = newCircularDrawable();
diff --git a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
index 36ddd89..1aa0b683 100644
--- a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
+++ b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
@@ -23,6 +23,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
+import android.support.design.R;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.view.View;
import android.view.animation.Animation;
@@ -42,6 +43,8 @@
ShadowDrawableWrapper mShadowDrawable;
+ private boolean mIsHiding;
+
FloatingActionButtonEclairMr1(View view, ShadowViewDelegate shadowViewDelegate) {
super(view, shadowViewDelegate);
@@ -153,6 +156,41 @@
mStateListAnimator.jumpToCurrentState();
}
+ @Override
+ void hide() {
+ if (mIsHiding) {
+ // There is currently an hide animation running, return
+ return;
+ }
+
+ Animation anim = android.view.animation.AnimationUtils.loadAnimation(
+ mView.getContext(), R.anim.fab_out);
+ anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+ anim.setDuration(SHOW_HIDE_ANIM_DURATION);
+ anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ mIsHiding = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mIsHiding = false;
+ mView.setVisibility(View.GONE);
+ }
+ });
+ mView.startAnimation(anim);
+ }
+
+ @Override
+ void show() {
+ Animation anim = android.view.animation.AnimationUtils.loadAnimation(
+ mView.getContext(), R.anim.fab_in);
+ anim.setDuration(SHOW_HIDE_ANIM_DURATION);
+ anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+ mView.startAnimation(anim);
+ }
+
private void updatePadding() {
Rect rect = new Rect();
mShadowDrawable.getPadding(rect);
diff --git a/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java
new file mode 100644
index 0000000..62da58e
--- /dev/null
+++ b/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.view.View;
+
+class FloatingActionButtonHoneycombMr1 extends FloatingActionButtonEclairMr1 {
+
+ private boolean mIsHiding;
+
+ FloatingActionButtonHoneycombMr1(View view, ShadowViewDelegate shadowViewDelegate) {
+ super(view, shadowViewDelegate);
+ }
+
+ @Override
+ void hide() {
+ if (mIsHiding) {
+ // A hide animation is in progress, skip the call
+ return;
+ }
+
+ mView.animate()
+ .scaleX(0f)
+ .scaleY(0f)
+ .alpha(0f)
+ .setDuration(SHOW_HIDE_ANIM_DURATION)
+ .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsHiding = true;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mIsHiding = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsHiding = false;
+ mView.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ @Override
+ void show() {
+ mView.animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .alpha(1f)
+ .setDuration(SHOW_HIDE_ANIM_DURATION)
+ .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+ .setListener(null);
+ }
+}
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index c21d341..a440427 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -33,7 +33,7 @@
import android.view.animation.Interpolator;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-class FloatingActionButtonLollipop extends FloatingActionButtonImpl {
+class FloatingActionButtonLollipop extends FloatingActionButtonHoneycombMr1 {
private Drawable mShapeDrawable;
private RippleDrawable mRippleDrawable;
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index bd2ce10..13262a2 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -27,10 +27,8 @@
import android.support.annotation.Nullable;
import android.support.design.R;
import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
import android.util.AttributeSet;
import android.view.View;
-import android.view.animation.Animation;
import android.widget.ImageView;
import java.util.List;
@@ -119,8 +117,11 @@
}
};
- if (Build.VERSION.SDK_INT >= 21) {
+ final int sdk = Build.VERSION.SDK_INT;
+ if (sdk >= 21) {
mImpl = new FloatingActionButtonLollipop(this, delegate);
+ } else if (sdk >= 12) {
+ mImpl = new FloatingActionButtonHoneycombMr1(this, delegate);
} else {
mImpl = new FloatingActionButtonEclairMr1(this, delegate);
}
@@ -220,7 +221,36 @@
public void setBackgroundDrawable(Drawable background) {
if (mImpl != null) {
mImpl.setBackgroundDrawable(
- background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth);
+ background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth);
+ }
+ }
+
+ /**
+ * Shows the button.
+ * <p>This method will animate it the button show if the view has already been laid out.</p>
+ */
+ public void show() {
+ if (getVisibility() == VISIBLE) {
+ return;
+ }
+ setVisibility(VISIBLE);
+ if (ViewCompat.isLaidOut(this)) {
+ mImpl.show();
+ }
+ }
+
+ /**
+ * Hides the button.
+ * <p>This method will animate the button hide if the view has already been laid out.</p>
+ */
+ public void hide() {
+ if (getVisibility() != VISIBLE) {
+ return;
+ }
+ if (ViewCompat.isLaidOut(this)) {
+ mImpl.hide();
+ } else {
+ setVisibility(GONE);
}
}
@@ -299,13 +329,11 @@
private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
private Rect mTmpRect;
- private boolean mIsAnimatingOut;
private float mTranslationY;
@Override
public boolean layoutDependsOn(CoordinatorLayout parent,
- FloatingActionButton child,
- View dependency) {
+ FloatingActionButton child, View dependency) {
// We're dependent on all SnackbarLayouts (if enabled)
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
}
@@ -316,32 +344,47 @@
if (dependency instanceof Snackbar.SnackbarLayout) {
updateFabTranslationForSnackbar(parent, child, dependency);
} else if (dependency instanceof AppBarLayout) {
- final AppBarLayout appBarLayout = (AppBarLayout) dependency;
- if (mTmpRect == null) {
- mTmpRect = new Rect();
- }
-
- // First, let's get the visible rect of the dependency
- final Rect rect = mTmpRect;
- ViewGroupUtils.getDescendantRect(parent, dependency, rect);
-
- if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
- // If the anchor's bottom is below the seam, we'll animate our FAB out
- if (!mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {
- animateOut(child);
- }
- } else {
- // Else, we'll animate our FAB back in
- if (child.getVisibility() != View.VISIBLE) {
- animateIn(child);
- }
- }
+ // If we're depending on an AppBarLayout we will show/hide it automatically
+ // if the FAB is anchored to the AppBarLayout
+ updateFabVisibility(parent, (AppBarLayout) dependency, child);
}
return false;
}
+ private boolean updateFabVisibility(CoordinatorLayout parent,
+ AppBarLayout appBarLayout, FloatingActionButton child) {
+ final CoordinatorLayout.LayoutParams lp =
+ (CoordinatorLayout.LayoutParams) child.getLayoutParams();
+ if (lp.getAnchorId() != appBarLayout.getId()) {
+ // The anchor ID doesn't match the dependency, so we won't automatically
+ // show/hide the FAB
+ return false;
+ }
+
+ if (mTmpRect == null) {
+ mTmpRect = new Rect();
+ }
+
+ // First, let's get the visible rect of the dependency
+ final Rect rect = mTmpRect;
+ ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect);
+
+ if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
+ // If the anchor's bottom is below the seam, we'll animate our FAB out
+ child.hide();
+ } else {
+ // Else, we'll animate our FAB back in
+ child.show();
+ }
+ return true;
+ }
+
private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
FloatingActionButton fab, View snackbar) {
+ if (fab.getVisibility() != View.VISIBLE) {
+ return;
+ }
+
final float translationY = getFabTranslationYForSnackbar(parent, fab);
if (translationY != mTranslationY) {
// First, cancel any current animation
@@ -377,83 +420,25 @@
return minOffset;
}
- private void animateIn(FloatingActionButton button) {
- button.setVisibility(View.VISIBLE);
-
- if (Build.VERSION.SDK_INT >= 14) {
- ViewCompat.animate(button)
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f)
- .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
- .withLayer()
- .setListener(null)
- .start();
- } else {
- Animation anim = android.view.animation.AnimationUtils.loadAnimation(
- button.getContext(), R.anim.fab_in);
- anim.setDuration(200);
- anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
- button.startAnimation(anim);
- }
- }
-
@Override
public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
int layoutDirection) {
- // Let the CoordinatorLayout lay out the FAB
+ // First, lets make sure that the visibility of the FAB is consistent
+ final List<View> dependencies = parent.getDependencies(child);
+ for (int i = 0, count = dependencies.size(); i < count; i++) {
+ final View dependency = dependencies.get(i);
+ if (dependency instanceof AppBarLayout
+ && updateFabVisibility(parent, (AppBarLayout) dependency, child)) {
+ break;
+ }
+ }
+ // Now let the CoordinatorLayout lay out the FAB
parent.onLayoutChild(child, layoutDirection);
// Now offset it if needed
offsetIfNeeded(parent, child);
return true;
}
- private void animateOut(final FloatingActionButton button) {
- if (Build.VERSION.SDK_INT >= 14) {
- ViewCompat.animate(button)
- .scaleX(0f)
- .scaleY(0f)
- .alpha(0f)
- .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
- .withLayer()
- .setListener(new ViewPropertyAnimatorListener() {
- @Override
- public void onAnimationStart(View view) {
- mIsAnimatingOut = true;
- }
-
- @Override
- public void onAnimationCancel(View view) {
- mIsAnimatingOut = false;
- }
-
- @Override
- public void onAnimationEnd(View view) {
- mIsAnimatingOut = false;
- view.setVisibility(View.GONE);
- }
- }).start();
- } else {
- Animation anim = android.view.animation.AnimationUtils.loadAnimation(
- button.getContext(), R.anim.fab_out);
- anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
- anim.setDuration(200);
- anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
- @Override
- public void onAnimationStart(Animation animation) {
- mIsAnimatingOut = true;
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- mIsAnimatingOut = false;
- button.setVisibility(View.GONE);
- }
- });
- button.startAnimation(anim);
- }
- }
-
/**
* Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method
* offsets our layout position so that we're positioned correctly if we're on one of
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 3e2c841..5db9d05 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -571,7 +571,7 @@
<item name="isLightTheme">true</item>
</style>
- <style name="Base.ThemeOverlay.AppCompat.Dark">
+ <style name="Base.ThemeOverlay.AppCompat.Dark" parent="Platform.ThemeOverlay.AppCompat.Dark">
<item name="android:windowBackground">@color/background_material_dark</item>
<item name="android:colorForeground">@color/bright_foreground_material_dark</item>
<item name="android:colorForegroundInverse">@color/bright_foreground_material_light</item>