如何在 android 布局视图中添加或替换页面
How to add or replace page in android layout view
我正在尝试使用 andoid 的 BottomNavigationView
在 Xamarin Forms 跨平台应用程序中制作类似 iOS 的选项卡式页面,方法是在 android 项目中制作平台特定的渲染器。基本上我在其中添加了一个带有 bottomnavigationview 的布局,并在渲染器中将此视图添加到根 activity,并根据表单选项卡式页面创建菜单项。现在的问题是如何将表单选项卡页面的当前页面 add/show 设置为 android 布局,并在单击栏项目时替换它。我发现即使我在渲染器中调用RemoveAllViews
,标签页中的内容页也会占据整个屏幕,甚至会覆盖底栏。因此,如果选项卡页面的任何一个内容页面可见,它将占据整个屏幕。另外 android 的 AddView
方法只接受 android 视图,不接受表单视图。
BottomNav 布局:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent"
android:layout_above="@+id/bottom_navigation"
android:orientation="vertical">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="@color/colorPrimary"
android:background="@color/colorPrimary"
app:itemIconTint="@color/white"
app:itemTextColor="@color/white"
app:menu="@menu/bottom_navigation_main" />
</RelativeLayout>
渲染器 OnElementChanged:
base.OnElementChanged(e);
TabbedPage oe = e.OldElement;
TabbedPage ne = e.NewElement;
int id = 1000;
foreach(Page p in ne.Children)
{
items.Add(new Item
{
text = p.Title,
icon = p.Icon,
page = p,
id = id++,
});
p.IsVisible = false; // without this, page show full screen and covers the bottom bar
}
RemoveAllViews();
Activity activity = this.Context as Activity;
view = activity.LayoutInflater.Inflate(Resource.Layout.BottomNav, this, false);
AddView(view);
layout = view.FindViewById<FrameLayout>(Resource.Id.rootLayout);
// add current page to layout, or replace layout with page? and How?
//
//
BottomNavigationView bottomNavigationView = (BottomNavigationView)
FindViewById(Resource.Id.bottom_navigation);
bottomNavigationView.NavigationItemSelected += BottomNavigationView_NavigationItemSelected;
Android.Support.V7.View.Menu.MenuBuilder menu = bottomNavigationView.Menu as Android.Support.V7.View.Menu.MenuBuilder;
menu.Clear();
foreach(Item i in items)
{
IMenuItem mi = menu.Add(0, i.id, 0, i.text);
mi.SetIcon(Resource.Drawable.icon);
}
编辑:添加更多代码和屏幕截图
我的预期布局是这样的,不显示来自XF tabbedpage的页面,红色的主要部分是id rootLayout
的FrameLayout
,目标是添加当前页面的tabbedpage 到 Framelayout 并显示它,而当单击 BottomNavigationView 中的项目时,切换 FrameLayout 以显示其他页面。但是这个屏幕只有通过以下方式使所有页面不可见才能实现:
foreach (Page p in e.NewElement.Children)
{
p.IsVisible = false;
}
如果不设置visible为false,子页面会占满整个屏幕:
我的测试 XF 标签页:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BottomTabbed"
x:Class="BottomTabbed.TabbedPage1"
NavigationPage.HasNavigationBar="False">
<ContentPage Title="Tab 1" BackgroundColor="Green" />
<ContentPage Title="Tab 2" BackgroundColor="Blue"/>
<ContentPage Title="Tab 3" BackgroundColor="Red"/>
<ContentPage Title="Tab 4" BackgroundColor="Gray" />
</TabbedPage>
根据您的描述,我认为您根本不需要 TabbedPage
,您只需要一个包含 BottomNavigationView
和 FrameLayout
的片段视图即可。然后你可以为此自定义一个ViewRenderer
。
首先,在PCL中创建View
的子类,例如:
public class BottomTabbedView:View
{
}
然后在android客户端项目中实现它的渲染器:
[assembly:ExportRenderer(typeof(BottomTabbedView),typeof(BottomTabbedViewRenderer))]
namespace PackageNameSpace.Droid
{
public class BottomTabbedViewRenderer:ViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if (Control == null)
{
// Instantiate the native control and assign it to the Control property with
// the SetNativeControl method
var context = Xamarin.Forms.Forms.Context;
LayoutInflater inflater = context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater;
var bottomnav_view = inflater.Inflate(Resource.Layout.bottomnav_view, this, false);
var frame = bottomnav_view.FindViewById<FrameLayout>(Resource.Id.rootLayout);
var navi = bottomnav_view.FindViewById<BottomNavigationView>(Resource.Id.bottom_navigation);
SetNativeControl(bottomnav_view);
}
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
}
if (e.NewElement != null)
{
// Configure the control and subscribe to event handlers
}
}
}
}
BottomNavigationView
和FrameLayout
的逻辑我这里没有全部写完,你应该可以实现这个ViewRenderer
里面的逻辑。该代码应与 Xamarin.Android.
的代码类似
然后你可以在XF的ContentPage
中使用这个视图作为一个普通的视图控件,例如:
<local:BottomTabbedView/>
我正在尝试使用 andoid 的 BottomNavigationView
在 Xamarin Forms 跨平台应用程序中制作类似 iOS 的选项卡式页面,方法是在 android 项目中制作平台特定的渲染器。基本上我在其中添加了一个带有 bottomnavigationview 的布局,并在渲染器中将此视图添加到根 activity,并根据表单选项卡式页面创建菜单项。现在的问题是如何将表单选项卡页面的当前页面 add/show 设置为 android 布局,并在单击栏项目时替换它。我发现即使我在渲染器中调用RemoveAllViews
,标签页中的内容页也会占据整个屏幕,甚至会覆盖底栏。因此,如果选项卡页面的任何一个内容页面可见,它将占据整个屏幕。另外 android 的 AddView
方法只接受 android 视图,不接受表单视图。
BottomNav 布局:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent"
android:layout_above="@+id/bottom_navigation"
android:orientation="vertical">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="@color/colorPrimary"
android:background="@color/colorPrimary"
app:itemIconTint="@color/white"
app:itemTextColor="@color/white"
app:menu="@menu/bottom_navigation_main" />
</RelativeLayout>
渲染器 OnElementChanged:
base.OnElementChanged(e);
TabbedPage oe = e.OldElement;
TabbedPage ne = e.NewElement;
int id = 1000;
foreach(Page p in ne.Children)
{
items.Add(new Item
{
text = p.Title,
icon = p.Icon,
page = p,
id = id++,
});
p.IsVisible = false; // without this, page show full screen and covers the bottom bar
}
RemoveAllViews();
Activity activity = this.Context as Activity;
view = activity.LayoutInflater.Inflate(Resource.Layout.BottomNav, this, false);
AddView(view);
layout = view.FindViewById<FrameLayout>(Resource.Id.rootLayout);
// add current page to layout, or replace layout with page? and How?
//
//
BottomNavigationView bottomNavigationView = (BottomNavigationView)
FindViewById(Resource.Id.bottom_navigation);
bottomNavigationView.NavigationItemSelected += BottomNavigationView_NavigationItemSelected;
Android.Support.V7.View.Menu.MenuBuilder menu = bottomNavigationView.Menu as Android.Support.V7.View.Menu.MenuBuilder;
menu.Clear();
foreach(Item i in items)
{
IMenuItem mi = menu.Add(0, i.id, 0, i.text);
mi.SetIcon(Resource.Drawable.icon);
}
编辑:添加更多代码和屏幕截图
我的预期布局是这样的,不显示来自XF tabbedpage的页面,红色的主要部分是id rootLayout
的FrameLayout
,目标是添加当前页面的tabbedpage 到 Framelayout 并显示它,而当单击 BottomNavigationView 中的项目时,切换 FrameLayout 以显示其他页面。但是这个屏幕只有通过以下方式使所有页面不可见才能实现:
foreach (Page p in e.NewElement.Children)
{
p.IsVisible = false;
}
如果不设置visible为false,子页面会占满整个屏幕:
我的测试 XF 标签页:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BottomTabbed"
x:Class="BottomTabbed.TabbedPage1"
NavigationPage.HasNavigationBar="False">
<ContentPage Title="Tab 1" BackgroundColor="Green" />
<ContentPage Title="Tab 2" BackgroundColor="Blue"/>
<ContentPage Title="Tab 3" BackgroundColor="Red"/>
<ContentPage Title="Tab 4" BackgroundColor="Gray" />
</TabbedPage>
根据您的描述,我认为您根本不需要 TabbedPage
,您只需要一个包含 BottomNavigationView
和 FrameLayout
的片段视图即可。然后你可以为此自定义一个ViewRenderer
。
首先,在PCL中创建View
的子类,例如:
public class BottomTabbedView:View
{
}
然后在android客户端项目中实现它的渲染器:
[assembly:ExportRenderer(typeof(BottomTabbedView),typeof(BottomTabbedViewRenderer))]
namespace PackageNameSpace.Droid
{
public class BottomTabbedViewRenderer:ViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if (Control == null)
{
// Instantiate the native control and assign it to the Control property with
// the SetNativeControl method
var context = Xamarin.Forms.Forms.Context;
LayoutInflater inflater = context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater;
var bottomnav_view = inflater.Inflate(Resource.Layout.bottomnav_view, this, false);
var frame = bottomnav_view.FindViewById<FrameLayout>(Resource.Id.rootLayout);
var navi = bottomnav_view.FindViewById<BottomNavigationView>(Resource.Id.bottom_navigation);
SetNativeControl(bottomnav_view);
}
if (e.OldElement != null)
{
// Unsubscribe from event handlers and cleanup any resources
}
if (e.NewElement != null)
{
// Configure the control and subscribe to event handlers
}
}
}
}
BottomNavigationView
和FrameLayout
的逻辑我这里没有全部写完,你应该可以实现这个ViewRenderer
里面的逻辑。该代码应与 Xamarin.Android.
然后你可以在XF的ContentPage
中使用这个视图作为一个普通的视图控件,例如:
<local:BottomTabbedView/>