Share via


Xamarin.Forms em Xamarin Native Projects

Normalmente, um Xamarin.Forms aplicativo inclui uma ou mais páginas derivadas do , e essas páginas são compartilhadas por todas as plataformas em um projeto de ContentPagebiblioteca do .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 fato de o projeto nativo consumir ContentPagepáginas derivadas de um projeto de biblioteca do .NET Standard ou 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 com x:Name e referenciadas a partir do code-behind. Para obter mais informações sobre modos de exibição nativos, consulte Modos de exibição nativos.

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 ContentPagepágina -derived e quaisquer dependências ao projeto nativo.
  3. Chame o método Forms.Init .
  4. Construa uma instância da ContentPagepágina derivada e 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 usando a API de ContentPagenavegaçã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 em seu fluxo de aplicativo – ele pode ser executado na inicialização do aplicativo ou pouco antes da página derivada ContentPageser construída. 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á razão 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 formulários nativos, Xamarin.Forms recursos como DependencyService, MessagingCentere o mecanismo de vinculação de dados, todos ainda funcionam. Porém, a navegação de página deve ser executada usando a API de navegação nativa.

iOS

No iOS, a FinishedLaunchingAppDelegate substituição na classe normalmente é o local para executar tarefas relacionadas à inicialização do 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 um ResourceDictionary que é definido em XAML.
  • Uma referência à AppDelegate classe é armazenada no staticInstance 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 visualizações em aplicativos iOS nativos, é criado.
  • A FolderPath propriedade é inicializada para um caminho no dispositivo onde 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 método de CreateViewController extensão.
  • A Title propriedade do UIViewController é definida, que será exibida no UINavigationBar.
  • A AppNavigationController é criado para gerenciar a navegação hierárquica. Esta é 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 é carregado.
  • O AppNavigationController objeto é definido como o nível UIViewController superior para o UIWindow, e o UIWindow é definido como a janela de chave para o aplicativo e é tornado 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 esteja definida como o Application objeto.

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

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

O staticAppDelegate.Instance campo permite que o AppDelegate.NavigateToNoteEntryPage método a ser chamado, 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 método de CreateViewController extensão e define a Title propriedade do UIViewController. O UIViewController é então empurrado AppNavigationControllerPushViewController 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 ecrã mostra uma Entrada de Nota num dispositivo móvel.

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

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 Page ficará órfãoUIViewController.

Importante

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

Android

No Android, a OnCreateMainActivity 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 um ResourceDictionary que é definido em XAML.
  • Uma referência à MainActivity classe é armazenada no staticInstance 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 Toolbare 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 onde 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 método de CreateSupportFragment extensão.
  • A SupportFragmentManager classe cria e confirma uma transação que substitui a FrameLayout instância pela 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 do Notes em um dispositivo móvel com um banner azul e um texto de anotação colorido.

Importante

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

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

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

O staticMainActivity.Instance campo permite que o MainActivity.NavigateToNoteEntryPage método a ser chamado, 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 método de CreateSupportFragment extensão e adiciona o à pilha de fragmento Fragment de volta. 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 anotação em um dispositivo móvel com um banner azul.

Quando o NoteEntryPage for exibido, tocar na seta para trás abrirá o Fragment para a NoteEntryPage pilha traseira do fragmento, retornando o usuário para a NotesPageFragment classe.

Ativar o suporte à navegação de volta

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 esse 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 traseira de fragmentos. A resposta ao tocar no botão Voltar é manipulada OnOptionsItemSelected pela 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 exibe o fragmento atual da pilha traseira do fragmento, desde que o botão Voltar tenha sido selecionado e haja uma ou mais Fragment instâncias na pilha traseira do fragmento.

Múltiplas atividades

Quando um aplicativo é composto de várias atividades, ContentPageas páginas derivadas podem ser incorporadas em cada uma das atividades. Nesse cenário, o Forms.Init método precisa ser chamado somente na substituição do primeiro Activity que incorpora OnCreate um Xamarin.FormsContentPagearquivo . 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 chamado Forms.Init método.

Escolher um arquivo

Ao incorporar uma ContentPagepágina derivada que usa um WebView que precisa oferecer suporte a um botão HTML "Escolher arquivo", o Activity será necessário 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 à MainPage classe é armazenada no staticInstance 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 onde 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 método de CreateFrameworkElement 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 ecrã mostra uma página de notas com notas e data/horas.

Importante

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

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

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

O staticMainPage.Instance campo permite que o MainPage.NavigateToNoteEntryPage método a ser chamado, 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 método de Frame.Navigate extensão que usa uma ContentPageinstância de página derivada. Portanto, quando o NavigateToNoteEntryPage método é executado, a interface do usuário definida no Xamarin.FormsNoteEntryPage será exibida, conforme mostrado na captura de tela a seguir:

A captura de ecrã mostra uma página de notas com uma caixa de texto com uma nota introduzida.

Quando o NoteEntryPage for exibido, tocar na seta para trás abrirá o FrameworkElement para a NoteEntryPage pilha traseira do aplicativo, retornando o usuário para a FrameworkElementNotesPage classe.

Habilitar suporte a 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 ActualHeight propriedades ou são ActualWidth alteradas no Frame. Em resposta, o conteúdo da página ativa é redimensionado Xamarin.Forms chamando o Layout método.

Ativar o suporte à navegação de volta

Na UWP, os aplicativos devem habilitar a navegação de volta para todos os botões Voltar de hardware e software, em diferentes fatores de 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 ao modo de exibição atual e, em seguida, registra um manipulador de eventos para o BackRequested evento. O aplicativo só recebe esse evento se for o aplicativo em primeiro plano e, em resposta, chama 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 para true marcar o evento como manipulado. A falha em marcar o evento como manipulado pode resultar no evento ser ignorado.

O aplicativo escolhe se deseja mostrar um botão Voltar na barra de título. Isso é obtido definindo a AppViewBackButtonVisibility propriedade para 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 disparo Navigated de eventos, atualiza a visibilidade do botão Voltar da barra de título quando a navegação da página ocorre. Isso garante que o botão Voltar da barra de título fique visível se a pilha traseira no aplicativo não estiver vazia ou removido da barra de título se a pilha traseira no aplicativo estiver vazia.

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