Gestione della rotazione

Questo argomento descrive come gestire le modifiche dell'orientamento del dispositivo in Xamarin.Android. Illustra come usare il sistema di risorse Android per caricare automaticamente le risorse per un particolare orientamento del dispositivo e come gestire a livello di codice le modifiche dell'orientamento.

Panoramica

Poiché i dispositivi mobili sono facilmente ruotati, la rotazione predefinita è una funzionalità standard nei sistemi operativi mobili. Android offre un framework sofisticato per gestire la rotazione all'interno delle applicazioni, indipendentemente dal fatto che l'interfaccia utente venga creata in modo dichiarativo in XML o a livello di codice. Quando si gestiscono automaticamente le modifiche di layout dichiarativo in un dispositivo ruotato, un'applicazione può trarre vantaggio dalla stretta integrazione con il sistema di risorse Android. Per il layout a livello di codice, le modifiche devono essere gestite manualmente. Ciò consente un controllo più dettagliato in fase di esecuzione, ma a scapito di un maggiore lavoro per lo sviluppatore. Un'applicazione può anche scegliere di rifiutare esplicitamente il riavvio dell'attività e di assumere il controllo manuale delle modifiche di orientamento.

In questa guida vengono esaminati gli argomenti di orientamento seguenti:

  • Rotazione del layout dichiarativo: come usare il sistema di risorse Android per creare applicazioni con riconoscimento dell'orientamento, tra cui come caricare sia layout che disegnare per determinati orientamenti.

  • Rotazione layout a livello di codice: come aggiungere controlli a livello di codice e come gestire manualmente le modifiche di orientamento.

Gestione della rotazione in modo dichiarativo con layout

Includendo i file nelle cartelle che seguono le convenzioni di denominazione, Android carica automaticamente i file appropriati quando cambia l'orientamento. È incluso il supporto per:

  • Risorse layout: specificare i file di layout gonfiati per ogni orientamento.

  • Risorse disegnabili: specificando quali elementi di disegno vengono caricati per ogni orientamento.

Risorse layout

Per impostazione predefinita, i file XML Android (AXML) inclusi nella cartella Resources/layout vengono usati per il rendering delle visualizzazioni per un'attività. Le risorse di questa cartella vengono usate sia per l'orientamento verticale che orizzontale se non vengono fornite risorse di layout aggiuntive specificamente per il panorama orizzontale. Si consideri la struttura del progetto creata dal modello di progetto predefinito:

Default project template structure

Questo progetto crea un singolo file Main.axml nella cartella Resources/layout . Quando viene chiamato il metodo dell'attività OnCreate , viene gonfiata la visualizzazione definita in Main.axml, che dichiara un pulsante come illustrato nel codice XML seguente:

<?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 il dispositivo viene ruotato sull'orientamento orizzontale, viene chiamato di nuovo il metodo dell'attività OnCreate e lo stesso file Main.axml viene gonfiato, come illustrato nello screenshot seguente:

Same screen but in landscape orientation

Layout specifici dell'orientamento

Oltre alla cartella di layout (che per impostazione predefinita è verticale e può anche essere denominata in modo esplicito layout-port includendo una cartella denominata layout-land), un'applicazione può definire le visualizzazioni necessarie quando in orizzontale senza modifiche al codice.

Si supponga che il file Main.axml contenga il codice XML seguente:

<?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 al progetto viene aggiunta una cartella denominata layout-land che contiene un file Main.axml aggiuntivo, l'aumento del layout quando in orizzontale comporta ora il caricamento di Android del file Main.axml appena aggiunto. Si consideri la versione orizzontale del file Main.axml che contiene il codice seguente (per semplicità, questo codice XML è simile alla versione verticale predefinita del codice, ma usa una stringa diversa in 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>

L'esecuzione di questo codice e la rotazione del dispositivo da verticale a orizzontale illustra il nuovo caricamento XML, come illustrato di seguito:

Portrait and landscape screenshots printing the portrait mode

Risorse disegnabili

Durante la rotazione, Android gestisce risorse disegnabili in modo analogo alle risorse di layout. In questo caso, il sistema ottiene i drawable rispettivamente dalle cartelle Risorse/disegnabili e Risorse/terreni disegnabili.

Si supponga, ad esempio, che il progetto includa un'immagine denominata Monkey.png nella cartella Resources/drawable , in cui viene fatto riferimento all'oggetto drawable da un oggetto ImageView in FORMATO XML simile al seguente:

<ImageView
  android:layout_height="wrap_content"
  android:layout_width="wrap_content"
  android:src="@drawable/monkey"
  android:layout_centerVertical="true"
  android:layout_centerHorizontal="true" />

Si supponga inoltre che una versione diversa di Monkey.png sia inclusa in Risorse/terreno di disegno. Proprio come con i file di layout, quando il dispositivo viene ruotato, le modifiche disegnabili per l'orientamento specificato, come illustrato di seguito:

Different version of Monkey.png shown in portrait and landscape modes

Gestione della rotazione a livello di codice

A volte definiamo i layout nel codice. Ciò può verificarsi per diversi motivi, tra cui limitazioni tecniche, preferenze per gli sviluppatori e così via. Quando si aggiungono controlli a livello di codice, un'applicazione deve tenere conto manualmente dell'orientamento del dispositivo, che viene gestito automaticamente quando si usano risorse XML.

Aggiunta di controlli nel codice

Per aggiungere controlli a livello di codice, un'applicazione deve eseguire la procedura seguente:

  • Creare un layout.
  • Impostare i parametri di layout.
  • Creare controlli.
  • Impostare i parametri del layout del controllo.
  • Aggiungere controlli al layout.
  • Impostare il layout come visualizzazione contenuto.

Si consideri, ad esempio, un'interfaccia utente costituita da un singolo TextView controllo aggiunto a un RelativeLayoutoggetto , come illustrato nel codice seguente.

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);
}

Questo codice crea un'istanza di una RelativeLayout classe e ne imposta la LayoutParameters proprietà. La LayoutParams classe è il modo di incapsulare il modo in cui i controlli vengono posizionati in modo riutilizzabile. Dopo aver creato un'istanza di un layout, i controlli possono essere creati e aggiunti. I controlli hanno LayoutParametersanche , ad esempio TextView in questo esempio. Dopo aver creato l'oggetto TextView , aggiungerlo a RelativeLayout e impostando RelativeLayout come risultato della visualizzazione contenuto nell'applicazione viene visualizzato TextView come illustrato:

Increment counter button shown in both portrait and landscape modes

Rilevamento dell'orientamento nel codice

Se un'applicazione tenta di caricare un'interfaccia utente diversa per ogni orientamento quando OnCreate viene chiamato (questo avviene ogni volta che un dispositivo viene ruotato), deve rilevare l'orientamento e quindi caricare il codice dell'interfaccia utente desiderato. Android ha una classe denominata WindowManager, che può essere usata per determinare la rotazione del dispositivo corrente tramite la WindowManager.DefaultDisplay.Rotation proprietà , come illustrato di seguito:

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);
}

Questo codice imposta l'oggetto TextView da posizionare 100 pixel dall'alto a sinistra dello schermo, animando automaticamente il nuovo layout, quando ruotato in orizzontale, come illustrato di seguito:

View state is preserved across portrait and landscape modes

Prevenzione del riavvio delle attività

Oltre a gestire tutti gli elementi in OnCreate, un'applicazione può anche impedire il riavvio di un'attività quando l'orientamento cambia impostando ConfigurationChanges in ActivityAttribute come indicato di seguito:

[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]

Ora, quando il dispositivo viene ruotato, l'attività non viene riavviata. Per gestire manualmente la modifica dell'orientamento in questo caso, un'attività può eseguire l'override del OnConfigurationChanged metodo e determinare l'orientamento dell'oggetto Configuration passato, come nella nuova implementazione dell'attività seguente:

[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";
    }
  }
}

Qui i parametri di layout vengono inizializzati sia per orizzontale TextView's che per verticale. Le variabili di classe contengono i parametri, insieme a TextView se stessa, perché l'attività non verrà ricreata quando cambia l'orientamento. Il codice usa ancora in surfaceOrientartionOnCreate per impostare il layout iniziale per .TextView Successivamente, OnConfigurationChanged gestisce tutte le modifiche di layout successive.

Quando si esegue l'applicazione, Android carica le modifiche dell'interfaccia utente man mano che si verifica la rotazione del dispositivo e non riavvia l'attività.

Prevenzione del riavvio delle attività per layout dichiarativi

I riavvii dell'attività causati dalla rotazione del dispositivo possono essere impediti anche se definiamo il layout in XML. Ad esempio, è possibile usare questo approccio se si vuole impedire un riavvio dell'attività (per motivi di prestazioni, ad esempio) e non è necessario caricare nuove risorse per orientamenti diversi.

A tale scopo, si segue la stessa procedura usata con un layout a livello di codice. È sufficiente impostare ConfigurationChanges in ActivityAttribute, come abbiamo fatto in CodeLayoutActivity precedenza. Qualsiasi codice che deve essere eseguito per la modifica dell'orientamento può essere nuovamente implementato nel OnConfigurationChanged metodo .

Mantenimento dello stato durante le modifiche all'orientamento

Indipendentemente dalla gestione della rotazione in modo dichiarativo o programmatico, tutte le applicazioni Android devono implementare le stesse tecniche per la gestione dello stato quando cambia l'orientamento del dispositivo. La gestione dello stato è importante perché il sistema riavvia un'attività in esecuzione quando un dispositivo Android viene ruotato. Android esegue questa operazione per semplificare il caricamento di risorse alternative, ad esempio layout e drawable progettati appositamente per un particolare orientamento. Al riavvio, l'attività perde qualsiasi stato temporaneo archiviato nelle variabili di classe locale. Pertanto, se un'attività è conforme allo stato, deve mantenere lo stato a livello di applicazione. Un'applicazione deve gestire il salvataggio e il ripristino di qualsiasi stato dell'applicazione che vuole mantenere tra le modifiche di orientamento.

Per altre informazioni sulla persistenza dello stato in Android, vedere la Guida al ciclo di vita dell'attività.

Riepilogo

Questo articolo ha illustrato come usare le funzionalità predefinite di Android per lavorare con la rotazione. In primo luogo, è stato spiegato come usare il sistema di risorse Android per creare applicazioni con riconoscimento dell'orientamento. Quindi ha illustrato come aggiungere controlli nel codice e come gestire manualmente le modifiche di orientamento.