我正在尝试将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_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
将ViewPager
放置到屏幕底部,因此底部被BottomNavigationView
遮挡,如下所示:
即使CoordinatorLayout
本身到目前为止没有延伸,也会发生这种情况:
我尝试将app:layout_insetEdge="bottom"
添加到BottomNavigationView
,app:layout_dodgeInsetEdges="bottom"
添加到ViewPager
,但这有一个不同的问题:它会移动{{1}的底部向上,但它保持相同的高度,所以顶部现在被切断:
我尝试了另外两个实验。首先,我尝试从ViewPager
中移除BottomNavigationView
并将其作为垂直CoordinatorLayout
下的兄弟姐妹。其次,我将LinearLayout
和ViewPager
放在BottomNavigationView
下,希望它们能够正确布局。两者都没有帮助:在第一种情况下,LinearLayout
仍然相对于整个屏幕调整了CoordinatorLayout
的大小,要么将其中的一部分隐藏在ViewPager
后面,要么将其从顶部切掉。在第二种情况下,用户需要滚动才能看到BottomNavigationView
。
如何正确布局?
P.S。当我尝试the layout suggested by @Anoop S S(将BottomNavigationView
和CoordinatorLayout
作为兄弟姐妹放在BottomNavigationView
下)时,我得到以下内容(RelativeLayout
仍在后面延伸ViewPager
):
与以前一样,BottomNavigationView
本身只会延伸到CoordinatorView
的顶部。
答案 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" />