Control de giro

En este tema se describe cómo controlar los cambios de orientación del dispositivo en Xamarin.Android. Trata cómo trabajar con el sistema de recursos de Android para cargar automáticamente los recursos de una orientación de dispositivo determinada, así como cómo controlar mediante programación los cambios de orientación.

Información general

Dado que los dispositivos móviles se rotan fácilmente, la rotación integrada es una característica estándar en los sistemas operativos móviles. Android proporciona un marco sofisticado para tratar la rotación dentro de las aplicaciones, ya sea que la interfaz de usuario se cree declarativamente en XML o mediante programación en el código. Al controlar automáticamente los cambios de diseño declarativo en un dispositivo girado, una aplicación puede beneficiarse de la estrecha integración con el sistema de recursos de Android. Para el diseño mediante programación, los cambios se deben controlar manualmente. Esto permite un control más preciso en tiempo de ejecución, pero a costa de más trabajo para el desarrollador. Una aplicación también puede optar por no participar en el reinicio de la actividad y tomar el control manual de los cambios de orientación.

En esta guía se examinan los temas de orientación siguientes:

  • Rotación de diseño declarativo : cómo usar el sistema de recursos de Android para crear aplicaciones compatibles con orientación, incluido cómo cargar diseños y dibujables para orientaciones concretas.

  • Rotación de diseño mediante programación: cómo agregar controles mediante programación, así como cómo controlar manualmente los cambios de orientación.

Controlar la rotación mediante declaración con diseños

Al incluir archivos en carpetas que siguen las convenciones de nomenclatura, Android carga automáticamente los archivos adecuados cuando cambia la orientación. Esto incluye la compatibilidad para:

  • Recursos de diseño : especifica qué archivos de diseño se inflan para cada orientación.

  • Recursos dibujables : especifica qué drawables se cargan para cada orientación.

Recursos de diseño

De forma predeterminada, los archivos XML de Android (AXML) incluidos en la carpeta Resources/layout se usan para representar vistas para una actividad. Los recursos de esta carpeta se usan para la orientación vertical y horizontal si no se proporcionan recursos de diseño adicionales específicamente para horizontal. Considere la estructura del proyecto creada por la plantilla de proyecto predeterminada:

Estructura predeterminada de la plantilla de proyecto

Este proyecto crea un único archivo Main.axml en la carpeta Resources/layout . Cuando se llama al método Activity OnCreate , infla la vista definida en Main.axml, que declara un botón como se muestra en el código XML siguiente:

<?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 el dispositivo se gira a la orientación horizontal, se vuelve a llamar al método de OnCreate la actividad y se infla el mismo archivo Main.axml , como se muestra en la captura de pantalla siguiente:

Misma pantalla, pero en orientación horizontal

diseños de Orientation-Specific

Además de la carpeta de diseño (que el valor predeterminado es vertical y también se puede denominar explícitamente layout-port mediante la inclusión de una carpeta denominada layout-land), una aplicación puede definir las vistas que necesita cuando está en horizontal sin cambios de código.

Supongamos que el archivo Main.axml contenía el siguiente 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>

Si se agrega un archivo Main.axml adicional denominado layout-land que contiene un archivo Main.axml adicional, inflando el diseño cuando esté en horizontal, ahora se cargará Android el archivo Main.axml recién agregado. Considere la versión horizontal del archivo Main.axml que contiene el código siguiente (por simplicidad, este XML es similar a la versión vertical predeterminada del código, pero usa una cadena diferente en 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>

La ejecución de este código y la rotación del dispositivo de vertical a horizontal muestra la nueva carga XML, como se muestra a continuación:

Capturas de pantalla vertical y horizontal que imprimen el modo vertical

Recursos dibujables

Durante la rotación, Android trata los recursos dibujables de forma similar a los recursos de diseño. En este caso, el sistema obtiene los elementos dibujables de las carpetas Resources/drawable y Resources/drawable-land , respectivamente.

Por ejemplo, supongamos que el proyecto incluye una imagen denominada Monkey.png en la carpeta Resources/drawable , a la que se hace referencia desde un ImageView elemento en XML de la siguiente manera:

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

Supongamos además que se incluye una versión diferente de Monkey.png en Recursos/terreno dibujable. Al igual que con los archivos de diseño, cuando se gira el dispositivo, los cambios dibujables para la orientación especificada, como se muestra a continuación:

Versión diferente de Monkey.png que se muestra en los modos vertical y horizontal

Control de la rotación mediante programación

A veces definimos diseños en el código. Esto puede ocurrir por diversos motivos, incluidas las limitaciones técnicas, las preferencias del desarrollador, etc. Cuando agregamos controles mediante programación, una aplicación debe tener en cuenta manualmente la orientación del dispositivo, que se controla automáticamente cuando usamos recursos XML.

Agregar controles en el código

Para agregar controles mediante programación, una aplicación debe realizar los pasos siguientes:

  • Cree un diseño.
  • Establecer parámetros de diseño.
  • Crear controles.
  • Establecer parámetros de diseño de control.
  • Agregue controles al diseño.
  • Establezca el diseño como la vista de contenido.

Por ejemplo, considere una interfaz de usuario que consta de un único TextView control agregado a , RelativeLayoutcomo se muestra en el código siguiente.

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

Este código crea una instancia de una RelativeLayout clase y establece su LayoutParameters propiedad. La LayoutParams clase es la forma de Android de encapsular cómo se colocan los controles de forma reutilizable. Una vez creada una instancia de un diseño, se pueden crear y agregar controles a él. Los controles también tienen LayoutParameters, como en TextView este ejemplo. Después de crear el objeto TextView , agregándolo a RelativeLayout y estableciendo como RelativeLayout la vista de contenido da como resultado la aplicación que muestra como TextView se muestra:

Botón Incrementar contador que se muestra en los modos vertical y horizontal

Detección de la orientación en el código

Si una aplicación intenta cargar una interfaz de usuario diferente para cada orientación cuando OnCreate se llama (esto ocurrirá cada vez que se gira un dispositivo), debe detectar la orientación y, a continuación, cargar el código de interfaz de usuario deseado. Android tiene una clase denominada WindowManager, que se puede usar para determinar la rotación de dispositivos actual a través de la WindowManager.DefaultDisplay.Rotation propiedad , como se muestra a continuación:

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 establece que TextView se colocará 100 píxeles desde la parte superior izquierda de la pantalla, animando automáticamente al nuevo diseño, cuando se gira a horizontal, como se muestra aquí:

El estado de vista se conserva en los modos vertical y horizontal

Impedir el reinicio de la actividad

Además de controlar todo en OnCreate, una aplicación también puede impedir que se reinicie una actividad cuando cambia la orientación estableciendo ConfigurationChanges de la ActivityAttribute siguiente manera:

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

Ahora, cuando se gira el dispositivo, no se reinicia la actividad. Para controlar manualmente el cambio de orientación en este caso, una actividad puede invalidar el OnConfigurationChanged método y determinar la orientación del Configuration objeto que se pasa, como en la nueva implementación de la actividad siguiente:

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

Aquí se inicializan los TextView's parámetros de diseño para horizontal y vertical. Las variables de clase contienen los parámetros, junto con el TextView propio , ya que la actividad no se volverá a crear cuando cambie la orientación. El código sigue usando en surfaceOrientartionOnCreate para establecer el diseño inicial de .TextView Después de eso, OnConfigurationChanged controla todos los cambios de diseño posteriores.

Cuando se ejecuta la aplicación, Android carga los cambios de la interfaz de usuario a medida que se produce la rotación de dispositivos y no reinicia la actividad.

Impedir el reinicio de la actividad para diseños declarativos

También se pueden evitar los reinicios de actividad causados por la rotación de dispositivos si definimos el diseño en XML. Por ejemplo, podemos usar este enfoque si queremos evitar un reinicio de actividad (por motivos de rendimiento, quizás) y no es necesario cargar nuevos recursos para distintas orientaciones.

Para ello, seguimos el mismo procedimiento que usamos con un diseño mediante programación. Simplemente se establece ConfigurationChanges en , ActivityAttributecomo hicimos en el CodeLayoutActivity anterior. Cualquier código que necesite ejecutarse para el cambio de orientación se puede implementar de nuevo en el OnConfigurationChanged método .

Mantenimiento del estado durante los cambios de orientación

Tanto si controla la rotación mediante declaración como mediante programación, todas las aplicaciones Android deben implementar las mismas técnicas para administrar el estado cuando cambia la orientación del dispositivo. La administración del estado es importante porque el sistema reinicia una actividad en ejecución cuando se gira un dispositivo Android. Android hace esto para facilitar la carga de recursos alternativos, como diseños y dibujables diseñados específicamente para una orientación determinada. Cuando se reinicia, la actividad pierde cualquier estado transitorio que pueda haber almacenado en variables de clase locales. Por lo tanto, si una actividad depende del estado, debe conservar su estado en el nivel de aplicación. Una aplicación debe controlar el guardado y la restauración de cualquier estado de aplicación que quiera conservar en los cambios de orientación.

Para obtener más información sobre cómo conservar el estado en Android, consulte la guía ciclo de vida de la actividad.

Resumen

En este artículo se explica cómo usar las funcionalidades integradas de Android para trabajar con la rotación. En primer lugar, se explica cómo usar el sistema de recursos de Android para crear aplicaciones compatibles con orientación. A continuación, se muestra cómo agregar controles en el código, así como cómo controlar los cambios de orientación manualmente.