FloatingActionButton不会隐藏

50

我正在尝试通过以下方式以编程方式隐藏我的 FloatingActionButton fabLocation

fabLocation.setVisibility(View.GONE)

但它不起作用。

如果我在我的XML布局中添加 android:visibility="gone",当我运行活动时,fabLocation 被隐藏了,但当我滚动时,它又出现了。

这是我的布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fitsSystemWindows="true"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        app:contentScrim="@color/colorOverlay"
        app:expandedTitleMarginEnd="64dp"
        app:expandedTitleMarginStart="48dp"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

        <ImageView
            android:id="@+id/img_couverture"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            android:scaleType="centerCrop"
            android:src="@drawable/bg_navigation_drawer_header"
            app:layout_collapseMode="parallax" />

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_collapseMode="pin"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    </android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.v7.widget.CardView
            android:layout_marginTop="8dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="16dp">

                <TextView
                    android:id="@+id/tv_name"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textSize="18sp" />

                <View
                    android:background="@drawable/separator_orange_gradient"
                    android:layout_marginTop="8dp"
                    android:layout_marginBottom="16dp"
                    android:layout_width="match_parent"
                    android:layout_height="2dp"/>

                <TextView
                    android:id="@+id/tv_history"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textAppearance="@style/TextAppearance.RobotoLight" />

            </LinearLayout>

        </android.support.v7.widget.CardView>

    </LinearLayout>

</android.support.v4.widget.NestedScrollView>

<android.support.design.widget.FloatingActionButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    android:clickable="true"
    android:id="@+id/fab_location"
    android:src="@drawable/ic_fab_location_24dp"
    app:backgroundTint="@color/colorOrange"
    app:layout_anchor="@id/appbar"
    app:layout_anchorGravity="bottom|right|end" />

17个回答

46

这是由于app:layout_anchor属性造成的。在更改可见性之前,您必须先去除该锚点:

CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
p.setAnchorId(View.NO_ID);
fab.setLayoutParams(p);
fab.setVisibility(View.GONE);

10
如果我们使用fab.hide()而不是setVisibility(View.GONE),我们该如何做呢?因为现在你的示例会在消失之前在屏幕左上角显示fab动画(变小),这是不希望出现的行为,理想情况下,你希望它立即隐藏。 - Linxy
@Linxy 有相同的问题。 - Konstantin Konopko
对于像我这样遇到动画问题的人,解决方案是将fab.width和fab.height设置为零,如此问题在这个问题中提到。http://stackoverflow.com/questions/32732932/floatingactionbutton-visible-for-sometime-even-if-visibility-is-set-to-gone - Gatunox
@Linxy: 如果你还需要这个,可以看下面的回答。即使将宽度和高度设置为0也没有帮助,我最后是完全从父布局中移除并重新添加了FAB。 - Lisa Wray
1
我的FAB位于相对布局内,没有使用app:layout_anchor属性,并且我已经尝试通过编程和XML设置可见性。有什么解决方案吗? - NAUSHAD
1
它对我不起作用,所以我只是设置了fab的“x”值。fab.x = -500f; 和 fab.x = oldPos; 以便将其带回来。 - Jayakrishnan

16

与AppBarLayout锚定的FloatingActionButtons具有特殊关系,其可见性受AppBarLayout的滚动位置控制。

您可以通过behavior_autoHide属性关闭此功能:

<android.support.design.widget.FloatingActionButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_anchor="..."
    app:behavior_autoHide="false"/>

如果你愿意,也可以通过编程来实现这一点:

编辑:

fab.getBehavior()是不正确的,请使用其LayoutParams中的getBehavior()

CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
FloatingActionButton.Behavior behavior = (FloatingActionButton.Behavior) lp.getBehavior();
if (behavior != null) {
    behavior.setAutoHideEnabled(false);
} else {
    behavior = new FloatingActionButton.Behavior();
    behavior.setAutoHideEnabled(false);
    lp.setBehavior(behavior);
}

3
找不到fab.getBehavior()方法? - KittoKatto
它可以工作。从CoordinatorLayout.LayoutParams获取行为。 - inhogo
仅使用 behavior_autoHide 是不够的。默认情况下,它是使用默认构造函数创建的。为了使此属性起作用,您必须显式指定 layout_behaviorcom.google.android.material.floatingactionbutton.FloatingActionButton$Behavior - wrozwad

15

最简单的隐藏和显示浮动操作按钮的方法是在您的活动中调用此函数。这也将自动适当地对您的 FAB 进行动画处理。

隐藏:

nameOfYourFAB.Hide();

展示:

nameOfYourFAB.Show();

这并不适用于所有情况,例如在CoordinatorLayout中涉及FAB。 - CanCoder

14

我不完全满意发布的任何解决方案。有些只能在某些时候起作用,而其他一些只适用于fab.setVisibility()。虽然我知道这从技术上讲是原始问题所要求的,但是一些回复表达了使用fab.hide()的兴趣,并且玩弄布局参数并不能真正解决问题的根源。

正如@ChrisBanes所指出的那样,FloatingActionButton.BehaviorAppBarLayout相关联正是导致这个问题的原因。因此,就像他的答案一样,您必须setAutoHideEnabled(false)以禁用该功能。但是,如果您实际上想要FloatingActionButton自动隐藏并在手动调用hide()时隐藏,这种解决方案没有帮助。

因此,为了做到这一点; 我只需在手动隐藏Button之前禁用自动隐藏功能,然后在手动显示Button后重新启用该功能:

private void hideFloatingActionButton(FloatingActionButton fab) {
    CoordinatorLayout.LayoutParams params =
            (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
    FloatingActionButton.Behavior behavior =
            (FloatingActionButton.Behavior) params.getBehavior();

    if (behavior != null) {
        behavior.setAutoHideEnabled(false);
    }

    fab.hide();
}

private void showFloatingActionButton(FloatingActionButton fab) {
    fab.show();
    CoordinatorLayout.LayoutParams params =
            (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
    FloatingActionButton.Behavior behavior =
            (FloatingActionButton.Behavior) params.getBehavior();

    if (behavior != null) {
        behavior.setAutoHideEnabled(true);
    }
}

7
FloatingActionButton layers = (FloatingActionButton) findViewById(R.id.layers);
layers.hide();

对我而言它可行,但是setVisibility对于FloatingActionButton无效,因为它属于另一个没有setVisibility方法的viewGroup


它适用于我,setVisibility 对于 FloatingActionButton 不起作用,因为它属于另一个没有 setVisibility 方法的 viewGroup。 - Satendra Behre

6

如果您想显示隐藏的浮动操作按钮(FAB)

    <android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/coordinator"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">


    <android.support.design.widget.AppBarLayout
            android:id="@+id/appbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            android:fitsSystemWindows="true">
    ...

    </android.support.design.widget.AppBarLayout>

    ...

    <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:visibility="gone"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:clickable="true"/>

</android.support.design.widget.CoordinatorLayout>

并且

    CoordinatorLayout.LayoutParams p = new CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.WRAP_CONTENT, CoordinatorLayout.LayoutParams.WRAP_CONTENT);
    p.anchorGravity = Gravity.BOTTOM | Gravity.END;
    p.setAnchorId(R.id.appbar);
    p.setMargins(...);
    fab.setLayoutParams(p);
    fab.setVisibility(View.VISIBLE);

谢谢!很有用,可以让按钮回来。 - Gopal Singh Sirvi

3

在一些答案的帮助下,我找到了解决我的问题的方法(即使fab已隐藏,它也可以处理操作!)。

首先,我建议您使用.show().hide()方法来替换.setVisibility(View.GONE).setVisibility(View.VISIBLE)。这两个方法还能处理actionMode

对我来说,第二个问题是即使fab被隐藏了,操作依然可以被处理,为了解决这个问题,我做了以下处理:

final CoordinatorLayout.LayoutParams p = (CoordinatorLayout.LayoutParams)floatingActionButton.getLayoutParams();
    if (show){
        p.setAnchorId(R.id.appBarLayout);
        p.width = CoordinatorLayout.LayoutParams.WRAP_CONTENT;
        p.height = CoordinatorLayout.LayoutParams.WRAP_CONTENT;
        floatingActionButton.setLayoutParams(p);
        floatingActionButton.show();
    }else{
        p.setAnchorId(View.NO_ID);
        p.width = 0;
        p.height = 0;
        floatingActionButton.setLayoutParams(p);
        floatingActionButton.hide();
    }

1
哦,安卓,为什么这么复杂?谢谢。 - Mrusful

1
你的问题的真正解决方案是子类化默认的FloatingActionButton.Behavior以在其构造函数中调用setAutoHide(false),这样你就可以自己控制它。
注意,下面我谈到了底部表单,但对于所有锚定的浮动操作按钮都是完全相同的问题,并且它确实回答了问题并应该按预期解决问题。
或者,你可以从自定义行为中重写boolean onDependentViewChanged(…)方法,复制FloatingActionButton.Behavior类中存在的isBottomSheet(…)静态方法,并且仅在不是底部表单时调用和返回超级方法的值。
你可以通过这种方式进一步自定义默认行为,或直接扩展香草CoordinatorLayout.Behavior类,并且如果需要,可以从FloatingActionButton.Behavior类中挑选代码进行复制粘贴。
以下是我用来控制FAB可见性的代码:
/**
 * Allows controlling the FAB visibility manually.
 */
@SuppressWarnings("unused")
public class FabManualHideBehavior extends FloatingActionButton.Behavior {

    public FabManualHideBehavior() {
        super();
        setAutoHideEnabled(false);
    }

    public FabManualHideBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        setAutoHideEnabled(false);
    }
}

我把它应用到我的xml中的FAB上,方法如下:

<android.support.design.widget.CoordinatorLayout
    ...>
    ...
    <android.support.design.widget.FloatingActionButton
        ...
        app:layout_anchor="@+id/bottom_sheet"
        app:layout_anchorGravity="top|end"
        app:layout_behavior="your.package.name.ui.behavior.FabManualHideBehavior"/>

</android.support.design.widget.CoordinatorLayout>

1
这是一个简单的解决方案。
private var fabAnchorId: Int = View.NO_ID
private val fabParams: CoordinatorLayout.LayoutParams
        get() = (fab.layoutParams as CoordinatorLayout.LayoutParams)

fun showFab() {
    fabParams.anchorId = fabAnchorId
    fabParams.gravity = Gravity.NO_GRAVITY
    fab.show()
}

fun hideFab() {
    fabParams.anchorId = View.NO_ID
    fabParams.gravity = Gravity.END.or(Gravity.BOTTOM)
    fab.hide()
}

在显示/隐藏之前,我们需要更改锚点和重力


0

这些其他解决方案都没有完全帮助到我。我需要重复的、动画式的显示和隐藏(例如像FAB.show()hide()),同时也需要在可见时锚定到应用程序栏。

最终,我每次显示FAB时都会创建新的FAB,手动插入并锚定它,并基于支持库实现对其进行动画处理。虽然有点原始,但运行非常完美。

private void hidePlayButton(final FloatingActionButton fab) {
    // Cancel any animation from the default behavior
    fab.animate().cancel();
    fab.animate()
               .scaleX(0f)
               .scaleY(0f)
               .alpha(0f)
               .setDuration(200)
               .setInterpolator(new FastOutLinearInInterpolator())
               .setListener(new Animator.AnimatorListener() {
                   @Override public void onAnimationStart(Animator animation) {}

                   @Override public void onAnimationEnd(Animator animation) {
                      coordinatorLayout.removeView(fab);
                   }

                   @Override public void onAnimationCancel(Animator animation) {}

                   @Override public void onAnimationRepeat(Animator animation) {}
               });
}

private void showPlayButton() {
    int fabSize = getResources().getDimensionPixelSize(R.dimen.fab_size);
    int margin = getResources().getDimensionPixelSize(R.dimen.fab_margin);
    final FloatingActionButton fab = new FloatingActionButton(getActivity());
    fab.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(getActivity(), R.color.tint)));
    fab.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.play));
    CoordinatorLayout.LayoutParams p = new CoordinatorLayout.LayoutParams(fabSize, fabSize);
    p.rightMargin = p.leftMargin = p.bottomMargin = p.topMargin = margin;
    p.anchorGravity = Gravity.BOTTOM | Gravity.END;
    p.setAnchorId(R.id.appbar);
    fab.setLayoutParams(p);
    // Start from 1 pixel
    fab.setAlpha(0f);
    fab.setScaleX(0f);
    fab.setScaleY(0f);
    binding.coordinatorLayout.addView(fab);
    fab.animate()
       .alpha(1f)
       .scaleX(1f)
       .scaleY(1f)
       .setDuration(200)
       .setInterpolator(new FastOutLinearInInterpolator());

    fab.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View v) {
            hidePlayButton(fab);
            // do action
        }
    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接