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 ContentPage
biblioteca .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 consumo de páginas derivadas do projeto ContentPage
nativo 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.FormsContentPage
página derivada em um projeto nativo é o seguinte:
- Adicione o Xamarin.Forms pacote NuGet ao projeto nativo.
- Adicione a página -derivada
ContentPage
e quaisquer dependências ao projeto nativo. - Chame o método
Forms.Init
. - Construa uma instância da página derivada
ContentPage
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
ContentPage
usando 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 ContentPage
pá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 ContentPage
pá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 ContentPage
páginas derivadas. No entanto, não há motivo 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 o Native Forms, Xamarin.Forms recursos como DependencyService
, MessagingCenter
e 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 definidoResourceDictionary
em XAML. - Uma referência à classe é armazenada
AppDelegate
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 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.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 oCreateViewController
método de extensão. - A
Title
propriedade doUIViewController
é definida, que será exibida noUINavigationBar
. - A
AppNavigationController
é criado para gerenciar a navegação hierárquica. Essa é 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
for carregado. - O
AppNavigationController
objeto é definido como o nívelUIViewController
superior para oUIWindow
, e éUIWindow
definido como a janela de chave para o aplicativo e fica 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 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.FormsContentPage
em 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:
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 definidoResourceDictionary
em XAML. - Uma referência à classe é armazenada
MainActivity
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 em que 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 oCreateSupportFragment
método de extensão. - A
SupportFragmentManager
classe cria e confirma uma transação que substitui aFrameLayout
instância peloFragment
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 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.FormsContentPage
em 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:
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, ContentPage
as 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 doActivity
chamadoForms.Init
método. - O valor de
Xamarin.Forms.Application.Current
será associado aoActivity
que chamou oForms.Init
método.
Escolher um arquivo
Ao incorporar uma ContentPage
pá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.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 à classe é armazenada
MainPage
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 em que 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 oCreateFrameworkElement
método de 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 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 ContentPage
instâ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:
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.