Android - 在 vi​​ewpager 中的不同片段之间导航时隐藏的 FAB

Android - FAB to hide when navigating between different fragments in a viewpager

我正在尝试做一些非常简单的事情。

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

我遵循了'typical'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 使用了以下行为。这会导致任何向上滚动导致 FAB 消失,并会 return 在向下滚动时返回屏幕:

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 出现在第一页上,而在所有其他页面上消失。

代码有效,但是当我在下一页上向下滑动时,即使可见性设置为消失,FAB 也会出现。我认为这与为 FAB 设置的行为有关。有谁知道如果可见性设置为消失,为什么 FAB 在向下滑动时仍然可见?

您可以在滑动到下一个选项卡时以编程方式执行此操作。

FloatingActionButton fab = (FloatingActionButton) view.findViewByID(R.id.fab)
fab.setVisibility(View.GONE);

然后在向后滑动时将可见性设置为 View.VISIBLE

听起来你从错误的角度来处理这个问题...

So for example, one tab would let you add new items in the FAB, but the next tab would not let you add items.

将 FAB 放在第一页的 Fragment 而不是托管 Fragment / Activity。这样它会自动与第一页片段一起消失 - 它甚至会与它一起动画。它也将是它所属的位置,因为它与其他页面/标签完全无关。

如果您确实需要在您的主机 Fragment / Activity 中处理点击事件,请将您的第一个页面片段回调到主机 Fragment / Activity当点击发生时。

在您的 activity 中创建一个静态布尔值(请参阅下面的 YourActivity.isFABinCurrentTabVisible)并根据当前选项卡中是否应该有 fab 相应地从您的 ViewPager 中更改它。 虽然可能是一个肮脏的修复...... 并考虑使用 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();
    }
}
}

实现 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();

另请查看 了解更多详情。

这并没有成为我想要在我的应用程序中实现的东西,但我最终设法找到了答案,通过查看他们如何在 wordpress 应用程序上实现相同的东西得到了一些帮助。

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

他们通过以下代码完成此操作 - 这是用于保存 viewpager 的 Activity 的代码。您可以在 onPageScrolled 方法下看到代码的相关部分,其中包含每次滚动页面时发布事件的事件总线。该事件仅包含一个名为 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 方法,该方法根据 positionOffset 确定的页面滚动出视图的距离来垂直动画 FAB:

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 而不是 activity 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>

嘿,我遇到了和你完全一样的问题。

我发现了两种对我有用的方法。

方法一:

在您的 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 应该是可见的。

方法二:

您不再需要在 MainActivity 中添加任何标志,例如 "fabVisible"。尽管此方法确实需要您在 MainActivity 中将 viewPager 设置为静态,因为我们需要能够在自定义 fab 行为 class.

中访问它

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

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

    }

这个方法的关键点是,当你在你想要的片段中时,只进行显示和隐藏方法(在我的例子中,它是片段一,所以 currenItem == 0)。当然,为了删除第二个片段中的 fab,您仍然需要将其隐藏在您的 viewPager 侦听器中。

如果您仍然对该工具感到困惑,请随时查看我在 GitHub 上的存储库并使用代码。地址是:https://github.com/Anthonyeef/FanfouDaily

虽然提要内容都是中文的,但是代码都是英文的