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 ContentPage
biblioteca do .NET Standard ou projeto compartilhado. No entanto, o Native Forms permite que ContentPage
pá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 ContentPage
pá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.FormsContentPage
página derivada em um projeto nativo é o seguinte:
- Adicione o Xamarin.Forms pacote NuGet ao projeto nativo.
- Adicione a
ContentPage
página -derived e quaisquer dependências ao projeto nativo. - Chame o método
Forms.Init
. - Construa uma instância da
ContentPage
página derivada e converta-a no tipo nativo apropriado usando um dos seguintes métodos de extensão:CreateViewController
para iOS,CreateSupportFragment
para Android ouCreateFrameworkElement
para UWP. - Navegue até a representação de tipo nativo da página derivada usando a API de
ContentPage
navegação nativa.
Xamarin.Forms deve ser inicializado chamando o Forms.Init
método antes que um projeto nativo possa construir uma ContentPage
pá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 ContentPage
ser 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 ContentPage
páginas derivadas. No entanto, não há razão para que os projetos nativos não possam consumir ContentPage
páginas derivadas de um projeto de biblioteca do .NET Standard ou Projeto Compartilhado.
Ao usar formulários nativos, Xamarin.Forms recursos como DependencyService
, MessagingCenter
e 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 FinishedLaunching
AppDelegate
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 umResourceDictionary
que é definido em XAML. - Uma referência à
AppDelegate
classe é armazenada nostatic
Instance
campo. Isso é para fornecer um mecanismo para que outras classes chamem métodos definidos naAppDelegate
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.FormsContentPage
página derivada definida em XAML, e seu pai é definido como o objeto criadoXamarin.Forms.Application
anteriormente. - O
NotesPage
objeto é convertido em umUIViewController
usando o método deCreateViewController
extensão. - A
Title
propriedade doUIViewController
é definida, que será exibida noUINavigationBar
. - A
AppNavigationController
é criado para gerenciar a navegação hierárquica. Esta é uma classe de controlador de navegação personalizada, que deriva deUINavigationController
. OAppNavigationController
objeto gerencia uma pilha de controladores de exibição, e oUIViewController
passado para o construtor será apresentado inicialmente quando oAppNavigationController
é carregado. - O
AppNavigationController
objeto é definido como o nívelUIViewController
superior para oUIWindow
, e oUIWindow
é definido como a janela de chave para o aplicativo e é tornado visível. - A
Parent
propriedade do objeto é definida comonull
, para evitar um vazamento deNotesPage
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:
Importante
Todas as ContentPage
páginas derivadas podem consumir recursos definidos no nível ResourceDictionary
do 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 static
AppDelegate.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.FormsContentPage
em um UIViewController
com o método de CreateViewController
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:
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 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 umResourceDictionary
que é definido em XAML. - Uma referência à
MainActivity
classe é armazenada nostatic
Instance
campo. Isso é para fornecer um mecanismo para que outras classes chamem métodos definidos naMainActivity
classe. - O
Activity
conteúdo é definido a partir de um recurso de layout. No aplicativo de exemplo, o layout consiste em umLinearLayout
que contém umToolbar
e umFrameLayout
para atuar como um contêiner de fragmento. - O
Toolbar
é recuperado e definido como a barra de ação para oActivity
, 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.FormsContentPage
página derivada definida em XAML, e seu pai é definido como o objeto criadoXamarin.Forms.Application
anteriormente. - O
NotesPage
objeto é convertido em umFragment
usando o método deCreateSupportFragment
extensão. - A
SupportFragmentManager
classe cria e confirma uma transação que substitui aFrameLayout
instância pelaFragment
para aNotesPage
classe. - A
Parent
propriedade do objeto é definida comonull
, para evitar um vazamento deNotesPage
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:
Importante
Todas as ContentPage
páginas derivadas podem consumir recursos definidos no nível ResourceDictionary
do 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 static
MainActivity.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.FormsContentPage
em 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:
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 NotesPage
Fragment
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, ContentPage
as 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.FormsContentPage
arquivo . No entanto, isso tem o seguinte impacto:
- O valor de
Xamarin.Forms.Color.Accent
será retirado doActivity
chamadoForms.Init
método. - O valor de
Xamarin.Forms.Application.Current
será associado aoActivity
chamadoForms.Init
método.
Escolher um arquivo
Ao incorporar uma ContentPage
pá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.FormsContentPage
pá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 nostatic
Instance
campo. Isso é para fornecer um mecanismo para que outras classes chamem métodos definidos naMainPage
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.FormsContentPage
página derivada definida em XAML, e seu pai é definido como o objeto criadoXamarin.Forms.Application
anteriormente. - O
NotesPage
objeto é convertido em umFrameworkElement
usando o método deCreateFrameworkElement
extensão e, em seguida, definido como o conteúdo daMainPage
classe. - A
Parent
propriedade do objeto é definida comonull
, para evitar um vazamento deNotesPage
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:
Importante
Todas as ContentPage
páginas derivadas podem consumir recursos definidos no nível ResourceDictionary
do 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 static
MainPage.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 ContentPage
instâ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:
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 FrameworkElement
NotesPage
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.