CoordinatorLayout 内容子项与 BottomNavigationView 重叠
Posted
技术标签:
【中文标题】CoordinatorLayout 内容子项与 BottomNavigationView 重叠【英文标题】:CoordinatorLayout content child overlaps BottomNavigationView 【发布时间】:2018-06-03 16:21:53 【问题描述】:我正在尝试将CoordinatorLayout
与BottomNavigationView
、AppBarLayout
和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_
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>
问题是CoordinatorLayout
把ViewPager
放到屏幕底部,所以底部被BottomNavigationView
遮住了,像这样:
即使CoordinatorLayout
本身并没有向下延伸,也会发生这种情况:
我尝试将app:layout_insetEdge="bottom"
添加到BottomNavigationView
和app:layout_dodgeInsetEdges="bottom"
到ViewPager
,但这有一个不同的问题:它将ViewPager
的底部向上移动,但它保持相同的高度,所以顶部现在被切掉了:
我尝试了另外两个实验。首先,我尝试从CoordinatorLayout
中删除BottomNavigationView
,并使它们成为垂直LinearLayout
下的兄弟姐妹。其次,我将ViewPager
和BottomNavigationView
放在LinearLayout
下,希望它们的布局正确。两者都没有帮助:在第一种情况下,CoordinatorLayout
仍然相对于整个屏幕调整ViewPager
的大小,要么将部分隐藏在BottomNavigationView
后面,要么切掉顶部。在第二种情况下,用户需要滚动才能看到BottomNavigationView
。
如何正确布局?
附:当我尝试the layout suggested by @Anoop S S (将CoordinatorLayout
和BottomNavigationView
作为兄弟姐妹放在RelativeLayout
下)时,我得到以下信息(ViewPager
仍在BottomNavigationView
后面向下延伸):
和以前一样,CoordinatorView
本身仅向下延伸到BottomNavigationView
的顶部。
【问题讨论】:
please check the answer here it is working fine here 我创建了一个关于这个issuetracker.google.com/issues/116541304的问题 【参考方案1】:我想出了一个不同的方法(虽然还没有经过实战测试):
我对@987654321@ 进行了子类化,以根据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_height
的CoordinatorLayout
设置为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得到错误