21

我正在尝试将 aCoordinatorLayout与 a BottomNavigationView、 anAppBarLayout和 a一起使用ViewPager。这是我的布局:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="enterAlways|scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

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

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="?android:attr/windowBackground"
        app:itemIconTint="?colorPrimaryDark"
        app:itemTextColor="?colorPrimaryDark"
        app:menu="@menu/navigation"/>
</android.support.design.widget.CoordinatorLayout>

问题是延伸到屏幕底部的CoordinatorLayout地方,所以底部被 遮住了,像这样:ViewPagerBottomNavigationView

ViewPager 的边界

即使它CoordinatorLayout本身没有向下延伸,也会发生这种情况:

CoordinatorLayout 的边界

我尝试添加app:layout_insetEdge="bottom"BottomNavigationViewapp:layout_dodgeInsetEdges="bottom"ViewPager但这有一个不同的问题:它移动了底部的ViewPager向上,但它保持相同的高度,所以顶部现在被切掉了:

移动 ViewPager 边界

我尝试了另外两个实验。首先,我尝试从BottomNavigationView中 删除CoordinatorLayout并使它们成为垂直下的兄弟姐妹LinearLayout。其次,我将ViewPagerandBottomNavigationView放在 a 下LinearLayout,希望它们的布局正确。两者都没有帮助:在第一种情况下,CoordinatorLayout仍然是ViewPager相对于整个屏幕的大小,或者将部分隐藏在后面BottomNavigationView或切掉顶部。在第二种情况下,用户需要滚动才能看到BottomNavigationView.

如何正确布局?

PS当我尝试@Anoop SS建议的布局时(将CoordinatorLayoutBottomNavigationView作为兄弟姐妹放在 a 下RelativeLayout),我得到以下信息(ViewPager仍然在 后面向下延伸BottomNavigationView):

Anoop 布局的结果

和以前一样,它CoordinatorView本身只向下延伸到BottomNavigationView.

4

7 回答 7

14

我想出了一个不同的方法(虽然还没有经过实战测试):

我根据(如果存在)AppBarLayout.ScrollingViewBehavior的高度进行了子类化以调整内容视图的底部边距。BottomNavigationView这样,如果高度BottomNavigationView因任何原因发生变化,它应该是未来的证明(希望如此)。

子类(Kotlin):

class ScrollingViewWithBottomNavigationBehavior(context: Context, attrs: AttributeSet) : AppBarLayout.ScrollingViewBehavior(context, attrs) {
    // We add a bottom margin to avoid the bottom navigation bar
    private var bottomMargin = 0

    override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        return super.layoutDependsOn(parent, child, dependency) || dependency is BottomNavigationView
    }

    override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        val result = super.onDependentViewChanged(parent, child, dependency)

        if(dependency is BottomNavigationView && dependency.height != bottomMargin) {
            bottomMargin = dependency.height
            val layout = child.layoutParams as CoordinatorLayout.LayoutParams
            layout.bottomMargin = bottomMargin
            child.requestLayout()
            return true
        } else {
            return result
        }
    }
}

然后在您放置的布局 XML 中:

app:layout_behavior=".ScrollingViewWithBottomNavigationBehavior"

代替

app:layout_behavior="@string/appbar_scrolling_view_behavior"
于 2018-11-27T10:08:36.130 回答
1

这是由app:layout_behavior="@string/appbar_scrolling_view_behavior"您的 ViewPager 引起的。如果删除此行,您将看到它现在适合 CoordinatorLayout 容器(不幸的是,这包括现在位于工具栏下方)。

我发现它有助于将 CoordinatorLayout 视为一个 FrameLayout,并带有一些额外的技巧。上面的 app:layout_behavior 属性是允许工具栏出现滚动进出所必需的......实际上,布局是通过将视图链接到折叠工具栏(在您的情况下,您的 ViewPager)来做到这一点的工具栏的高度大于边界。向上滚动将视图带到边界内的底部,并将工具栏向上推到边界之外。向下滚动,反之亦然。

现在,进入 BottomNavigationView!如果像我一样,您希望BottomNavigationView 始终可见,然后将其移到CoordinatorLayout 之外,正如Anoop 所说。仅将 CoordinatorLayout 用于需要协调的事物,其他一切都在外面。我碰巧为我的父视图使用了一个 ConstraintLayout(你可以使用 RelativeLayout 或任何对你有用的东西)。使用 ConstraintLayout,对您而言,它看起来像这样:

 <android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">


<android.support.design.widget.CoordinatorLayout
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@id/navigation"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="enterAlways|scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

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

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

<android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:background="?android:attr/windowBackground"
    app:itemIconTint="?colorPrimaryDark"
    app:itemTextColor="?colorPrimaryDark"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:menu="@menu/navigation" />

 </android.support.constraint.ConstraintLayout>

在 Android Studio 设计视图中,您仍然会看到 ViewPager 看起来比容器大(可能看起来它仍然位于底部导航的后面)。不过没关系,当您到达 ViewPager 内容的底部时,它会显示(即不会在底部导航的后面)。如前所述,设计视图中的这个怪癖正是 CoordinatorLayout 使工具栏显示/隐藏的方式。

于 2017-12-23T23:05:44.867 回答
1

基本上,您要做的就是创建一个 Relativelayout 作为父级,并将 BottomNavigationView 和 CoordinatorLayout 作为子级。然后在底部对齐 BottomNavigationView 并在其上方设置 CoordinatorLayout。请尝试以下代码。它可能很少有属性错误,因为我自己在这里写的。并为混乱的缩进感到抱歉。

<?xml version="1.0" encoding="utf-8"?>

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/navigation"
        >

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_scrollFlags="enterAlways|scroll"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

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

        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

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

<android.support.design.widget.BottomNavigationView
            android:id="@+id/navigation"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="?android:attr/windowBackground"
            app:itemIconTint="?colorPrimaryDark"
            app:itemTextColor="?colorPrimaryDark"
            app:menu="@menu/navigation"/>

    </RelativeLayout>
于 2017-12-21T03:53:58.420 回答
1

我有一个非常接近 OP 的布局和一个有 3 页但只有第 2 页和第 3 页应该受 appbar_scrolling_view_behavior 影响的 ViewPager 的类似问题。

经过数小时探索死胡同的可能解决方案(layout_dodgeInsetEdges、Window insets、尝试修改 ViewPager 的页面测量大小、android:clipChildren、fitSystemWindows...)后,我终于找到了一个简单的解决方案,详述如下。

正如 Vin Norman 所解释的,ViewPager 与 BottomNavigation 重叠完全是由 ViewPager 上设置的 appbar_scrolling_view_behavior 引起的。AppBarLayout 只会使具有 appbar_scrolling_view_behavior 的兄弟姐妹全屏。这就是它的工作原理。

如果您只需要在某些 ViewPager 页面上使用此行为,则有一个简单的修复方法,您可以将其应用于 ViewPager 的 OnPageChangeListener 以动态更改行为并添加/删除所需的填充:

public class MyOnPageChangeListener extends  ViewPager.SimpleOnPageChangeListener {

        @Override
        public void onPageSelected(int position) {

           ...

           CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) _viewPager.getLayoutParams();

        if(position == 0) {
            params.setBehavior(null);
            params.setMargins(params.leftMargin, _appBarLayoutViewPagerMarginTopPx, 
                    params.rightMargin, _appBarLayoutViewPagerMarginBottomPx);
        } else {
            params.setBehavior(_appBarLayoutViewPagerBehavior);
            params.setMargins(params.leftMargin, 0, params.rightMargin, 0);
        }

        _viewPager.requestLayout();

        }

}

对于位置 0 的页面(我们希望 ViewPager 正好延伸到 Toolbar 下方和 BottomNavigationView 上方的页面),它会删除行为并添加顶部和底部填充,分别是 _appBarLayoutViewPagerMarginTopPx 和 _appBarLayoutViewPagerMarginBottomPx 是易于预先计算的常量(分别是值R.attr.actionbarSize 的像素和 NavigationBottomView 的高度。通常都是 56dp)

对于需要 appbar_scrolling_view_behavior 的所有其他页面,我们恢复相关的滚动行为(预先存储在 _appBarLayoutViewPagerBehavior 中)并删除顶部和底部填充。

我测试了这个解决方案,它没有任何警告就可以正常工作。

于 2018-05-14T12:31:46.693 回答
0

如果有人仍在寻找此问题的解决方案:

问题的原因是 CoordinatorLayout 没有正确计算 AppBarLayout 的大小,因为它具有带有app:layout_scrollFlags="enterAlways|scroll"设置的工具栏。它认为工具栏在滚动时会隐藏,因此它将所有可用空间留给 ViewPager,但实际上发生的是工具栏显示,因此 ViewPager 向下移动,位于 NavigationBar 后面。

解决此问题的最简单方法是将android:minHeight="?attr/actionBarSize"(或您正在使用的任何工具栏高度)添加到 AppBarLayout。这样 CoordinatorLayout 将正确地知道它需要为 ViewPager 留下多少空间。

于 2019-09-11T15:22:07.337 回答
0

如果它对某人仍然很重要:

在上面 Anoop SS 的回答中,尝试RelativeLayoutLinearLayout. 也设置layout_heightCoordinatorLayout0dp 并设置layout_weight为 1。

我有几乎同样的问题....只是我想AdView在底部有一个静态而不是BottomNavigationView. 尝试 Anoop SS 建议,起初,我得到了与 OP 相同的行为:ViewPagerAdView. 但后来我按照我的建议做了,一切都很好。

Android 布局的行为方式很奇怪,或者可能是缺乏良好的文档或我们缺乏知识......但是大多数时候制作布局太烦人了。

于 2018-09-09T19:06:40.533 回答
-1

如果您使用的是 Androidx 试试这个

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">

<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:layout_above="@+id/bottomNavView">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/mobile_navigation" />
    </FrameLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottomNavView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:background="?android:attr/windowBackground"
    app:menu="@menu/bottom_nav" />

于 2019-12-03T17:21:06.753 回答