Partilhar via


Xamarin.Forms em Projetos Nativos do Xamarin

Normalmente, um Xamarin.Forms aplicativo inclui uma ou mais páginas derivadas de , e essas páginas são compartilhadas por todas as plataformas em um projeto de ContentPagebiblioteca .NET Standard ou Projeto Compartilhado. No entanto, o Native Forms permite que ContentPagepáginas derivadas sejam adicionadas diretamente a aplicativos nativos Xamarin.iOS, Xamarin.Android e UWP. Em comparação com o consumo de páginas derivadas do projeto ContentPagenativo de um projeto de biblioteca do .NET Standard ou de um Projeto Compartilhado, a vantagem de adicionar páginas diretamente a projetos nativos é que as páginas podem ser estendidas com exibições nativas. As exibições nativas podem ser nomeadas em XAML e x:Name referenciadas no code-behind. Para obter mais informações sobre exibições nativas, consulte Exibições nativas.

O processo para consumir uma Xamarin.FormsContentPagepágina derivada em um projeto nativo é o seguinte:

  1. Adicione o Xamarin.Forms pacote NuGet ao projeto nativo.
  2. Adicione a página -derivada ContentPagee quaisquer dependências ao projeto nativo.
  3. Chame o método Forms.Init .
  4. Construa uma instância da página derivada ContentPagee converta-a no tipo nativo apropriado usando um dos seguintes métodos de extensão: CreateViewController para iOS, CreateSupportFragment para Android ou CreateFrameworkElement para UWP.
  5. Navegue até a representação de tipo nativo da página derivada ContentPageusando a API de navegação nativa.

Xamarin.Forms deve ser inicializado chamando o Forms.Init método antes que um projeto nativo possa construir uma ContentPagepágina derivada. A escolha de quando fazer isso depende principalmente de quando é mais conveniente no fluxo do aplicativo – ele pode ser executado na inicialização do aplicativo ou pouco antes da construção da ContentPagepágina derivada. Neste artigo e nos aplicativos de exemplo que o acompanham, o método é chamado na inicialização do Forms.Init aplicativo.

Observação

A solução de aplicativo de exemplo NativeForms não contém nenhum Xamarin.Forms projeto. Em vez disso, ele consiste em um projeto Xamarin.iOS, um projeto Xamarin.Android e um projeto UWP. Cada projeto é um projeto nativo que usa formulários nativos para consumir ContentPagepáginas derivadas. No entanto, não há motivo para que os projetos nativos não possam consumir ContentPagepáginas derivadas de um projeto de biblioteca do .NET Standard ou Projeto Compartilhado.

Ao usar o Native Forms, Xamarin.Forms recursos como DependencyService, MessagingCentere o mecanismo de vinculação de dados ainda funcionam. Porém, a navegação de página deve ser executada usando a API de navegação nativa.

iOS

No iOS, a FinishedLaunching substituição na classe normalmente é o local para executar tarefas relacionadas à inicialização do AppDelegate aplicativo. Ele é chamado depois que o aplicativo é iniciado e geralmente é substituído para configurar a janela principal e o controlador de exibição. O exemplo de código a seguir mostra a AppDelegate classe no aplicativo de exemplo:

[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{
    public static AppDelegate Instance;
    UIWindow _window;
    AppNavigationController _navigation;

    public static string FolderPath { get; private set; }

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        Forms.Init();

        // Create app-level resource dictionary.
        Xamarin.Forms.Application.Current = new Xamarin.Forms.Application();
        Xamarin.Forms.Application.Current.Resources = new MyDictionary();

        Instance = this;
        _window = new UIWindow(UIScreen.MainScreen.Bounds);

        UINavigationBar.Appearance.SetTitleTextAttributes(new UITextAttributes
        {
            TextColor = UIColor.Black
        });

        FolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));

        NotesPage notesPage = new NotesPage()
        {
            // Set the parent so that the app-level resource dictionary can be located.
            Parent = Xamarin.Forms.Application.Current
        };

        UIViewController notesPageController = notesPage.CreateViewController();
        notesPageController.Title = "Notes";

        _navigation = new AppNavigationController(notesPageController);

        _window.RootViewController = _navigation;
        _window.MakeKeyAndVisible();

        notesPage.Parent = null;
        return true;
    }
    // ...
}

Esse métodoFinishedLaunching executa as seguintes tarefas:

  • Xamarin.Forms é inicializado chamando o Forms.Init método.
  • Um novo Xamarin.Forms.Application objeto is é criado e seu dicionário de recursos no nível do aplicativo é definido como definido ResourceDictionary em XAML.
  • Uma referência à classe é armazenada AppDelegate no static Instance campo. Isso é para fornecer um mecanismo para que outras classes chamem métodos definidos na AppDelegate classe.
  • O UIWindow, que é o contêiner principal para exibições em aplicativos iOS nativos, é criado.
  • A FolderPath propriedade é inicializada para um caminho no dispositivo em que os dados da nota serão armazenados.
  • Um NotesPage objeto é criado, que é uma Xamarin.FormsContentPagepágina derivada definida em XAML, e seu pai é definido como o objeto criado Xamarin.Forms.Application anteriormente.
  • O NotesPage objeto é convertido em um UIViewController usando o CreateViewController método de extensão.
  • A Title propriedade do UIViewController é definida, que será exibida no UINavigationBar.
  • A AppNavigationController é criado para gerenciar a navegação hierárquica. Essa é uma classe de controlador de navegação personalizada, que deriva de UINavigationController. O AppNavigationController objeto gerencia uma pilha de controladores de exibição e o UIViewController passado para o construtor será apresentado inicialmente quando o AppNavigationController for carregado.
  • O AppNavigationController objeto é definido como o nível UIViewController superior para o UIWindow, e é UIWindow definido como a janela de chave para o aplicativo e fica visível.
  • A Parent propriedade do objeto é definida como null, para evitar um vazamento de NotesPage memória.

Depois que o FinishedLaunching método for executado, a interface do usuário definida na Xamarin.FormsNotesPage classe será exibida, conforme mostrado na captura de tela a seguir:

A captura de tela mostra uma tela do Notes em um dispositivo móvel.

Importante

Todas as ContentPagepáginas derivadas podem consumir recursos definidos no nível ResourceDictionarydo aplicativo, desde que a Parent propriedade da página seja definida como o Application objeto.

Interagir com a interface do usuário, por exemplo, tocando no + Button, resultará no seguinte manipulador de eventos na execução code-behind NotesPage :

void OnNoteAddedClicked(object sender, EventArgs e)
{
    AppDelegate.Instance.NavigateToNoteEntryPage(new Note());
}

O static AppDelegate.Instance campo permite que o AppDelegate.NavigateToNoteEntryPage método seja invocado, o que é mostrado no exemplo de código a seguir:

public void NavigateToNoteEntryPage(Note note)
{
    NoteEntryPage noteEntryPage = new NoteEntryPage
    {
        BindingContext = note,
        // Set the parent so that the app-level resource dictionary can be located.
        Parent = Xamarin.Forms.Application.Current
    };

    var noteEntryViewController = noteEntryPage.CreateViewController();
    noteEntryViewController.Title = "Note Entry";

    _navigation.PushViewController(noteEntryViewController, true);
    noteEntryPage.Parent = null;
}

O NavigateToNoteEntryPage método converte a página derivada Xamarin.FormsContentPageem um UIViewController com o CreateViewController método de extensão e define a Title propriedade do UIViewController. O UIViewController é então empurrado AppNavigationController PushViewController pelo método. Portanto, a interface do usuário definida na Xamarin.FormsNoteEntryPage classe será exibida, conforme mostrado na captura de tela a seguir:

A captura de tela mostra uma entrada de nota em um dispositivo móvel.

Quando o for exibido, a NoteEntryPage navegação regressiva exibirá o UIViewController para a NoteEntryPage classe do AppNavigationController, retornando o usuário para o UIViewController para a NotesPage classe. No entanto, remover um UIViewController da pilha de navegação nativa do iOS não descarta automaticamente o UIViewController objeto e anexado Page . Portanto, a AppNavigationController classe substitui o PopViewController método para descartar controladores de exibição na navegação regressiva:

public class AppNavigationController : UINavigationController
{
    //...
    public override UIViewController PopViewController(bool animated)
    {
        UIViewController topView = TopViewController;
        if (topView != null)
        {
            // Dispose of ViewController on back navigation.
            topView.Dispose();
        }
        return base.PopViewController(animated);
    }
}

A PopViewController substituição chama o Dispose método no UIViewController objeto que foi retirado da pilha de navegação nativa do iOS. Se isso não for feito, o objeto anexado UIViewController Page ficará órfão.

Importante

Objetos órfãos não podem ser coletados como lixo e, portanto, resultam em um vazamento de memória.

Android

No Android, a OnCreate MainActivity substituição na classe normalmente é o local para executar tarefas relacionadas à inicialização do aplicativo. O exemplo de código a seguir mostra a MainActivity classe no aplicativo de exemplo:

public class MainActivity : AppCompatActivity
{
    public static string FolderPath { get; private set; }

    public static MainActivity Instance;

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        Forms.Init(this, bundle);

        // Create app-level resource dictionary.
        Xamarin.Forms.Application.Current = new Xamarin.Forms.Application();
        Xamarin.Forms.Application.Current.Resources = new MyDictionary();

        Instance = this;

        SetContentView(Resource.Layout.Main);
        var toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);
        SetSupportActionBar(toolbar);
        SupportActionBar.Title = "Notes";

        FolderPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData));

        NotesPage notesPage = new NotesPage()
        {
            // Set the parent so that the app-level resource dictionary can be located.
            Parent = Xamarin.Forms.Application.Current
        };
        AndroidX.Fragment.App.Fragment notesPageFragment = notesPage.CreateSupportFragment(this);

        SupportFragmentManager
            .BeginTransaction()
            .Replace(Resource.Id.fragment_frame_layout, mainPage)
            .Commit();
        //...

        notesPage.Parent = null;
    }
    ...
}

Esse métodoOnCreate executa as seguintes tarefas:

  • Xamarin.Forms é inicializado chamando o Forms.Init método.
  • Um novo Xamarin.Forms.Application objeto is é criado e seu dicionário de recursos no nível do aplicativo é definido como definido ResourceDictionary em XAML.
  • Uma referência à classe é armazenada MainActivity no static Instance campo. Isso é para fornecer um mecanismo para que outras classes chamem métodos definidos na MainActivity classe.
  • O Activity conteúdo é definido a partir de um recurso de layout. No aplicativo de exemplo, o layout consiste em um LinearLayout que contém um Toolbar, e um FrameLayout para atuar como um contêiner de fragmento.
  • O Toolbar é recuperado e definido como a barra de ação para o Activity, e o título da barra de ação é definido.
  • A FolderPath propriedade é inicializada para um caminho no dispositivo em que os dados da nota serão armazenados.
  • Um NotesPage objeto é criado, que é uma Xamarin.FormsContentPagepágina derivada definida em XAML, e seu pai é definido como o objeto criado Xamarin.Forms.Application anteriormente.
  • O NotesPage objeto é convertido em um Fragment usando o CreateSupportFragment método de extensão.
  • A SupportFragmentManager classe cria e confirma uma transação que substitui a FrameLayout instância pelo Fragment para a NotesPage classe.
  • A Parent propriedade do objeto é definida como null, para evitar um vazamento de NotesPage memória.

Para obter mais informações sobre fragmentos, consulte Fragmentos.

Depois que o OnCreate método for executado, a interface do usuário definida na Xamarin.FormsNotesPage classe será exibida, conforme mostrado na captura de tela a seguir:

A captura de tela mostra uma tela de notas em um dispositivo móvel com um banner azul e texto de nota colorido.

Importante

Todas as ContentPagepáginas derivadas podem consumir recursos definidos no nível ResourceDictionarydo aplicativo, desde que a Parent propriedade da página seja definida como o Application objeto.

Interagir com a interface do usuário, por exemplo, tocando no + Button, resultará no seguinte manipulador de eventos na execução code-behind NotesPage :

void OnNoteAddedClicked(object sender, EventArgs e)
{
    MainActivity.Instance.NavigateToNoteEntryPage(new Note());
}

O static MainActivity.Instance campo permite que o MainActivity.NavigateToNoteEntryPage método seja invocado, o que é mostrado no exemplo de código a seguir:

public void NavigateToNoteEntryPage(Note note)
{
    NoteEntryPage noteEntryPage = new NoteEntryPage
    {
        BindingContext = note,
        // Set the parent so that the app-level resource dictionary can be located.
        Parent = Xamarin.Forms.Application.Current
    };

    AndroidX.Fragment.App.Fragment noteEntryFragment = noteEntryPage.CreateSupportFragment(this);
    SupportFragmentManager
        .BeginTransaction()
        .AddToBackStack(null)
        .Replace(Resource.Id.fragment_frame_layout, noteEntryFragment)
        .Commit();

    noteEntryPage.Parent = null;
}

O NavigateToNoteEntryPage método converte a página derivada Xamarin.FormsContentPageem um Fragment com o CreateSupportFragment método de extensão e adiciona a Fragment à pilha de retorno do fragmento. Portanto, a interface do usuário definida no Xamarin.FormsNoteEntryPage será exibida, conforme mostrado na captura de tela a seguir:

A captura de tela mostra uma entrada de nota em um dispositivo móvel com um banner azul.

Quando o NoteEntryPage for exibido, tocar na seta para trás exibirá o Fragment para a NoteEntryPage pilha de retorno do fragmento, retornando o usuário para o Fragment para a NotesPage classe.

Ativar suporte à navegação regressiva

A SupportFragmentManager classe tem um BackStackChanged evento que é acionado sempre que o conteúdo da pilha de retorno do fragmento é alterado. O OnCreate método na MainActivity classe contém um manipulador de eventos anônimo para este evento:

SupportFragmentManager.BackStackChanged += (sender, e) =>
{
    bool hasBack = SupportFragmentManager.BackStackEntryCount > 0;
    SupportActionBar.SetHomeButtonEnabled(hasBack);
    SupportActionBar.SetDisplayHomeAsUpEnabled(hasBack);
    SupportActionBar.Title = hasBack ? "Note Entry" : "Notes";
};

Esse manipulador de eventos exibe um botão Voltar na barra de ações, desde que haja uma ou mais Fragment instâncias na pilha de retorno do fragmento. A resposta ao toque no botão Voltar é tratada pela OnOptionsItemSelected substituição:

public override bool OnOptionsItemSelected(Android.Views.IMenuItem item)
{
    if (item.ItemId == global::Android.Resource.Id.Home && SupportFragmentManager.BackStackEntryCount > 0)
    {
        SupportFragmentManager.PopBackStack();
        return true;
    }
    return base.OnOptionsItemSelected(item);
}

A OnOptionsItemSelected substituição é chamada sempre que um item no menu de opções é selecionado. Essa implementação remove o fragmento atual da pilha de retorno do fragmento, desde que o botão Voltar tenha sido selecionado e haja uma ou mais Fragment instâncias na pilha de retorno do fragmento.

Múltiplas atividades

Quando um aplicativo é composto de várias atividades, ContentPageas páginas derivadas de -podem ser incorporadas em cada uma das atividades. Nesse cenário, o Forms.Init método precisa ser chamado apenas na OnCreate substituição do primeiro Activity que incorpora um Xamarin.FormsContentPage. No entanto, isso tem o seguinte impacto:

  • O valor de Xamarin.Forms.Color.Accent será retirado do Activity chamado Forms.Init método.
  • O valor de Xamarin.Forms.Application.Current será associado ao Activity que chamou o Forms.Init método.

Escolher um arquivo

Ao incorporar uma ContentPagepágina derivada que usa um WebView que precisa suportar um botão HTML "Escolher arquivo", o Activity precisará substituir o OnActivityResult método:

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    ActivityResultCallbackRegistry.InvokeCallback(requestCode, resultCode, data);
}

UWP

Na UWP, a classe nativa App normalmente é o local para executar tarefas relacionadas à inicialização do aplicativo. Xamarin.Forms geralmente é inicializado, em Xamarin.Forms aplicativos UWP, na OnLaunched substituição na classe nativa App , para passar o LaunchActivatedEventArgs argumento para o Forms.Init método. Por esse motivo, os aplicativos UWP nativos que consomem uma Xamarin.FormsContentPagepágina derivada podem chamar mais facilmente o Forms.Init método do App.OnLaunched método:

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    // ...
    Xamarin.Forms.Forms.Init(e);

    // Create app-level resource dictionary.
    Xamarin.Forms.Application.Current = new Xamarin.Forms.Application();
    Xamarin.Forms.Application.Current.Resources = new MyDictionary();

    // ...
}

Além disso, o OnLaunched método também pode criar qualquer dicionário de recursos no nível do aplicativo exigido pelo aplicativo.

Por padrão, a classe nativa App inicia a MainPage classe como a primeira página do aplicativo. O exemplo de código a seguir mostra a MainPage classe no aplicativo de exemplo:

public sealed partial class MainPage : Page
{
    NotesPage notesPage;
    NoteEntryPage noteEntryPage;

    public static MainPage Instance;
    public static string FolderPath { get; private set; }

    public MainPage()
    {
        this.NavigationCacheMode = NavigationCacheMode.Enabled;
        Instance = this;
        FolderPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData));

        notesPage = new Notes.UWP.Views.NotesPage
        {
            // Set the parent so that the app-level resource dictionary can be located.
            Parent = Xamarin.Forms.Application.Current
        };
        this.Content = notesPage.CreateFrameworkElement();
        // ...
        notesPage.Parent = null;    
    }
    // ...
}

O MainPage construtor executa as seguintes tarefas:

  • O cache é habilitado para a página, para que um novo MainPage não seja construído quando um usuário navega de volta para a página.
  • Uma referência à classe é armazenada MainPage no static Instance campo. Isso é para fornecer um mecanismo para que outras classes chamem métodos definidos na MainPage classe.
  • A FolderPath propriedade é inicializada para um caminho no dispositivo em que os dados da nota serão armazenados.
  • Um NotesPage objeto é criado, que é uma Xamarin.FormsContentPagepágina derivada definida em XAML, e seu pai é definido como o objeto criado Xamarin.Forms.Application anteriormente.
  • O NotesPage objeto é convertido em um FrameworkElement usando o CreateFrameworkElement método de extensão e, em seguida, definido como o conteúdo da MainPage classe.
  • A Parent propriedade do objeto é definida como null, para evitar um vazamento de NotesPage memória.

Depois que o MainPage construtor for executado, a interface do usuário definida na Xamarin.FormsNotesPage classe será exibida, conforme mostrado na captura de tela a seguir:

A captura de tela mostra uma página de notas com notas e data/hora.

Importante

Todas as ContentPagepáginas derivadas podem consumir recursos definidos no nível ResourceDictionarydo aplicativo, desde que a Parent propriedade da página seja definida como o Application objeto.

Interagir com a interface do usuário, por exemplo, tocando no + Button, resultará no seguinte manipulador de eventos na execução code-behind NotesPage :

void OnNoteAddedClicked(object sender, EventArgs e)
{
    MainPage.Instance.NavigateToNoteEntryPage(new Note());
}

O static MainPage.Instance campo permite que o MainPage.NavigateToNoteEntryPage método seja invocado, o que é mostrado no exemplo de código a seguir:

public void NavigateToNoteEntryPage(Note note)
{
    noteEntryPage = new Notes.UWP.Views.NoteEntryPage
    {
        BindingContext = note,
        // Set the parent so that the app-level resource dictionary can be located.
        Parent = Xamarin.Forms.Application.Current
    };
    this.Frame.Navigate(noteEntryPage);
    noteEntryPage.Parent = null;
}

A navegação na UWP normalmente é executada com o Frame.Navigate método, que usa um Page argumento. Xamarin.Forms Define um Frame.Navigate método de extensão que usa uma ContentPageinstância de página derivada. Portanto, quando o NavigateToNoteEntryPage método for executado, a interface do usuário definida no Xamarin.FormsNoteEntryPage será exibida, conforme mostrado na captura de tela a seguir:

A captura de tela mostra uma página de anotações com uma caixa de texto com uma anotação inserida.

Quando o NoteEntryPage for exibido, tocar na seta para trás exibirá o FrameworkElement para a NoteEntryPage pilha de retorno no aplicativo, retornando o usuário para o FrameworkElement para a NotesPage classe.

Habilitar suporte ao redimensionamento de página

Quando a janela do aplicativo UWP é redimensionada, o Xamarin.Forms conteúdo também deve ser redimensionado. Isso é feito registrando um manipulador de eventos para o Loaded evento, no MainPage construtor:

public MainPage()
{
    // ...
    this.Loaded += OnMainPageLoaded;
    // ...
}

O Loaded evento é acionado quando a página é disposta, renderizada e pronta para interação e executa o OnMainPageLoaded método em resposta:

void OnMainPageLoaded(object sender, RoutedEventArgs e)
{
    this.Frame.SizeChanged += (o, args) =>
    {
        if (noteEntryPage != null)
            noteEntryPage.Layout(new Xamarin.Forms.Rectangle(0, 0, args.NewSize.Width, args.NewSize.Height));
        else
            notesPage.Layout(new Xamarin.Forms.Rectangle(0, 0, args.NewSize.Width, args.NewSize.Height));
    };
}

O OnMainPageLoaded método registra um manipulador de eventos anônimo para o Frame.SizeChanged evento, que é gerado quando as propriedades ou ActualHeight as ActualWidth propriedades são alteradas no Frame. Em resposta, o Xamarin.Forms conteúdo da página ativa é redimensionado chamando o Layout método.

Ativar suporte à navegação regressiva

Na UWP, os aplicativos devem habilitar a navegação de volta para todos os botões Voltar de hardware e software, em diferentes fatores forma de dispositivo. Isso pode ser feito registrando um manipulador de eventos para o BackRequested evento, que pode ser executado no MainPage construtor:

public MainPage()
{
    // ...
    SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}

Quando o aplicativo é iniciado, o GetForCurrentView método recupera o SystemNavigationManager objeto associado à exibição atual e, em seguida, registra um manipulador de eventos para o BackRequested evento. O aplicativo só receberá esse evento se for o aplicativo em primeiro plano e, em resposta, chamar o manipulador de OnBackRequested eventos:

void OnBackRequested(object sender, BackRequestedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;
    if (rootFrame.CanGoBack)
    {
        e.Handled = true;
        rootFrame.GoBack();
        noteEntryPage = null;
    }
}

O OnBackRequested manipulador de eventos chama o GoBack método no quadro raiz do aplicativo e define a BackRequestedEventArgs.Handled propriedade como true para marcar o evento como manipulado. A falha em marcar o evento como manipulado pode resultar na ignorância do evento.

O aplicativo escolhe se deseja mostrar um botão Voltar na barra de título. Isso é feito definindo a AppViewBackButtonVisibility propriedade como um dos valores de AppViewBackButtonVisibility enumeração, na App classe:

void OnNavigated(object sender, NavigationEventArgs e)
{
    SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
        ((Frame)sender).CanGoBack ? AppViewBackButtonVisibility.Visible : AppViewBackButtonVisibility.Collapsed;
}

O OnNavigated manipulador de eventos, que é executado em resposta ao acionamento do Navigated evento, atualiza a visibilidade do botão Voltar da barra de título quando ocorre a navegação na página. Isso garante que o botão Voltar da barra de título fique visível se a pilha de retorno no aplicativo não estiver vazia ou removido da barra de título se a pilha de retorno no aplicativo estiver vazia.

Para obter mais informações sobre o suporte à navegação regressiva na UWP, consulte Histórico de navegação e navegação regressiva para aplicativos UWP.