CoordinatorLayout内容子重叠BottomNavigationView

时间:2017-12-21 03:08:50

标签: android android-layout bottomnavigationview coordinator-layout

我正在尝试将CoordinatorLayoutBottomNavigationViewAppBarLayoutViewPager一起使用。这是我的布局:

<?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>

问题在于CoordinatorLayoutViewPager放置到屏幕底部,因此底部被BottomNavigationView遮挡,如下所示:

bounds of ViewPager

即使CoordinatorLayout本身到目前为止没有延伸,也会发生这种情况:

bounds of CoordinatorLayout

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

shifted ViewPager bounds

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

如何正确布局?

P.S。当我尝试the layout suggested by @Anoop S S(将BottomNavigationViewCoordinatorLayout作为兄弟姐妹放在BottomNavigationView下)时,我得到以下内容(RelativeLayout仍在后面延伸ViewPager):

result of Anoop's layout

与以前一样,BottomNavigationView本身只会延伸到CoordinatorView的顶部。

7 个答案:

答案 0 :(得分:3)

我想出了另一种方法(虽然尚未经过战斗测试):

我将AppBarLayout.ScrollingViewBehavior细分为子类,以根据BottomNavigationView的高度(如果存在)调整内容视图的底边距。这样,BottomNavigationView的高度由于任何原因发生变化(希望)将是对未来的证明。

子类(科特琳):

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"

答案 1 :(得分:2)

基本上你需要做的是创建一个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>

答案 2 :(得分:1)

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

经过几个小时探索死胡同可能的解决方案(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正好在工具栏下方和BottomNavigationView上方延伸的页面),它会删除行为并添加顶部和底部填充,分别为_appBarLayoutViewPagerMarginTopPx和_appBarLayoutViewPagerMarginBottomPx,它们是易于预先计算的常量(分别为R.attr.actionbarSize的像素值和NavigationBottomView的高度。通常两者都是56dp)

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

我测试了这个解决方案,它运行正常,没有任何警告。

答案 3 :(得分:0)

这是由ViewPager中的app:layout_behavior="@string/appbar_scrolling_view_behavior"引起的。如果你删除这一行,你会看到它适合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看起来比容器大(可能看起来仍然在Bottom Nav背后)。但是没关系,当你到达ViewPager内容的底部时,它会显示(即不会落后于底部导航)。如前所述,设计视图中的这个怪癖就是CoordinatorLayout使工具栏显示/隐藏的方式。

答案 4 :(得分:0)

如果仍然对某人重要:

在上述Anoop SS的回答中,尝试将RelativeLayout替换为LinearLayout。还将layout_height中的CoordinatorLayout设置为0dp并将layout_weight设置为1。

我遇到了几乎相同的问题。...只是我想在底部使用静态AdView而不是BottomNavigationView。尝试使用Anoop SS建议时,起初,我的行为与OP相同:ViewPager扩展到AdView之后。但是后来我按照我的建议做了,一切都很好。

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

答案 5 :(得分:0)

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

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

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

答案 6 :(得分:0)

如果您使用的是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" />