Rotation de la gestion

Cette rubrique explique comment gérer les modifications d’orientation des appareils dans Xamarin.Android. Il explique comment utiliser le système de ressources Android pour charger automatiquement des ressources pour une orientation d’appareil particulière, ainsi que la gestion programmatique des modifications d’orientation.

Vue d’ensemble

Étant donné que les appareils mobiles sont facilement pivotés, la rotation intégrée est une fonctionnalité standard dans les systèmes d’exploitation mobiles. Android fournit une infrastructure sophistiquée pour gérer la rotation au sein des applications, que l’interface utilisateur soit créée de manière déclarative en XML ou par programmation dans du code. Lorsque vous gérez automatiquement les modifications de disposition déclarative sur un appareil pivoté, une application peut bénéficier de l’intégration étroite avec le système de ressources Android. Pour la disposition programmatique, les modifications doivent être gérées manuellement. Cela permet un contrôle plus fin au moment de l’exécution, mais au détriment de plus de travail pour le développeur. Une application peut également choisir de refuser le redémarrage de l’activité et de prendre le contrôle manuel des modifications d’orientation.

Ce guide examine les rubriques d’orientation suivantes :

  • Rotation de la disposition déclarative : comment utiliser le système de ressources Android pour créer des applications prenant en charge l’orientation, notamment comment charger des dispositions et des dessins pour des orientations particulières.

  • Rotation de disposition programmatique : ajout de contrôles par programme et gestion manuelle des modifications d’orientation.

Gestion de la rotation de manière déclarative avec les dispositions

En incluant des fichiers dans des dossiers qui suivent les conventions d’affectation de noms, Android charge automatiquement les fichiers appropriés lorsque l’orientation change. Cela inclut la prise en charge des éléments suivants :

  • Ressources de disposition : spécification des fichiers de disposition qui sont gonflés pour chaque orientation.

  • Ressources dessinables : spécification des éléments à dessiner qui sont chargés pour chaque orientation.

Ressources de disposition

Par défaut, les fichiers ANDROID XML (AXML) inclus dans le dossier Ressources/disposition sont utilisés pour le rendu des vues d’une activité. Les ressources de ce dossier sont utilisées à la fois pour l’orientation portrait et paysage si aucune ressource de disposition supplémentaire n’est fournie spécifiquement pour le paysage. Considérez la structure de projet créée par le modèle de projet par défaut :

Structure de modèle de projet par défaut

Ce projet crée un fichier Main.axml unique dans le dossier Resources/layout . Lorsque la méthode de l’activité OnCreate est appelée, elle augmente la vue définie dans Main.axml, qui déclare un bouton comme indiqué dans le code XML ci-dessous :

<?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>

Si l’appareil est pivoté vers l’orientation paysage, la méthode de OnCreate l’activité est appelée à nouveau et le même fichier Main.axml est gonflé, comme illustré dans la capture d’écran ci-dessous :

Même écran, mais en orientation paysage

dispositions Orientation-Specific

En plus du dossier de disposition (qui est défini par défaut sur portrait et peut également être explicitement nommé layout-port en incluant un dossier nommé layout-land), une application peut définir les vues dont elle a besoin dans le paysage sans aucune modification de code.

Supposons que le fichier Main.axml contenait le code XML suivant :

<?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>

Si un dossier nommé layout-land qui contient un fichier Main.axml supplémentaire est ajouté au projet, le fait de gonfler la disposition dans le paysage entraîne désormais le chargement par Android du fichier Main.axml nouvellement ajouté. Considérez la version paysage du fichier Main.axml qui contient le code suivant (par souci de simplicité, ce code XML est similaire à la version portrait par défaut du code, mais utilise une chaîne différente dans le 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’exécution de ce code et la rotation de l’appareil de portrait en mode paysage illustrent le nouveau chargement XML, comme indiqué ci-dessous :

Captures d’écran portrait et paysage imprimant le mode portrait

Ressources dessinables

Pendant la rotation, Android traite les ressources dessinables de la même façon que les ressources de disposition. Dans ce cas, le système obtient les dessinables des dossiers Ressources/drawable et Ressources/drawable-land , respectivement.

Par exemple, supposons que le projet inclut une image nommée Monkey.png dans le dossier Resources/drawable , où le drawable est référencé à partir d’un ImageView en XML comme suit :

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

Supposons également qu’une autre version de Monkey.png soit incluse sous Ressources/terrain à dessin. Tout comme avec les fichiers de disposition, lorsque l’appareil est pivoté, le dessinable change pour l’orientation donnée, comme indiqué ci-dessous :

Différentes versions de Monkey.png affichées en mode portrait et paysage

Gestion de la rotation par programmation

Parfois, nous définissons des dispositions dans le code. Cela peut se produire pour diverses raisons, notamment les limitations techniques, la préférence des développeurs, etc. Lorsque nous ajoutons des contrôles par programmation, une application doit prendre en compte manuellement l’orientation de l’appareil, qui est gérée automatiquement lorsque nous utilisons des ressources XML.

Ajout de contrôles dans le code

Pour ajouter des contrôles par programmation, une application doit effectuer les étapes suivantes :

  • Créez une disposition.
  • Définissez les paramètres de disposition.
  • Créer des contrôles.
  • Définissez les paramètres de disposition de contrôle.
  • Ajoutez des contrôles à la disposition.
  • Définissez la disposition en tant qu’affichage de contenu.

Par exemple, considérez une interface utilisateur composée d’un seul TextView contrôle ajouté à un RelativeLayout, comme indiqué dans le code suivant.

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

Ce code crée une instance d’une RelativeLayout classe et définit sa LayoutParameters propriété. La LayoutParams classe est la façon d’Android d’encapsuler la façon dont les contrôles sont positionnés de manière réutilisable. Une fois qu’une instance d’une disposition est créée, des contrôles peuvent être créés et ajoutés à celle-ci. Les contrôles ont LayoutParameterségalement , comme dans TextView cet exemple. Une fois le TextView est créé, en l’ajoutant au RelativeLayout et en définissant comme RelativeLayout affichage de contenu, l’application affiche le TextView comme indiqué :

Bouton Incrémenter le compteur affiché en mode portrait et paysage

Détection de l’orientation dans le code

Si une application tente de charger une interface utilisateur différente pour chaque orientation quand OnCreate est appelée (cela se produit chaque fois qu’un appareil est pivoté), elle doit détecter l’orientation, puis charger le code d’interface utilisateur souhaité. Android a une classe appelée WindowManager, qui peut être utilisée pour déterminer la rotation actuelle de l’appareil via la WindowManager.DefaultDisplay.Rotation propriété , comme indiqué ci-dessous :

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

Ce code définit le TextView à positionner à 100 pixels du haut à gauche de l’écran, en animant automatiquement vers la nouvelle disposition, lors d’une rotation vers le paysage, comme indiqué ici :

L’état d’affichage est conservé dans les modes portrait et paysage

Empêcher le redémarrage de l’activité

En plus de tout gérer dans OnCreate, une application peut également empêcher le redémarrage d’une activité lorsque l’orientation change en définissant ConfigurationChanges dans le ActivityAttribute comme suit :

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

Maintenant, lorsque l’appareil est pivoté, l’activité n’est pas redémarrée. Pour gérer manuellement le changement d’orientation dans ce cas, une activité peut remplacer la OnConfigurationChanged méthode et déterminer l’orientation à partir de l’objet Configuration passé, comme dans la nouvelle implémentation de l’activité ci-dessous :

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

Ici, les TextView's paramètres de disposition sont initialisés pour le paysage et le portrait. Les variables de classe contiennent les paramètres, ainsi que le TextView lui-même, car l’activité ne sera pas recréé lorsque l’orientation change. Le code utilise toujours le surfaceOrientartion dans OnCreate pour définir la disposition initiale du TextView. Après cela, OnConfigurationChanged gère toutes les modifications de disposition suivantes.

Lorsque nous exécutons l’application, Android charge les modifications de l’interface utilisateur à mesure que la rotation de l’appareil se produit et ne redémarre pas l’activité.

Empêcher le redémarrage de l’activité pour les dispositions déclaratives

Les redémarrages d’activité provoqués par la rotation des appareils peuvent également être empêchés si nous définissons la disposition en XML. Par exemple, nous pouvons utiliser cette approche si nous voulons empêcher un redémarrage de l’activité (pour des raisons de performances, peut-être) et que nous n’avons pas besoin de charger de nouvelles ressources pour différentes orientations.

Pour ce faire, nous suivons la même procédure que celle que nous utilisons avec une disposition programmatique. ConfigurationChanges Définissez simplement dans le ActivityAttribute, comme nous l’avons fait dans le CodeLayoutActivity précédent. Tout code qui doit s’exécuter pour le changement d’orientation peut à nouveau être implémenté dans la OnConfigurationChanged méthode .

Maintien de l’état pendant les changements d’orientation

Qu’il s’agisse de gérer la rotation de manière déclarative ou programmatique, toutes les applications Android doivent implémenter les mêmes techniques de gestion de l’état lorsque l’orientation de l’appareil change. La gestion de l’état est importante, car le système redémarre une activité en cours d’exécution lorsqu’un appareil Android est pivoté. Android fait cela pour faciliter le chargement d’autres ressources, telles que des dispositions et des dessins conçus spécifiquement pour une orientation particulière. Lorsqu’elle redémarre, l’activité perd tout état temporaire qu’elle peut avoir stocké dans des variables de classe locales. Par conséquent, si une activité dépend de l’état, elle doit conserver son état au niveau de l’application. Une application doit gérer l’enregistrement et la restauration de l’état de l’application qu’elle souhaite conserver entre les changements d’orientation.

Pour plus d’informations sur la persistance de l’état dans Android, reportez-vous au Guide de cycle de vie des activités .

Résumé

Cet article explique comment utiliser les fonctionnalités intégrées d’Android pour travailler avec la rotation. Tout d’abord, il a expliqué comment utiliser le système de ressources Android pour créer des applications prenant en charge l’orientation. Il a ensuite présenté comment ajouter des contrôles dans le code, ainsi que comment gérer manuellement les modifications d’orientation.