ebk
ebk

Reputation: 335

tabbed page causing more time to load the application

I am trying develop a xamarin app which has tabbed pages.

I have 3 main tabs.Each page viewmodel contructor has 3-5 Api calls.So its taking more time(20s) to load my app(for opening).

mainpage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage  xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Myapplication.Views.MenuPage"
             xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             xmlns:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True" 
              xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
             xmlns:views="clr-namespace:Dyocense.Views"
             android:TabbedPage.ToolbarPlacement="Bottom"
             android:TabbedPage.IsSwipePagingEnabled="False"
             >

        <views:A Title="A" Icon="dsjsdsd_18dp.png" ></views:A>
        <views:B Title="B" Icon="askjasa.png"></views:B>
        <views:C Title="C" Icon="abc.png"></views:C>
        <views:D Title="D" Icon="abc.png"></views:D>


</TabbedPage>

How to load only first tab(A) detail page on app loading and rest of the pages on tab changing.

Upvotes: 0

Views: 1697

Answers (2)

Alessandro Lallo
Alessandro Lallo

Reputation: 761

As a workaround I created a new class extending the Xamarin.Forms.TabbedPage and I send a message every time one of the tabs is clicked (or in general is diplayed)

public enum TabbedPages
{
    MyPage1 = 0,
    MyPage2 = 1,
    MyPage3 = 2,

}

public class BottomBarPage : Xamarin.Forms.TabbedPage
{

    protected override void OnCurrentPageChanged()
    {
        base.OnCurrentPageChanged();

        var newCurrentPage = (TabbedPages)Children.IndexOf(CurrentPage);

        MessagingCenter.Send<Xamarin.Forms.TabbedPage>(this, newCurrentPage.ToString("g"));

    }
}

and then on the view models used for each page loaded when clicking on the tab I subscribe to the message and call my APIs

public class MyPage2ViewModel
{
    public MyPage2ViewModel()
    {
        MessagingCenter.Subscribe<TabbedPage>(this, TabbedPages.MyPage2 .ToString("g"), async (obj) =>
        {
            //API call
        });
    }
}

Upvotes: 0

Leo Zhu
Leo Zhu

Reputation: 15001

A solution is to make the heavy pages load their content in a lazy manner, only when their tab becomes selected. This way, since these pages are now empty when TabbedPage is created, navigating to the TabbedPage suddenly becomes very fast!

1.create a behavior for the TabbedPage page, called 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;
    }
  }
}

2.define IActiveAware interface

interface IActiveAware
  {
    bool IsActive { get; set; }
    event EventHandler IsActiveChanged;
  }

3.create a base generic abstract class called 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);
}

4.the specialized LazyContentPageBehavior

class LazyContentPageBehavior : LoadContentOnActivateBehavior<ContentView>
 {
   protected override void SetContent(ContentView element, View contentView)
    {
      element.Content = contentView;
    }
 }

then we can use in xaml like this:

<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>

we moved the ContentPage complex layout to become a DataTemplate.

Here's the custom LazyLoadedContentPage page which is activation aware:

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);
        }
      }
   }
 }

SlowContentView do some complex things

 public partial class SlowContentView : ContentView
{
    public SlowContentView()
    {
        InitializeComponent();

        // Simulating a complex view
        ...
    }
}

you could refer to the link

Upvotes: 1

Related Questions