/* * Copyright (C) 2019 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 com.google.android.material.floatingactionbutton; import com.google.android.material.R; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import androidx.annotation.AnimatorRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.button.MaterialButton; import com.google.android.material.shape.ShapeAppearanceModel; import androidx.core.util.Preconditions; import androidx.core.view.ViewCompat; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Property; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout.AttachedBehavior; import androidx.coordinatorlayout.widget.CoordinatorLayout.Behavior; import com.google.android.material.animation.AnimatorSetCompat; import com.google.android.material.animation.MotionSpec; import com.google.android.material.internal.DescendantOffsetUtils; import com.google.android.material.internal.ThemeEnforcement; import java.util.ArrayList; import java.util.List; /** * Extended floating action buttons are used for a special type of promoted action. They are * distinguished by an icon and a text floating above the UI and have special motion behaviors * related to morphing, launching, and the transferring anchor point. * *
Extended floating action buttons may have icon and text, but may also hold just an icon or * text. * *
As this class descends from {@link MaterialButton}, you can control the icon which is * displayed via {@link #setIcon(android.graphics.drawable.Drawable)}, and the text via {@link * #setText(CharSequence)}. * *
The background color of this view defaults to the your theme's {@code colorPrimary}. If you
* wish to change this at runtime then you can do so via {@link
* #setBackgroundTintList(android.content.res.ColorStateList)}.
*/
public class ExtendedFloatingActionButton extends MaterialButton implements AttachedBehavior {
private static final int DEF_STYLE_RES =
R.style.Widget_MaterialComponents_ExtendedFloatingActionButton_Icon;
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 final Rect shadowPadding = new Rect();
private int animState = ANIM_STATE_NONE;
@Nullable private Animator currentShowHideAnimator;
@Nullable private Animator currentCollapseExpandAnimator;
@Nullable private MotionSpec showMotionSpec;
@Nullable private MotionSpec hideMotionSpec;
@Nullable private MotionSpec extendMotionSpec;
@Nullable private MotionSpec shrinkMotionSpec;
@Nullable private MotionSpec defaultShowMotionSpec;
@Nullable private MotionSpec defaultHideMotionSpec;
@Nullable private MotionSpec defaultExtendMotionSpec;
@Nullable private MotionSpec defaultShrinkMotionSpec;
private final Behavior Components that add a listener should take care to remove it when finished via {@link
* #removeOnShowAnimationListener(AnimatorListener)}.
*
* @param listener listener to add
*/
public void addOnShowAnimationListener(@NonNull AnimatorListener listener) {
if (showListeners == null) {
showListeners = new ArrayList<>();
}
showListeners.add(listener);
}
/**
* Remove a listener that was previously added via {@link
* #addOnShowAnimationListener(AnimatorListener)}.
*
* @param listener listener to remove
*/
public void removeOnShowAnimationListener(@NonNull AnimatorListener listener) {
if (showListeners == null) {
// This can happen if this method is called before the first call to
// addOnShowAnimationListener.
return;
}
showListeners.remove(listener);
}
/**
* Add a listener that will be invoked when this ExtendedFloatingActionButton is hidden. See
* {@link AnimatorListener}.
*
* Components that add a listener should take care to remove it when finished via {@link
* #removeOnHideAnimationListener(AnimatorListener)}.
*
* @param listener listener to add
*/
public void addOnHideAnimationListener(@NonNull AnimatorListener listener) {
if (hideListeners == null) {
hideListeners = new ArrayList<>();
}
hideListeners.add(listener);
}
/**
* Remove a listener that was previously added via {@link
* #addOnHideAnimationListener(AnimatorListener)}.
*
* @param listener listener to remove
*/
public void removeOnHideAnimationListener(@NonNull AnimatorListener listener) {
if (hideListeners == null) {
// This can happen if this method is called before the first call to
// addOnHideAnimationListener.
return;
}
hideListeners.remove(listener);
}
/**
* Add a listener that will be invoked when this ExtendedFloatingActionButton is shrunk. See
* {@link AnimatorListener}.
*
* Components that add a listener should take care to remove it when finished via {@link
* #removeOnShrinkAnimationListener(AnimatorListener)}.
*
* @param listener listener to add
*/
public void addOnShrinkAnimationListener(@NonNull AnimatorListener listener) {
if (shrinkListeners == null) {
shrinkListeners = new ArrayList<>();
}
shrinkListeners.add(listener);
}
/**
* Remove a listener that was previously added via {@link
* #addOnShrinkAnimationListener(AnimatorListener)}.
*
* @param listener listener to remove
*/
public void removeOnShrinkAnimationListener(@NonNull AnimatorListener listener) {
if (shrinkListeners == null) {
// This can happen if this method is called before the first call to
// addOnShrinkAnimationListener.
return;
}
shrinkListeners.remove(listener);
}
/**
* Add a listener that will be invoked when this ExtendedFloatingActionButton is extended. See
* {@link AnimatorListener}.
*
* Components that add a listener should take care to remove it when finished via {@link
* #removeOnExtendAnimationListener(AnimatorListener)}.
*
* @param listener listener to add
*/
public void addOnExtendAnimationListener(@NonNull AnimatorListener listener) {
if (extendListeners == null) {
extendListeners = new ArrayList<>();
}
extendListeners.add(listener);
}
/**
* Remove a listener that was previously added via {@link
* #addOnExtendAnimationListener(AnimatorListener)}.
*
* @param listener listener to remove
*/
public void removeOnExtendAnimationListener(@NonNull AnimatorListener listener) {
if (extendListeners == null) {
// This can happen if this method is called before the first call to
// addOnExtendAnimationListener.
return;
}
extendListeners.remove(listener);
}
/**
* Hides the button.
*
* This method will animate the button hide if the view has already been laid out.
*/
public void hide() {
hide(true /* animate */);
}
/**
* Hides the button.
*
* @param animate whether or not the button's hiding is animated
*/
public void hide(boolean animate) {
hide(true /* fromUser */, animate, null /* listener */);
}
/**
* Hides the button.
*
* This method will animate the button hide if the view has already been laid out.
*
* @param listener the listener to notify when this view is hidden
*/
public void hide(@Nullable OnChangedListener listener) {
hide(true /* fromUser */, true /* animate */, listener);
}
private void hide(
final boolean fromUser, boolean animate, @Nullable final OnChangedListener listener) {
if (isOrWillBeHidden()) {
// We either are or will soon be hidden, skip the call
return;
}
if (currentShowHideAnimator != null) {
currentShowHideAnimator.cancel();
}
if (animate && shouldAnimateVisibilityChange()) {
Animator hideAnimation = createAnimator(getCurrentHideMotionSpec());
hideAnimation.addListener(
new AnimatorListenerAdapter() {
private boolean cancelled;
@Override
public void onAnimationStart(Animator animation) {
internalSetVisibility(View.VISIBLE, fromUser);
animState = ANIM_STATE_HIDING;
currentShowHideAnimator = animation;
cancelled = false;
}
@Override
public void onAnimationCancel(Animator animation) {
cancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
animState = ANIM_STATE_NONE;
currentShowHideAnimator = null;
if (!cancelled) {
internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
if (listener != null) {
listener.onHidden(ExtendedFloatingActionButton.this);
}
}
}
});
if (hideListeners != null) {
for (AnimatorListener l : hideListeners) {
hideAnimation.addListener(l);
}
}
hideAnimation.start();
} else {
// If the view isn't laid out, or we're in the editor, don't run the animation
internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
if (listener != null) {
listener.onHidden(this);
}
}
}
/**
* Shows the button.
*
* This method will animate the button show if the view has already been laid out.
*/
public void show() {
show(true /* animate */);
}
/**
* Shows the button.
*
* @param animate whether or not the button's showing is animated
*/
public void show(boolean animate) {
show(true /* fromUser */, animate, null /* listener */);
}
/**
* Shows the button.
*
* This method will animate the button show if the view has already been laid out.
*
* @param listener the listener to notify when this view is shown
*/
public void show(@Nullable OnChangedListener listener) {
show(true /* fromUser */, true /* animate */, listener);
}
private void show(
final boolean fromUser, boolean animate, @Nullable final OnChangedListener listener) {
if (isOrWillBeShown()) {
// We either are or will soon be visible, skip the call
return;
}
if (currentShowHideAnimator != null) {
currentShowHideAnimator.cancel();
}
if (animate && shouldAnimateVisibilityChange()) {
Animator showAnimation = createAnimator(getCurrentShowMotionSpec());
showAnimation.addListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
internalSetVisibility(View.VISIBLE, fromUser);
animState = ANIM_STATE_SHOWING;
currentShowHideAnimator = animation;
}
@Override
public void onAnimationEnd(Animator animation) {
animState = ANIM_STATE_NONE;
currentShowHideAnimator = null;
if (listener != null) {
listener.onShown(ExtendedFloatingActionButton.this);
}
}
});
if (showListeners != null) {
for (AnimatorListener l : showListeners) {
showAnimation.addListener(l);
}
}
showAnimation.start();
} else {
internalSetVisibility(View.VISIBLE, fromUser);
setAlpha(1f);
setScaleY(1f);
setScaleX(1f);
if (listener != null) {
listener.onShown(this);
}
}
}
/**
* Extends the FAB to show the text and the icon.
*
* This method will not affect an extended FAB which holds just text and no icon. Also, this
* method will animate the button show if the view has already been laid out.
*
* @see #extend(boolean)
*/
public void extend() {
extend(true /* animate */);
}
/**
* Extends the FAB to show the text and the icon.
*
* This method will not affect an extended FAB which holds just text and no icon.
*
* @param animate whether or not the extending is animated
*/
public void extend(boolean animate) {
setExtended(true /* extended */, animate, null /* listener */);
}
/**
* Extends the FAB to show the text and the icon.
*
* This method will not affect an extended FAB which holds just text and no icon. Also, this
* method will animate the button show if the view has already been laid out.
*
* @param listener the listener to notify when the FAB is extended
*/
public void extend(@Nullable final OnChangedListener listener) {
setExtended(true /* extended */, true /* animate */, listener);
}
/**
* Shrinks the FAB to show just the icon.
*
* This method will not affect an extended FAB which holds just text and no icon. Also, this
* method will animate the button show if the view has already been laid out.
*
* @see #shrink(boolean)
*/
public void shrink() {
shrink(true /* animate */);
}
/**
* Shrinks the FAB to show just the icon.
*
* This method will not affect an extended FAB which holds just text and no icon.
*
* @param animate whether or not the shrinking is animated
*/
public void shrink(boolean animate) {
setExtended(false /* extended */, animate, null /* listener */);
}
/**
* Shrinks the FAB to show just the icon.
*
* This method will not affect an extended FAB which holds just text and no icon. Also, this
* method will animate the button show if the view has already been laid out.
*
* @param listener the listener to notify when the FAB shrank
*/
public void shrink(@Nullable final OnChangedListener listener) {
setExtended(false /* extended */, true /* animate */, listener);
}
/** Returns the motion spec for the show animation. */
@Nullable
public MotionSpec getShowMotionSpec() {
return showMotionSpec;
}
/**
* Updates the motion spec for the show animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_showMotionSpec
*/
public void setShowMotionSpec(@Nullable MotionSpec spec) {
showMotionSpec = spec;
}
/**
* Updates the motion spec for the show animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_showMotionSpec
*/
public void setShowMotionSpecResource(@AnimatorRes int id) {
setShowMotionSpec(MotionSpec.createFromResource(getContext(), id));
}
/** Returns the motion spec for the hide animation. */
@Nullable
public MotionSpec getHideMotionSpec() {
return hideMotionSpec;
}
/**
* Updates the motion spec for the hide animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_hideMotionSpec
*/
public void setHideMotionSpec(@Nullable MotionSpec spec) {
hideMotionSpec = spec;
}
/**
* Updates the motion spec for the hide animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_hideMotionSpec
*/
public void setHideMotionSpecResource(@AnimatorRes int id) {
setHideMotionSpec(MotionSpec.createFromResource(getContext(), id));
}
/** Returns the motion spec for the extend animation. */
@Nullable
public MotionSpec getExtendMotionSpec() {
return extendMotionSpec;
}
/**
* Updates the motion spec for the extend animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_extendMotionSpec
*/
public void setExtendMotionSpec(@Nullable MotionSpec spec) {
extendMotionSpec = spec;
}
/**
* Updates the motion spec for the extend animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_extendMotionSpec
*/
public void setExtendMotionSpecResource(@AnimatorRes int id) {
setExtendMotionSpec(MotionSpec.createFromResource(getContext(), id));
}
/** Returns the motion spec for the shrink animation. */
@Nullable
public MotionSpec getShrinkMotionSpec() {
return shrinkMotionSpec;
}
/**
* Updates the motion spec for the shrink animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_shrinkMotionSpec
*/
public void setShrinkMotionSpec(@Nullable MotionSpec spec) {
shrinkMotionSpec = spec;
}
/**
* Updates the motion spec for the shrink animation.
*
* @attr ref com.google.android.material.R.styleable#ExtendedFloatingActionButton_shrinkMotionSpec
*/
public void setShrinkMotionSpecResource(@AnimatorRes int id) {
setShrinkMotionSpec(MotionSpec.createFromResource(getContext(), id));
}
/**
* Sets the extended state of this FAB. When {@code true}, the FAB will show the icon and the
* text, and when {@code false}, it will show just the icon.
*
* Note that this call will not affect an extended FAB that holds just text, or just an icon.
*
* @param extended the new extended state of the button
* @param animate whether or not the extending or shrinking is animated
* @param listener an {@link OnChangedListener} that will be notified with {@link
* OnChangedListener#onShrunken(ExtendedFloatingActionButton)} and {@link
* OnChangedListener#onExtended(ExtendedFloatingActionButton)} when the animation ends
*/
private void setExtended(
final boolean extended, boolean animate, @Nullable final OnChangedListener listener) {
if (extended == this.isExtended || getIcon() == null || TextUtils.isEmpty(getText())) {
return;
}
this.isExtended = extended;
if (currentCollapseExpandAnimator != null) {
currentCollapseExpandAnimator.cancel();
}
if (animate && shouldAnimateVisibilityChange()) {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
Animator collapseExpandAnimator =
createShrinkExtendAnimator(
isExtended ? getCurrentExtendMotionSpec() : getCurrentShrinkMotionSpec(),
!isExtended);
collapseExpandAnimator.addListener(
new AnimatorListenerAdapter() {
private boolean cancelled;
@Override
public void onAnimationStart(Animator animation) {
// Eliminates the word wrapping when the FAB is being extended or shrunk.
setHorizontallyScrolling(true);
currentCollapseExpandAnimator = animation;
cancelled = false;
}
@Override
public void onAnimationCancel(Animator animation) {
cancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
setHorizontallyScrolling(false);
currentCollapseExpandAnimator = null;
if (cancelled || listener == null) {
return;
}
if (extended) {
listener.onExtended(ExtendedFloatingActionButton.this);
} else {
listener.onShrunken(ExtendedFloatingActionButton.this);
}
}
});
ArrayList TODO(b/121352029): Remove this method once this bug is fixed.
*/
private int getAdjustedRadius(int value) {
return (value - 1) / 2;
}
/**
* Shrink to the smaller value between paddingStart and paddingEnd, such that when shrunk the icon
* will be centered.
*/
private int getCollapsedSize() {
return Math.min(ViewCompat.getPaddingStart(this), ViewCompat.getPaddingEnd(this)) * 2
+ getIconSize();
}
/**
* Behavior designed for use with {@link ExtendedFloatingActionButton} instances. Its main
* function is to move {@link ExtendedFloatingActionButton} views so that any displayed {@link
* com.google.android.material.snackbar.Snackbar}s do not cover them.
*/
protected static class ExtendedFloatingActionButtonBehavior<
T extends ExtendedFloatingActionButton>
extends CoordinatorLayout.Behavior In case auto-shrink is enabled, it will take precedence over the auto-hide option.
*
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoHide
* @param autoHide true to enable automatic hiding
*/
public void setAutoHideEnabled(boolean autoHide) {
autoHideEnabled = autoHide;
}
/**
* Returns whether the associated ExtendedFloatingActionButton automatically hides when there is
* not enough space to be displayed.
*
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoHide
* @return true if enabled
*/
public boolean isAutoHideEnabled() {
return autoHideEnabled;
}
/**
* Sets whether the associated ExtendedFloatingActionButton automatically shrink when there is
* not enough space to be displayed. This works with {@link AppBarLayout} and {@link
* BottomSheetBehavior}.
*
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoShrink
* @param autoShrink true to enable automatic shrinking
*/
public void setAutoShrinkEnabled(boolean autoShrink) {
autoShrinkEnabled = autoShrink;
}
/**
* Returns whether the associated ExtendedFloatingActionButton automatically shrinks when there
* is not enough space to be displayed.
*
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoShrink
* @return true if enabled
*/
public boolean isAutoShrinkEnabled() {
return autoShrinkEnabled;
}
@Override
public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams lp) {
if (lp.dodgeInsetEdges == Gravity.NO_GRAVITY) {
// If the developer hasn't set dodgeInsetEdges, lets set it to BOTTOM so that
// we dodge any Snackbars
lp.dodgeInsetEdges = Gravity.BOTTOM;
}
}
@Override
public boolean onDependentViewChanged(
CoordinatorLayout parent, ExtendedFloatingActionButton child, View dependency) {
if (dependency instanceof AppBarLayout) {
// If we're depending on an AppBarLayout we will show/hide it automatically
// if the FAB is anchored to the AppBarLayout
updateFabVisibilityForAppBarLayout(parent, (AppBarLayout) dependency, child);
} else if (isBottomSheet(dependency)) {
updateFabVisibilityForBottomSheet(dependency, child);
}
return false;
}
private static boolean isBottomSheet(@NonNull View view) {
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof CoordinatorLayout.LayoutParams) {
return ((CoordinatorLayout.LayoutParams) lp).getBehavior() instanceof BottomSheetBehavior;
}
return false;
}
@VisibleForTesting
public void setInternalAutoHideListener(@Nullable OnChangedListener listener) {
internalAutoHideListener = listener;
}
@VisibleForTesting
public void setInternalAutoShrinkListener(@Nullable OnChangedListener listener) {
internalAutoShrinkListener = listener;
}
private boolean shouldUpdateVisibility(View dependency, ExtendedFloatingActionButton child) {
final CoordinatorLayout.LayoutParams lp =
(CoordinatorLayout.LayoutParams) child.getLayoutParams();
if (!autoHideEnabled && !autoShrinkEnabled) {
return false;
}
if (lp.getAnchorId() != dependency.getId()) {
// The anchor ID doesn't match the dependency, so we won't automatically
// show/hide the FAB
return false;
}
//noinspection RedundantIfStatement
if (child.getUserSetVisibility() != VISIBLE) {
// The view isn't set to be visible so skip changing its visibility
return false;
}
return true;
}
private boolean updateFabVisibilityForAppBarLayout(
CoordinatorLayout parent, AppBarLayout appBarLayout, ExtendedFloatingActionButton child) {
if (!shouldUpdateVisibility(appBarLayout, child)) {
return false;
}
if (tmpRect == null) {
tmpRect = new Rect();
}
// First, let's get the visible rect of the dependency
final Rect rect = tmpRect;
DescendantOffsetUtils.getDescendantRect(parent, appBarLayout, rect);
if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
// If the anchor's bottom is below the seam, we'll animate our FAB out
shrinkOrHide(child);
} else {
// Else, we'll animate our FAB back in
extendOrShow(child);
}
return true;
}
private boolean updateFabVisibilityForBottomSheet(
View bottomSheet, ExtendedFloatingActionButton child) {
if (!shouldUpdateVisibility(bottomSheet, child)) {
return false;
}
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
if (bottomSheet.getTop() < child.getHeight() / 2 + lp.topMargin) {
shrinkOrHide(child);
} else {
extendOrShow(child);
}
return true;
}
/**
* Shrinks the Extended FAB, in case auto-shrink is enabled, or hides it in case auto-hide is
* enabled. The priority is given to the default shrink option, and the button will be hidden
* only when the auto-shrink is {@code false} and auto-hide is {@code true}.
*
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoShrink
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoHide
* @see #setAutoShrinkEnabled(boolean)
* @see #setAutoHideEnabled(boolean)
*/
protected void shrinkOrHide(@NonNull ExtendedFloatingActionButton fab) {
if (autoShrinkEnabled) {
fab.shrink(internalAutoShrinkListener);
} else if (autoHideEnabled) {
fab.hide(false /* fromUser */, true /* animate */, internalAutoHideListener);
}
}
/**
* Extends the Extended FAB, in case auto-shrink is enabled, or show it in case auto-hide is
* enabled. The priority is given to the default extend option, and the button will be shown
* only when the auto-shrink is {@code false} and auto-hide is {@code true}.
*
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoShrink
* @attr ref
* com.google.android.material.R.styleable#ExtendedFloatingActionButton_Behavior_Layout_behavior_autoHide
* @see #setAutoShrinkEnabled(boolean)
* @see #setAutoHideEnabled(boolean)
*/
protected void extendOrShow(@NonNull ExtendedFloatingActionButton fab) {
if (autoShrinkEnabled) {
fab.extend(internalAutoShrinkListener);
} else if (autoHideEnabled) {
fab.show(false /* fromUser */, true /* animate */, internalAutoHideListener);
}
}
@Override
public boolean onLayoutChild(
CoordinatorLayout parent, ExtendedFloatingActionButton child, int layoutDirection) {
// First, let's make sure that the visibility of the FAB is consistent
final Listwidth functionality handled by the {@link
* LayoutParams#width} value.
*/
private static final Propertyheight functionality handled by the {@link
* LayoutParams#height} value.
*/
private static final PropertycornerRadius functionality handled by the {@link
* ExtendedFloatingActionButton#setCornerRadius(int)} and {@link
* ExtendedFloatingActionButton#getCornerRadius()} methods.
*/
private static final Property