CoordinatorLayout 内容子项与 BottomNavigationView 重叠

Posted

技术标签:

【中文标题】CoordinatorLayout 内容子项与 BottomNavigationView 重叠【英文标题】:CoordinatorLayout content child overlaps BottomNavigationView 【发布时间】:2018-06-03 16:21:53 【问题描述】:

我正在尝试将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_
    android:layout_
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_
        android:layout_
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_
            android:layout_
            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_
        android:layout_
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_
        android:layout_
        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遮住了,像这样:

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

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

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

如何正确布局?

附:当我尝试the layout suggested by @Anoop S S (将CoordinatorLayoutBottomNavigationView 作为兄弟姐妹放在RelativeLayout 下)时,我得到以下信息(ViewPager 仍在BottomNavigationView 后面向下延伸):

和以前一样,CoordinatorView 本身仅向下延伸到BottomNavigationView 的顶部。

【问题讨论】:

please check the answer here it is working fine here 我创建了一个关于这个issuetracker.google.com/issues/116541304的问题 【参考方案1】:

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

我对@9​​87654321@ 进行了子类化,以根据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"

【讨论】:

很好的解决方案,谢谢!我发现的唯一解决方案利用了 CoordinatorLayout 强大的行为系统。注意:我的子视图是 Fragment/FrameLayout 所以我必须更新它以使用填充而不是边距,它的效果非常好。 噢,伙计。早点找到这个可以节省我几个小时的尝试时间:)谢谢! 最佳解决方案。非常感谢。【参考方案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_
        android:layout_
        tools:context=".MainActivity">

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

        <android.support.design.widget.AppBarLayout
            android:layout_
            android:layout_
            android:fitsSystemWindows="true"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_
                android:layout_
                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_
            android:layout_
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

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

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

    </RelativeLayout>

【讨论】:

我不得不将BottomNavigationView 移动为CoordinatorLayout 的兄弟而不是孩子。这仍然没有解决问题,尽管它稍微改善了它们。 ViewPager 仍然在BottomNavigationView 后面延伸,尽管它现在不在系统导航栏后面。 (CoordinatorLayout 本身并没有延伸到BottomNavigationView 的顶部以下,但和以前一样,它会将ViewPager 布置得比它本身大。) “我不得不将 BottomNavigationView 移动为 CoordinatorLayout 的兄弟而不是孩子。” - 是的,对不起,这是我的本意,但放错了。更新了答案。您现在面临的问题是什么,我没有正确理解。如果可能,请发布您的布局设计的屏幕截图并更新问题的布局代码 我用结果的屏幕截图更新了我的答案。 你在 CoordinatorLayout 中添加了 android:layout_above="@+id/navigation" 吗?你能检查一下吗。然后寻呼机应该只到达底部导航视图的顶部 是的,这是布局的一部分。 CoordinatorLayout 本身具有正确的尺寸。它只是把它的孩子放在自己的界限之外。【参考方案3】:

这是由 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_
android:layout_
android:fitsSystemWindows="true">


<android.support.design.widget.CoordinatorLayout
    android:layout_
    android:layout_
    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_
        android:layout_
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_
            android:layout_
            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_
        android:layout_
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

<android.support.design.widget.BottomNavigationView
    android:id="@+id/navigation"
    android:layout_
    android:layout_
    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 使工具栏显示/隐藏的方式。

【讨论】:

问题是我需要 ViewPager 的内容在不滚动的情况下完全可见,所以这不起作用。我希望找到一种方法让 CoordinatorLayout 在缩小工具栏时动态更改 ViewPager 的大小。我看着开发自己的行为,但我没有看到一种方法来使用它来获得我想要的东西。 协调器并没有缩小工具栏,而是将其移出视图(超出范围)。您需要将 viewpager 上的垂直滚动转换为使 viewPager 变大的动作,以具有将工具栏推到视野之外的相同效果......不确定它是如何工作的。出于兴趣,无论工具栏是否显示,您的 ViewPager 页面/片段中的内容是否大部分都完全可见?我很好奇为什么您需要折叠工具栏的行为以及所有可见的内容。 ViewPager 的内容大部分是可见的。然而,一些片段(不是全部)需要一个浮动操作按钮或其他小部件,锚定在底部附近,必须始终可见。我不认为我可以只创建一个*** fab 并在片段之间共享它,因为 fabs 需要锚定到不同的视图,并且具有不同的外观,具体取决于 ViewPager 中当前显示的片段。 这是有道理的,我也遇到了浮动操作按钮的问题,正如您提到的,我的解决方案是***工厂(例如,在您的情况下,ViewPager 的同级,在协调器布局中)。我做了你提到的,与孩子碎片分享了这个工厂。我可以使用 fab.setImageResource(R.drawable.my_drawable) 更改工厂、不同图标等的外观,但可能会因为锚定到视图寻呼机中的视图而陷入困境! 我唯一能想到的另一件事,我还没有测试过一个可行的解决方案,那就是把 'app:layout_behavior="@string/appbar_scrolling_view_behavior" 放在不同的视图中在寻呼机片段中,并将其从 viewPager 中取出。我认为您会遇到出现在工具栏下的 ViewPager 的问题,不确定设置边距是否可以解决该问题,以及当工具栏折叠时它是否会很好...【参考方案4】:

我遇到了类似的问题,布局非常接近 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 正好延伸到 Toolbar 下方和 BottomNavigationView 上方的页面),它会移除行为并添加顶部和底部填充,分别为 _appBarLayoutViewPagerMarginTopPx 和 _appBarLayoutViewPagerMarginBottomPx 常量,易于预先计算(分别是 R.attr.actionbarSize 的像素值和 NavigationBottomView 的高度。通常都是 56dp)

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

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

【讨论】:

【参考方案5】:

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

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

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

【讨论】:

【参考方案6】:

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

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

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

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

【讨论】:

【参考方案7】:

如果你使用的是 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_
android:layout_
android:fitsSystemWindows="true"
tools:context=".MainActivity">

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

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

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

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

    <FrameLayout
        android:layout_
        android:layout_
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_
            android:layout_
            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_
    android:layout_
    android:layout_alignParentBottom="true"
    android:background="?android:attr/windowBackground"
    app:menu="@menu/bottom_nav" />

【讨论】:

以上是关于CoordinatorLayout 内容子项与 BottomNavigationView 重叠的主要内容,如果未能解决你的问题,请参考以下文章

CoordinatorLayout + AppBarLayout + NavigationDrawer

如何将 CoordinatorLayout 内容滚动到顶部?

FAB 的片段布局与 CoordinatorLayout 冲突

Android分页与滑动刷新内部CoordinatorLayout得到错误

ListView 与 CoordinatorLayout 中的 SearchView 重叠

将 BottomSheetBehavior 与内部 CoordinatorLayout 一起使用