Tratamento de rotação
Este tópico descreve como lidar com alterações de orientação do dispositivo no Xamarin.Android. Ele aborda como trabalhar com o sistema de recursos Android para carregar automaticamente recursos para uma orientação de dispositivo específica, bem como como lidar programaticamente com alterações de orientação.
Visão geral
Como os dispositivos móveis são facilmente rotacionados, a rotação integrada é um recurso padrão em sistemas operacionais móveis. O Android fornece uma estrutura sofisticada para lidar com a rotação dentro de aplicativos, quer a interface do usuário seja criada declarativamente em XML ou programaticamente em código. Ao lidar automaticamente com alterações de layout declarativas em um dispositivo rotacionado, um aplicativo pode se beneficiar da forte integração com o sistema de recursos Android. Para o layout programático, as alterações devem ser tratadas manualmente. Isso permite um controle mais fino em tempo de execução, mas às custas de mais trabalho para o desenvolvedor. Um aplicativo também pode optar por desativar a reinicialização da atividade e assumir o controle manual das alterações de orientação.
Este guia examina os seguintes tópicos de orientação:
Rotação de layout declarativa – Como usar o sistema de recursos do Android para criar aplicativos com reconhecimento de orientação, incluindo como carregar layouts e desenhos para orientações específicas.
Rotação de layout programático – Como adicionar controles programaticamente, bem como lidar com alterações de orientação manualmente.
Manipulando a rotação declarativamente com layouts
Ao incluir arquivos em pastas que seguem convenções de nomenclatura, o Android carrega automaticamente os arquivos apropriados quando a orientação muda. Isso inclui suporte para:
Recursos de layout – Especificando quais arquivos de layout são inflados para cada orientação.
Recursos Desenháveis – Especificando quais sacáveis são carregados para cada orientação.
Recursos de layout
Por padrão, os arquivos XML (AXML) do Android incluídos na pasta Recursos/layout são usados para renderizar exibições de uma Atividade. Os recursos dessa pasta são usados para orientação retrato e paisagem se nenhum recurso de layout adicional for fornecido especificamente para paisagem. Considere a estrutura do projeto criada pelo modelo de projeto padrão:
Este projeto cria um único arquivo Main.axml na pasta Resources/layout . Quando o método Activity OnCreate
é chamado, ele infla a exibição definida em Main.axml, que declara um botão conforme mostrado no XML abaixo:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/myButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"/>
</LinearLayout>
Se o dispositivo for girado para a orientação paisagem, o método Activity OnCreate
será chamado novamente e o mesmo arquivo Main.axml será inflado, conforme mostrado na captura de tela abaixo:
Layouts específicos de orientação
Além da pasta de layout (que tem como padrão retrato e também pode ser explicitamente nomeada como layout-port incluindo uma pasta chamada layout-land
), um aplicativo pode definir as exibições necessárias quando estiver em paisagem sem alterações de código.
Suponha que o arquivo Main.axml contivesse o seguinte XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="This is portrait"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
</RelativeLayout>
Se uma pasta chamada layout-land que contém um arquivo Main.axml adicional for adicionada ao projeto, inflar o layout quando estiver em paisagem agora resultará no carregamento do Main.axml recém-adicionado pelo Android. Considere a versão paisagem do arquivo Main.axml que contém o código a seguir (para simplificar, esse XML é semelhante à versão retrato padrão do código, mas usa uma cadeia de caracteres diferente no TextView
):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="This is landscape"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
</RelativeLayout>
Executar esse código e girar o dispositivo de retrato para paisagem demonstra o novo carregamento XML, conforme mostrado abaixo:
Recursos Sacáveis
Durante a rotação, o Android trata os recursos desenháveis de forma semelhante aos recursos de layout. Nesse caso, o sistema obtém os sacáveis das pastas Resources/drawable e Resources/drawable-land , respectivamente.
Por exemplo, digamos que o projeto inclua uma imagem chamada Monkey.png na pasta Resources/drawable , onde o desenho é referenciado a partir de um ImageView
XML como este:
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/monkey"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
Vamos supor ainda que uma versão diferente do Monkey.png esteja incluída em Recursos/drawable-land. Assim como nos arquivos de layout, quando o dispositivo é girado, o desenho muda para a orientação dada, como mostrado abaixo:
Manipulando a rotação programaticamente
Às vezes, definimos layouts em código. Isso pode acontecer por uma variedade de razões, incluindo limitações técnicas, preferência do desenvolvedor, etc. Quando adicionamos controles programaticamente, um aplicativo deve contabilizar manualmente a orientação do dispositivo, que é manipulada automaticamente quando usamos recursos XML.
Adicionando controles no código
Para adicionar controles programaticamente, um aplicativo precisa executar as seguintes etapas:
- Crie um layout.
- Definir parâmetros de layout.
- Criar controles.
- Definir parâmetros de layout de controle.
- Adicione controles ao layout.
- Defina o layout como o modo de exibição de conteúdo.
Por exemplo, considere uma interface do usuário que consiste em um único TextView
controle adicionado a um RelativeLayout
, conforme mostrado no código a seguir.
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// create a layout
var rl = new RelativeLayout (this);
// set layout parameters
var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
rl.LayoutParameters = layoutParams;
// create TextView control
var tv = new TextView (this);
// set TextView's LayoutParameters
tv.LayoutParameters = layoutParams;
tv.Text = "Programmatic layout";
// add TextView to the layout
rl.AddView (tv);
// set the layout as the content view
SetContentView (rl);
}
Esse código cria uma instância de uma RelativeLayout
classe e define sua LayoutParameters
propriedade. A LayoutParams
classe é a maneira do Android encapsular como os controles são posicionados de forma reutilizável. Depois que uma instância de um layout é criada, os controles podem ser criados e adicionados a ele. Os controles também têm LayoutParameters
, como o TextView
neste exemplo. Depois que o TextView
for criado, adicioná-lo ao RelativeLayout
e definir o RelativeLayout
como o modo de exibição de conteúdo resulta no aplicativo exibindo o TextView
como mostrado:
Detectando orientação no código
Se um aplicativo tentar carregar uma interface de usuário diferente para cada orientação quando OnCreate
for chamada (isso acontecerá sempre que um dispositivo for girado), ele deverá detectar a orientação e, em seguida, carregar o código de interface do usuário desejado. O Android tem uma classe chamada WindowManager
, que pode ser usada para determinar a rotação atual do dispositivo através da WindowManager.DefaultDisplay.Rotation
propriedade, como mostrado abaixo:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// create a layout
var rl = new RelativeLayout (this);
// set layout parameters
var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
rl.LayoutParameters = layoutParams;
// get the initial orientation
var surfaceOrientation = WindowManager.DefaultDisplay.Rotation;
// create layout based upon orientation
RelativeLayout.LayoutParams tvLayoutParams;
if (surfaceOrientation == SurfaceOrientation.Rotation0 || surfaceOrientation == SurfaceOrientation.Rotation180) {
tvLayoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
} else {
tvLayoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
tvLayoutParams.LeftMargin = 100;
tvLayoutParams.TopMargin = 100;
}
// create TextView control
var tv = new TextView (this);
tv.LayoutParameters = tvLayoutParams;
tv.Text = "Programmatic layout";
// add TextView to the layout
rl.AddView (tv);
// set the layout as the content view
SetContentView (rl);
}
Este código define o para ser posicionado TextView
100 pixels a partir do canto superior esquerdo da tela, animando automaticamente para o novo layout, quando girado para paisagem, como mostrado aqui:
Impedindo a reinicialização da atividade
Além de lidar com tudo no OnCreate
, um aplicativo também pode impedir que uma Atividade seja reiniciada quando a orientação for alterada, definindo ConfigurationChanges
o ActivityAttribute
seguinte:
[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
Agora, quando o dispositivo é girado, a atividade não é reiniciada. Para manipular manualmente a mudança de orientação nesse caso, uma Atividade pode substituir o OnConfigurationChanged
método e determinar a Configuration
orientação do objeto que é passado, como na nova implementação da Atividade abaixo:
[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
public class CodeLayoutActivity : Activity
{
TextView _tv;
RelativeLayout.LayoutParams _layoutParamsPortrait;
RelativeLayout.LayoutParams _layoutParamsLandscape;
protected override void OnCreate (Bundle bundle)
{
// create a layout
// set layout parameters
// get the initial orientation
// create portrait and landscape layout for the TextView
_layoutParamsPortrait = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
_layoutParamsLandscape = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
_layoutParamsLandscape.LeftMargin = 100;
_layoutParamsLandscape.TopMargin = 100;
_tv = new TextView (this);
if (surfaceOrientation == SurfaceOrientation.Rotation0 || surfaceOrientation == SurfaceOrientation.Rotation180) {
_tv.LayoutParameters = _layoutParamsPortrait;
} else {
_tv.LayoutParameters = _layoutParamsLandscape;
}
_tv.Text = "Programmatic layout";
rl.AddView (_tv);
SetContentView (rl);
}
public override void OnConfigurationChanged (Android.Content.Res.Configuration newConfig)
{
base.OnConfigurationChanged (newConfig);
if (newConfig.Orientation == Android.Content.Res.Orientation.Portrait) {
_tv.LayoutParameters = _layoutParamsPortrait;
_tv.Text = "Changed to portrait";
} else if (newConfig.Orientation == Android.Content.Res.Orientation.Landscape) {
_tv.LayoutParameters = _layoutParamsLandscape;
_tv.Text = "Changed to landscape";
}
}
}
Aqui, os parâmetros de TextView's
layout são inicializados para paisagem e retrato. As variáveis de classe mantêm os parâmetros, juntamente com o TextView
próprio, uma vez que a Atividade não será recriada quando a orientação for alterada. O código ainda usa o surfaceOrientartion
in OnCreate
para definir o layout inicial para o TextView
. Depois disso, OnConfigurationChanged
lida com todas as alterações de layout subsequentes.
Quando executamos o aplicativo, o Android carrega as alterações da interface do usuário conforme a rotação do dispositivo ocorre e não reinicia a atividade.
Impedindo a reinicialização da atividade para layouts declarativos
As reinicializações de atividades causadas pela rotação do dispositivo também podem ser evitadas se definirmos o layout em XML. Por exemplo, podemos usar essa abordagem se quisermos impedir uma reinicialização de atividade (por motivos de desempenho, talvez) e não precisarmos carregar novos recursos para orientações diferentes.
Para fazer isso, seguimos o mesmo procedimento que usamos com um layout programático. Simplesmente definido ConfigurationChanges
no ActivityAttribute
, como fizemos no CodeLayoutActivity
anterior. Qualquer código que precise ser executado para a mudança de orientação pode ser implementado novamente no OnConfigurationChanged
método.
Mantendo o estado durante as alterações de orientação
Seja manipulando a rotação declarativa ou programaticamente, todos os aplicativos Android devem implementar as mesmas técnicas para gerenciar o estado quando a orientação do dispositivo muda. O gerenciamento de estado é importante porque o sistema reinicia uma atividade em execução quando um dispositivo Android é girado. O Android faz isso para facilitar o carregamento de recursos alternativos, como layouts e desenhos, projetados especificamente para uma orientação específica. Quando é reiniciada, a Atividade perde qualquer estado transitório que possa ter armazenado em variáveis de classe locais. Portanto, se uma atividade for dependente do estado, ela deverá manter seu estado no nível do aplicativo. Um aplicativo precisa lidar com o salvamento e a restauração de qualquer estado de aplicativo que ele deseja preservar em todas as alterações de orientação.
Para obter mais informações sobre o estado persistente no Android, consulte o guia Ciclo de Vida da atividade .
Resumo
Este artigo abordou como usar os recursos internos do Android para trabalhar com rotação. Primeiro, explicou como usar o sistema de recursos do Android para criar aplicativos com reconhecimento de orientação. Em seguida, ele apresentou como adicionar controles no código, bem como lidar com alterações de orientação manualmente.