我正在尝试在 Xamarin 表单中创建底部选项卡式页面,并且我正在为 Android 执行此操作。
我在这里使用最新版本的 Xamarin Forms。
我的示例底部选项卡页面如下所示。
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:SampleTabbedPage.Views"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.ToolbarPlacement="Bottom"
android:TabbedPage.BarItemColor="Gray"
android:TabbedPage.BarSelectedItemColor="Blue"
mc:Ignorable="d"
x:Class="SampleTabbedPage.Views.SampleTabbed">
<!--Pages can be added as references or inline-->
<NavigationPage
Title="Main"
NavigationPage.HasNavigationBar="False">
<x:Arguments>
<views:SampleDetailsPage/>
</x:Arguments>
</NavigationPage>
<ContentPage Title="Tab 1" />
<ContentPage Title="Tab 2" />
</TabbedPage>
Run Code Online (Sandbox Code Playgroud)
我的示例详细信息页面如下
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SampleTabbedPage.Views.SampleDetailsPage"
BackgroundColor="Gray">
<ContentPage.Content>
<StackLayout>
<ListView BackgroundColor="White" ItemTapped="ListView_ItemTapped">
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Mango</x:String>
<x:String>Banana</x:String>
<x:String>Pinaple</x:String>
<x:String>Apple</x:String>
<x:String>Avacado</x:String>
<x:String>Coconut</x:String>
<x:String>Dragan Fruit</x:String>
<x:String>Pomaganate</x:String>
<x:String>Wood Apple</x:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Run Code Online (Sandbox Code Playgroud)
我正在做的导航如下
在 App.xamal.cs 中
MainPage = new NavigationPage(new FirstPage());
Run Code Online (Sandbox Code Playgroud)
我有简单的第一页
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SampleTabbedPage.Views.FirstPage">
<ContentPage.Content>
<StackLayout>
<Button Text="Click Me!" Clicked="Button_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Run Code Online (Sandbox Code Playgroud)
在按钮单击事件中,我导航到“示例选项卡式页面”
await Navigation.PushAsync(new SampleTabbed());
Run Code Online (Sandbox Code Playgroud)
这工作正常。但存在性能问题。
如果我运行此程序并单击首页中的按钮,则需要3 秒才能加载选项卡式页面。
如果我删除示例详细信息页面中的列表视图,则需要2 秒才能加载。
但如果我只添加一个内容页面来单击事件,则加载只需要几毫秒。
我在实现选项卡式页面时做错了什么吗?
或者
有什么方法可以提高选项卡式页面的加载性能。
解决方案是让大页面以惰性方式加载其内容,仅当其选项卡被选中时。这样,由于创建 TabbedPage 时这些页面现在是空的,因此导航到 TabbedPage 突然变得非常快!
1.为 TabbedPage 页面创建一个行为,称为ActivePageTabbedPageBehavior。
class ActivePageTabbedPageBehavior : Behavior<TabbedPage>
{
protected override void OnAttachedTo(TabbedPage tabbedPage)
{
base.OnAttachedTo(tabbedPage);
tabbedPage.CurrentPageChanged += OnTabbedPageCurrentPageChanged;
}
protected override void OnDetachingFrom(TabbedPage tabbedPage)
{
base.OnDetachingFrom(tabbedPage);
tabbedPage.CurrentPageChanged -= OnTabbedPageCurrentPageChanged;
}
private void OnTabbedPageCurrentPageChanged(object sender, EventArgs e)
{
var tabbedPage = (TabbedPage)sender;
// Deactivate previously selected page
IActiveAware prevActiveAwarePage = tabbedPage.Children.OfType<IActiveAware>()
.FirstOrDefault(c => c.IsActive && tabbedPage.CurrentPage != c);
if (prevActiveAwarePage != null)
{
prevActiveAwarePage.IsActive = false;
}
// Activate selected page
if (tabbedPage.CurrentPage is IActiveAware activeAwarePage)
{
activeAwarePage.IsActive = true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
2.定义IActiveAware接口
interface IActiveAware
{
bool IsActive { get; set; }
event EventHandler IsActiveChanged;
}
Run Code Online (Sandbox Code Playgroud)
3.创建一个名为LoadContentOnActivateBehavior的基本通用抽象类
abstract class LoadContentOnActivateBehavior<TActivateAwareElement> : Behavior<TActivateAwareElement>
where TActivateAwareElement : VisualElement
{
public DataTemplate ContentTemplate { get; set; }
protected override void OnAttachedTo(TActivateAwareElement element)
{
base.OnAttachedTo(element);
(element as IActiveAware).IsActiveChanged += OnIsActiveChanged;
}
protected override void OnDetachingFrom(TActivateAwareElement element)
{
(element as IActiveAware).IsActiveChanged -= OnIsActiveChanged;
base.OnDetachingFrom(element);
}
void OnIsActiveChanged(object sender, EventArgs e)
{
var element = (TActivateAwareElement)sender;
element.Behaviors.Remove(this);
SetContent(element, (View)ContentTemplate.CreateContent());
}
protected abstract void SetContent(TActivateAwareElement element, View contentView);
}
Run Code Online (Sandbox Code Playgroud)
4.专门的LazyContentPageBehavior
class LazyContentPageBehavior : LoadContentOnActivateBehavior<ContentView>
{
protected override void SetContent(ContentView element, View contentView)
{
element.Content = contentView;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以像这样在xaml中使用:
<TabbedPage>
<TabbedPage.Behaviors>
<local:ActivePageTabbedPageBehavior />
</TabbedPage.Behaviors>
<ContentPage Title="First tab">
<Label Text="First tab layout" />
</ContentPage>
<local:LazyLoadedContentPage Title="Second tab">
<ContentPage.Behaviors>
<local:LazyContentPageBehavior ContentTemplate="{StaticResource ContentTemplate}" />
</ContentPage.Behaviors>
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="ContentTemplate">
<!-- Complex and slow to render layout -->
<local:SlowContentView />
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
</local:LazyLoadedContentPage>
</TabbedPage>
Run Code Online (Sandbox Code Playgroud)
我们将 ContentPage 复杂布局移至 DataTemplate。
这是可识别激活的自定义LazyLoadedContentPage页面:
class LazyLoadedContentPage : ContentPage, IActiveAware
{
public event EventHandler IsActiveChanged;
bool _isActive;
public bool IsActive
{
get => _isActive;
set
{
if (_isActive != value)
{
_isActive = value;
IsActiveChanged?.Invoke(this, EventArgs.Empty);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
SlowContentView 做一些复杂的事情
public partial class SlowContentView : ContentView
{
public SlowContentView()
{
InitializeComponent();
// Simulating a complex view
...
}
}
Run Code Online (Sandbox Code Playgroud)