Xamarin.Forms 如何移除NavigationPage.TitleView侧边及上下多余空白
你遇到的左侧已修复、上下残留蓝色空白、硬设高度又看不到内容的问题,和Xamarin高度计算逻辑无关,是三个可复现的配置缺失叠加导致的:
- 早期参考的方案适配的是老版Xamarin.Support库,你当前项目已经迁移到AndroidX,直接复制老代码会出现
Android.Support.V7命名空间不存在的报错 - 配置Toolbar.xml时只清空了左右方向的内容偏移,遗漏了上下方向的默认内边距,这些带colorPrimary蓝色背景的内边距区域,就是你看到的多余空白
- Xamarin.Forms自带的TitleView渲染器会在承载TitleView的Toolbar上自动叠加固定上下边距,不会随你StackLayout的Margin/Padding设为0自动消失,最终导致StackLayout撑不满容器、漏出背景色
落地解决步骤
1. 修正老代码的编译报错
所有之前引用Android.Support.V7.Widget.Toolbar的代码,全部替换为AndroidX对应的命名空间,原报错直接解决:
// 替换原报错行的引用 var toolbar = this.FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.toolbar);
注意:如果你的Toolbar.xml仍保留android.support.v7.widget.Toolbar作为根元素,要同步替换为androidx.appcompat.widget.Toolbar,不然系统根本找不到对应的控件,所有边距配置都不会生效。这段Toolbar实例的获取代码,要放在MainActivity的OnCreate方法里、LoadApplication调用之后执行,不要放在普通页面的生命周期里。
2. 补全Toolbar.xml的全方向零边距配置
你之前的配置只覆盖了左右方向的inset,补上遗漏的上下方向配置,完整的Toolbar.xml如下:
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:popupTheme="@style/ThemeOverlay.AppCompat.Light" <!-- 已配置的左右方向零偏移 --> android:contentInsetLeft="0dp" android:contentInsetRight="0dp" android:contentInsetStart="0dp" android:contentInsetEnd="0dp" android:contentInsetStartWithNavigation="0dp" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:contentInsetRight="0dp" app:contentInsetEnd="0dp" <!-- 补上遗漏的上下方向零内边距,这是上下蓝色空白的核心来源 --> android:paddingTop="0dp" android:paddingBottom="0dp" app:contentInsetTop="0dp" app:contentInsetBottom="0dp" <!-- 保留系统默认最小高度,避免工具栏过短导致触摸响应异常 --> android:minHeight="?attr/actionBarSize" />
3. 自定义NavigationPage渲染器,强制移除系统叠加的边距
很多人配完Toolbar.xml仍残留上下空白,是因为Xamarin.Forms的默认渲染器会在布局完成后重新给Toolbar加内边距,需要通过自定义渲染器拦截这个逻辑:
- 在Android项目下创建自定义NavigationPage渲染器,替换系统默认实现
- 在布局完成的回调里,手动清空Toolbar所有方向的内边距
[assembly: ExportRenderer(typeof(Xamarin.Forms.NavigationPage), typeof(CustomNavigationPageRenderer))] namespace YourApp.Droid.Renderers { public class CustomNavigationPageRenderer : Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer { public CustomNavigationPageRenderer(Context context) : base(context) { } protected override void OnLayout(bool changed, int l, int t, int r, int b) { base.OnLayout(changed, l, t, r, b); var toolbar = this.FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.toolbar); if (toolbar != null) { // 强制清空全方向内边距,阻止系统重新叠加偏移 toolbar.SetPadding(0, 0, 0, 0); toolbar.SetContentInsetsAbsolute(0, 0); toolbar.SetContentInsetsRelative(0, 0); } } } }
4. 修正TitleView的StackLayout配置,避免硬设超大高度
你之前的两种StackLayout配置都存在缺陷:
- 单纯设
VerticalOptions="Fill"但容器本身有内边距时,StackLayout根本撑不满剩余空间,会漏出Toolbar的蓝色背景 - 硬设
HeightRequest="1000"会让StackLayout溢出Toolbar的承载范围,超出部分被系统裁剪,自然看不到内部内容
正确的配置要让StackLayout的高度和Toolbar的实际高度完全匹配,不要设无意义的超大数值:
<NavigationPage.TitleView> <StackLayout Style="{DynamicResource ToolbarBkGrndColorStyle}" Orientation="Horizontal" Margin="0" Spacing="0" Padding="0" HorizontalOptions="Fill" VerticalOptions="FillAndExpand" HeightRequest="{OnPlatform Android={StaticResource ActionBarSize}, iOS=44}" > <!-- 内部原有布局逻辑保持不变即可 --> </StackLayout> </NavigationPage.TitleView>
其中ActionBarSize可以在App.xaml的资源字典里定义为56dp(Android端系统导航栏的标准高度),不需要写死超大数值,既能保证StackLayout刚好填满Toolbar区域、没有上下空隙,也不会出现内容溢出被裁剪的问题。
注意:如果你的项目开启了沉浸式/透明状态栏模式,需要在渲染器里把状态栏的高度补偿到Toolbar的顶部偏移量上,不要直接把Toolbar的最小高度压到0,不然会出现TitleView被状态栏遮挡的问题。
内容的提问来源于stack exchange,提问作者Belight

