Android - 在ViewPager中切换不同的Fragment时隐藏FAB按钮

12

我试图做一些非常简单的事情。

我希望FloatingActionButton(FAB)仅出现在我的TabLayout中的一个选项卡上,并在导航到另一个选项卡时隐藏。例如,一个选项卡将允许您在FAB中添加新项目,但下一个选项卡将不允许您添加项目。

我已经按照“典型”的XML设计布局进行了操作:

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

<android.support.design.widget.AppBarLayout
    android:id="@+id/appBarLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_scrollFlags="scroll|enterAlways">

        <LinearLayout
            android:id="@+id/search_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <EditText
                android:id="@+id/search_view"
                android:layout_width="0dp"
                android:layout_height="?attr/actionBarSize"
                android:layout_weight="1"
                android:background="@android:color/transparent"
                android:gravity="center_vertical"
                android:hint="Search"
                android:imeOptions="actionSearch"
                android:inputType="text"
                android:maxLines="1"
                android:paddingLeft="2dp"
                android:singleLine="true"
                android:textColor="#ffffff"
                android:textColorHint="#b3ffffff" />

            <ImageView
                android:id="@+id/search_clear"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:paddingLeft="16dp"
                android:paddingRight="16dp"
                android:src="@drawable/ic_action_cancel" />

        </LinearLayout>

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

    <android.support.design.widget.TabLayout
        android:id="@+id/sliding_tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        app:tabMode="scrollable"
        />


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

<android.support.v4.view.ViewPager
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="com.example.simon.behaviours.PatchedScrollingViewBehavior"/>

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    app:borderWidth="0dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_action_new"
    android:layout_margin="16dp"
    android:layout_gravity="bottom|right"
    app:layout_anchor="@+id/viewPager"
    app:layout_anchorGravity="bottom|right|end"
    app:layout_behavior="com.example.simon.behaviours.ScrollingFABBehavior"
    android:visibility="gone"
    app:fabSize="normal">
</android.support.design.widget.FloatingActionButton>

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

我已经使用了以下行为来处理FAB。当向上滚动时,它会消失,并在向下滚动时重新出现:

public class ScrollingFABBehavior extends FloatingActionButton.Behavior {
    private int toolbarHeight;

    public ScrollingFABBehavior(Context context, AttributeSet attrs) {
        super();
        this.toolbarHeight = getToolbarHeight(context);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
        return super.layoutDependsOn(parent, fab, dependency) || (dependency instanceof AppBarLayout);
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
        boolean returnValue = super.onDependentViewChanged(parent, fab, dependency);
        if (dependency instanceof AppBarLayout) {
            CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
            int fabBottomMargin = lp.bottomMargin;
            int distanceToScroll = fab.getHeight() + fabBottomMargin;
            float ratio = (float)dependency.getY()/(float)toolbarHeight;
            fab.setTranslationY(-distanceToScroll * ratio);
        }
        return returnValue;
    }

    public static int getToolbarHeight(Context context) {
        final TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
                new int[]{R.attr.actionBarSize});
        int toolbarHeight = (int) styledAttributes.getDimension(0, 0);
        styledAttributes.recycle();

        return toolbarHeight;
    }
}

我已添加了一个ViewPager的addOnPageChangeListener:

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        if (position == 0) {
            FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.fab);
            floatingActionButton.setVisibility(View.VISIBLE);
        }
        else
        {
            FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.fab);
            floatingActionButton.setVisibility(View.GONE);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
});

我只希望FAB出现在第一页,并在所有其他页面上消失。

代码可以工作,但当我在下一页向下滑动时,即使可见性设置为gone,FAB也会出现。我认为这与为FAB设置的行为有关。有人知道如果将可见性设置为gone,为什么在向下滑动时FAB仍然变得可见吗?


1
FAB 只是一个普通的视图。您不需要使用特殊布局。当页面更改片段时,只需更改可见性或动画平移即可。 - eleven
在查看FAB的代码后,hide()将其可见性设置为View.GONE,而show()将其可见性设置为View.VISIBLE。因此,当我们向上滚动屏幕时,它调用了hide(),而当我们向下滚动屏幕时,它调用了show(),这总是将其设置回View.VISIBLE。 - Dark Leonhart
考虑在OnPageChangeListener中使用标准的hide()show() FAB方法,并使FAB默认可见。这个解决方案对我很有效。 - irudyak
6个回答

8

虽然我最终没有在我的应用程序中实现这个功能,但最终我还是找到了答案,通过查看wordpress应用程序如何实现相同的功能来获得帮助。

在wordpress应用程序中,我们可以在应用程序的第一页看到一个浮动操作按钮,如果您滑动到视图页面中的任何其他页面,则该按钮会消失:

wordpress

他们通过以下代码实现了这一点 - 这是包含viewpager的Activity的代码。您可以在onPageScrolled方法下看到相关部分的代码,其中包含发送事件的eventbus,每次滚动页面时都会发布一个事件。事件仅包含一个名为positionOffset的变量,它是从0到1的整数值。如果您滚动并且页面处于两个viewpager之间的一半位置,则positionOffset为0.5,你明白了:

WPMainActivity.java

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                AppPrefs.setMainTabIndex(position);

                switch (position) {
                    case WPMainTabAdapter.TAB_NOTIFS:
                        new UpdateLastSeenTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                        break;
                }
                trackLastVisibleTab(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                // noop
            }

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                // fire event if the "My Site" page is being scrolled so the fragment can
                // animate its fab to match
                if (position == WPMainTabAdapter.TAB_MY_SITE) {
                    EventBus.getDefault().post(new MainViewPagerScrolled(positionOffset));
                }
            }
        });

该事件在包含下列代码的片段中被捕获。当页面根据位置偏移确定页面滚动超出视图时,事件会触发translationY方法,垂直地对浮动操作按钮进行动画处理:

MySiteFragment

/*
 * animate the fab as the users scrolls the "My Site" page in the main activity's ViewPager
 */
@SuppressWarnings("unused")
public void onEventMainThread(CoreEvents.MainViewPagerScrolled event) {
    mFabView.setTranslationY(mFabTargetYTranslation * event.mXOffset);
}

最后,my_site_fragment.xml布局显示FAB实际上是放置在片段的xml文件中而不是活动的xml文件中。
<!-- this coordinator is only here due to https://code.google.com/p/android/issues/detail?id=175330 -->
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_marginBottom="@dimen/fab_margin"
        android:layout_marginRight="@dimen/fab_margin"
        android:src="@drawable/gridicon_create_light"
        app:borderWidth="0dp"
        app:rippleColor="@color/fab_pressed" />
</android.support.design.widget.CoordinatorLayout>

5

嘿,我和你一样也遇到了同样的问题。

我找到了两种方法可以解决这个问题。

方法1:

在你的MainActivity中添加一个静态标志:public static boolean fabVisible;

我注意到你有一个监听器来检测当前片段页的viewPager,所以你可以添加这个:

    public void onPageSelected(int position) {
        switch (position) {
            case 0:
                fabVisible = true;
                mFloatingActionButton.show();
                break;
            default:
                fabVisible = false;
                mFloatingActionButton.hide();
                break;
        }
    }

在您的自定义 FAB 行为中添加以下代码:

    if (dyConsunmed > 0 && child.getVisibility() == View.VISIBLE ) {
            child.hide();
        } else if (dyConsunmed < 0 && child.getVisibility() != View.VISIBLE && MainActivity.fabVisible) {
            child.show();
        }

这种方法的目的是确保在实现隐藏和显示方法之前,FAB应该是可见的。

方法2:

您不再需要在MainActivity中添加任何类似于“fabVisible”的标志。虽然此方法要求您将viewPager设置为静态的,因为我们需要在自定义FAB行为类中访问它。

在您的ScrollAwareFABBehavior类中,添加以下代码:

    if (MainActivity.viewPager.getCurrentItem() == 0) {
        if (dyConsunmed > 0 && child.getVisibility() == View.VISIBLE ) {
            child.hide();
        } else if (dyConsunmed < 0 && child.getVisibility() != View.VISIBLE) {
            child.show();
        }

    }

这种方法的关键是,仅当你在想要的片段内时才显示和隐藏(在我的例子中,是片段一所以当前项为0)。当然,为了在第二个片段中删除fab,仍需要在您的viewPager侦听器中隐藏它。
如果您对实现感到困惑,请随意查看我的GitHub存储库并玩弄代码。地址是:https://github.com/Anthonyeef/FanfouDaily 尽管提要内容全部是中文,但代码都是英文。

3

实现ViewPager.onPageListener并重写onPageSelected,根据您的要求在选项卡上显示或隐藏FAB。以下是示例代码:

@Override
public void onPageSelected(int position) {
     //FAB_PAGE is the index of the page on which you want to show FAB
    if(position == FAB_PAGE)
    {
        fab.setVisibility(View.VISIBLE);

    }
    else
    {
        fab.setVisibility(View.GONE);
    }

}

你还可以使用 fab.show(); 和 fab.hide();

此外,详细了解请参考这里


与其像上面那样使用 OnPageChangeListener,你可以使用 Rx 和 RxBinding 来处理 ViewPager。代码应该长成这样:RxViewPager.pageSelections(mViewPager).subscribe([...])。订阅以获得当前页面位置。这个方法的好处是你不需要像 OnPageChangeListener 那样去实现其他的回调方法,只要别忘了取消订阅即可。 - Michael De Soto

1
当您向下滑动到下一个选项卡时,您可以通过编程方式完成它。
FloatingActionButton fab = (FloatingActionButton) view.findViewByID(R.id.fab)
fab.setVisibility(View.GONE);

当你向后滑动时,将可见性设置为View.VISIBLE


你好,我尝试了你的答案,但是没有成功。我的应用程序有一个工具栏和FAB,在向上滑动时消失,在向下滑动时返回。当我按照你在addOnPageChangeListener中建议的编程方式对FAB进行编码时,FAB确实会消失,但是当我向下滑动时,FAB会“返回”到视图中,并且变得可见,即使我将其设置为不可见/已消失。 - Simon

1
在您的活动中创建一个静态布尔值(参见下面的YourActivity.isFABinCurrentTabVisible),并根据需要从您的ViewPager进行更改,以确定当前选项卡中是否应该有fab。虽然可能是一种不太完美的解决方法...请考虑使用fab.show()fab.hide()而不是setVisiblity()。这些将执行淡入淡出动画。
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {

public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
    super();
}

@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
                                   final View directTargetChild, final View target, final int nestedScrollAxes) {
    // Ensure we react to vertical scrolling
    return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
            || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}

@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
                           final View target, final int dxConsumed, final int dyConsumed,
                           final int dxUnconsumed, final int dyUnconsumed) {
    super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
    if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
        // User scrolled down and the FAB is currently visible -> hide the FAB
        child.hide();
    } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE && YourActivity.isFABinCurrentTabVisible) {
        // User scrolled up and the FAB is currently not visible -> show the FAB
        child.show();
    }
}
}

-1

听起来你的方法可能有些不对...

例如,一个选项卡可以让你在FAB中添加新项目,但下一个选项卡则不能添加项目。

将FAB放在第一页的Fragment中,而不是托管Fragment/Activity中。这样它就会随着第一页片段自动消失 - 甚至会与其一起动画。它也会在正确的位置,因为它与其他页面/选项卡没有任何关系。

如果你真的需要在托管Fragment/Activity中处理点击事件,请让你的第一页片段在发生点击时回调到托管Fragment/Activity


你好。我尝试了你的方法,但仍然无法解决问题。FAB似乎不再与工具栏移动相关联,因此如果在向下滑动时工具栏消失,则实际上会出现FAB而不是消失。如果工具栏完全可见,则FAB只有一半可见。我认为问题在ScrollingFABBehavior的代码中。 - Simon
2
如果您在每个片段中放置FAB,它将在viewpager中移动,这与材料设计准则相违背:https://www.google.com/design/spec/components/buttons-floating-action-button.html#buttons-floating-action-button-behavior - android developer

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