Incorporación de actividades

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

La incorporación de actividades optimiza las apps en dispositivos de pantalla grande mediante la división de la ventana de tareas de una aplicación entre dos actividades o dos instancias de la misma actividad.

Figura 1: App de Configuración con actividades en paralelo.

La actualización de las bases de código heredadas para establecer la compatibilidad con pantallas grandes es una labor abrumadora que lleva mucho tiempo. La conversión de apps basadas en actividades a diseños de varios paneles mediante fragmentos requiere una refactorización considerable.

La incorporación de actividades prácticamente no requiere la refactorización de tu app. Determinarás la manera en que esta mostrará las actividades (una al lado de la otra o apiladas) mediante la creación de un archivo de configuración XML o llamadas a la API de Jetpack WindowManager.

La compatibilidad con pantallas pequeñas se mantiene automáticamente. Cuando tu app está en un dispositivo con una pantalla pequeña, las actividades se apilan una sobre otra. En pantallas grandes, las actividades se muestran una al lado de la otra. El sistema determina la presentación en función de la configuración que creaste: no se requiere lógica de ramificación.

La incorporación de actividades admite cambios de orientación del dispositivo y funciona a la perfección en dispositivos plegables, en los que se modifica la pila de actividades a medida que el dispositivo se pliega y se despliega.

Modern Android Development usa una arquitectura de actividad única con fragmentos, componentes de navegación y administradores de diseño versátiles, como SlidingPaneLayout.

Sin embargo, si tu app consta de varias actividades, incorporarlas te permitirá proporcionar con facilidad una experiencia del usuario mejorada en tablets, plegables y dispositivos ChromeOS.

Ventana de tareas dividida

La incorporación de actividades divide la ventana de tareas de la app en dos contenedores: uno principal y otro secundario. Estos contenedores incluyen las actividades iniciadas desde la actividad principal o desde otras que ya se encuentran en ellos.

Las actividades se apilan en el contenedor secundario a medida que se inician, y este se apila sobre el principal en pantallas pequeñas, por lo que la pila de actividades y la navegación hacia atrás son coherentes con el orden de las actividades ya integradas en tu app.

La incorporación de actividades te permite mostrarlas de varias formas. Tu app puede dividir la ventana de tareas iniciando dos actividades, una al lado de la otra y de manera simultánea:

Figura 2: Dos actividades en paralelo.

Alternativamente, una actividad que ocupa toda la ventana de tareas puede crear una división mediante el lanzamiento de una nueva actividad junto a ella:

Figura 3: La actividad A inicia la actividad B al costado.

Las actividades que ya están divididas y comparten una ventana de tareas pueden iniciar otras actividades de las siguientes maneras:

  • Al costado y sobre otra actividad:

    Figura 4: La actividad A inicia la actividad C al costado y sobre la actividad B.
  • Al costado y desplazando la división hacia un lado, ocultando la actividad principal anterior:

    Figura 5: La actividad B inicia la actividad C al costado y mueve la división hacia un lado.
  • Iniciando una actividad en el lugar, sobre la otra, es decir, en la misma pila de actividades:

    Figura 6: La actividad B inicia la actividad C sin marcas de intents adicionales.
  • Iniciando una ventana de actividad completa en la misma tarea:

    Figura 7: La actividad A o la actividad B inician la actividad C, que ocupa la ventana de tareas por completo.

Navegación hacia atrás

Los diferentes tipos de aplicaciones pueden tener distintas reglas de navegación hacia atrás en un estado de ventana de tareas dividida según las dependencias entre las actividades o la forma en que los usuarios activan el evento Atrás, por ejemplo:

  • En conjunto: Si las actividades están relacionadas y una no se debería mostrar sin la otra, se puede configurar la navegación hacia atrás para finalizar ambas.
  • Por separado: Si las actividades son completamente independientes, la navegación hacia atrás en una actividad no afecta el estado de otra actividad en la ventana de tareas.

El evento Atrás se envía a la última actividad enfocada cuando se usa la navegación con botones. Con la navegación basada en gestos, el evento Atrás se envía a la actividad donde ocurrió el gesto.

Diseño multipanel

Jetpack WindowManager 1.0 Beta03 te permite compilar un diseño multipanel con actividades en dispositivos de pantalla grande que ejecuten Android 12L (nivel de API 32) y algunos dispositivos con versiones de plataforma anteriores. Las apps existentes que se basan en varias actividades en lugar de fragmentos o diseños basados en vistas, como SlidingPaneLayout, pueden brindar una experiencia mejorada del usuario de pantallas grandes sin una refactorización significativa.

Un ejemplo común es una división de lista y detalles. Para garantizar una presentación de calidad, el sistema inicia la actividad de la lista y, luego, la aplicación inicia de inmediato la actividad de los detalles. El sistema de transición espera hasta que se dibujen ambas actividades y, luego, las muestra juntas. Para el usuario, las dos actividades se inician como una sola.

Figura 8: Dos actividades iniciadas simultáneamente en un diseño multipanel.

Proporciones de división

Tu aplicación puede especificar la proporción de la ventana de tareas mediante el atributo ratio de una configuración de división (consulta la sección Configuración de divisiones que aparece a continuación).

Figura 9: Dos divisiones de actividad con diferentes proporciones de división.

Marcadores de posición

Las actividades de marcadores de posición son actividades secundarias vacías que ocupan una parte de una división de actividad. En definitiva, se las reemplazará por otra actividad que incluya contenido. Por ejemplo, una actividad de marcador de posición puede ocupar el lado secundario de una división de actividad en un diseño de lista y detalles hasta que se seleccione un elemento de la lista, momento en el que una actividad que contiene la información de los detalles del elemento de lista seleccionado reemplaza el marcador de posición.

Los marcadores de posición solo se muestran cuando hay suficiente espacio para una división. Finalizan automáticamente cuando el tamaño de la pantalla cambia a un ancho demasiado pequeño para mostrar una división de actividad, pero se reinician automáticamente (con un estado de nueva inicialización) cuando el espacio lo permite.

Figura 10: Plegado y desplegado de un dispositivo plegable. La actividad del marcador de posición finaliza y se vuelve a crear cuando cambia el tamaño de la pantalla.

Cambios en el tamaño de la ventana

Cuando los cambios en la configuración del dispositivo reducen el ancho de la ventana de tareas de modo que no sea lo suficientemente grande para un diseño multipanel (por ejemplo, cuando un dispositivo plegable de pantalla grande se pliega y el tamaño de la pantalla cambia de uno de tablet a uno de teléfono, o cuando el tamaño de la ventana de la app cambia al modo multiventana), las actividades sin marcador de posición en el panel secundario de la ventana de tareas se apilan sobre las actividades del panel principal.

Las actividades de marcadores de posición solo se muestran cuando el ancho de la pantalla es el suficiente para realizar una división. En pantallas más pequeñas, el marcador de posición se descarta automáticamente. Cuando el área de visualización vuelve a ser lo bastante grande, se vuelve a crear el marcador de posición. (Consulta la sección Marcadores de posición más arriba).

Es posible apilar las actividades porque WindowManager aplica el orden Z a las actividades del panel secundario sobre las actividades del panel principal.

Varias actividades en el panel secundario

La actividad B inicia la actividad C en el lugar, sin marcas de intents adicionales:

División de actividad que contiene las actividades A, B y C, con C apilada sobre B.

Esto da como resultado el siguiente orden Z de las actividades en la misma tarea:

Pila secundaria de actividades que contiene la actividad C apilada sobre B.
          La pila secundaria se apila sobre la principal que contiene la actividad A.

Por lo tanto, en una ventana de tareas más pequeña, la aplicación se reduce a una sola actividad, con C en la parte superior de la pila:

Ventana pequeña que muestra solo la actividad C.

Si vuelves a la ventana más pequeña, podrás navegar por las actividades apiladas unas sobre otras.

Si se restablece la configuración de la ventana de tareas a un tamaño más grande que admite varios paneles, las actividades se volverán a mostrar una al lado de la otra.

Divisiones apiladas

La actividad B inicia la actividad C al costado y mueve la división hacia un lado.

Ventana de tareas que muestra las actividades A y B, y, luego, las actividades B y C.

El resultado es el siguiente orden Z de las actividades en la misma tarea:

Actividades A, B y C en una sola pila. Las actividades se apilan en el siguiente orden de arriba hacia abajo: C, B y A.

En una ventana de tareas más pequeña, la aplicación se reduce a una sola actividad, con C en la parte superior:

Ventana pequeña que muestra solo la actividad C.

Configuración de divisiones

La biblioteca de WindowManager puede crear contenedores y divisiones basadas en reglas de división. La configuración de reglas de división implica realizar varios pasos:

  1. Agrega la dependencia de la biblioteca WindowManager a tu archivo build.gradle:

    implementation("androidx.window:window:1.0.0-beta03")

  2. Crea un archivo de recursos que realice las siguientes acciones:

    • Definir las actividades que se deben dividir mediante filtros
    • Configurar las opciones de división para todas las actividades que comparten una división
    • Especificar las actividades que nunca se deberán dividir

    Por ejemplo:

    <!-- The split configuration for activities. -->
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Automatically split the following activity pairs. -->
        <SplitPairRule
            window:splitRatio="0.3"
            window:splitMinWidth="600dp"
            window:finishPrimaryWithSecondary="true"
            window:finishSecondaryWithPrimary="true">
            <SplitPairFilter
                window:primaryActivityName=".SplitActivityList"
                window:secondaryActivityName=".SplitActivityDetail"/>
            <SplitPairFilter
                window:primaryActivityName="*"
                window:secondaryActivityName="*/*"
                window:secondaryActivityAction="android.intent.action.VIEW"/>
        </SplitPairRule>
    
        <!-- Automatically launch a placeholder for the list activity. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".SplitActivityListPlaceholder"
            window:splitRatio="0.3"
            window:splitMinWidth="600dp">
            <ActivityFilter
                window:activityName=".SplitActivityList"/>
        </SplitPlaceholderRule>
    
    </resources>
    
  3. Informa a la biblioteca sobre las definiciones de las reglas.

    En este ejemplo, se usa la biblioteca de Jetpack Startup a fin de realizar la inicialización antes de que se inicien otros componentes de la carga de la app y las actividades. Para habilitar la funcionalidad de inicio, agrega la dependencia de biblioteca en el archivo de compilación de la app:

    implementation("androidx.startup:startup-runtime:1.1.0")

    Luego, agrega la siguiente entrada en el manifiesto de la app:

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- This entry makes ExampleWindowInitializer discoverable. -->
        <meta-data  android:name="androidx.window.sample.embedding.ExampleWindowInitializer"
            android:value="androidx.startup" />
    </provider>
    
  4. Por último, agrega la implementación de la clase de inicialización.

    Para configurar las reglas, proporciona el ID del archivo XML de recursos que contiene las definiciones (main_split_config) en SplitController.initialize():

    Kotlin

    class ExampleWindowInitializer : Initializer<SplitController> {
       override fun create(context: Context): SplitController {
           SplitController.initialize(context, R.xml.main_split_config)
           return SplitController.getInstance(context)
       }
    
       override fun dependencies(): List<Class<out Initializer<*>>> {
           return emptyList()
       }
    }
    

    Java

    class ExampleWindowInitializer extends Initializer<SplitController> {
       @Override
       SplitController create(Context context) {
           SplitController.initialize(context, R.xml.main_split_config);
           return SplitController.getInstance(context);
       }
    
       @Override
       List<Class<? extends Initializer<?>>> dependencies() {
           return emptyList();
       }
    }
    

Ejemplos de divisiones

División desde la ventana completa

Figura 11: La actividad A inicia la actividad B al costado.

No es necesario refactorizar. Puedes definir la configuración de la división de manera estática o en el tiempo de ejecución y, luego, llamar a Context#startActivity() sin ningún parámetro adicional.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

División predeterminada

Cuando la página de destino de una aplicación está diseñada para dividirse en dos contenedores en pantallas grandes, la experiencia del usuario resulta óptima si ambas actividades se crean y se presentan de forma simultánea. Sin embargo, es posible que el contenido no esté disponible para el contenedor secundario de la división hasta que el usuario interactúe con la actividad en el contenedor principal (por ejemplo, cuando el usuario seleccione un elemento de un menú de navegación). Una actividad de marcador de posición puede llenar el vacío hasta que el contenido se pueda mostrar en el contenedor secundario de la división (consulta la sección Marcadores de posición más arriba).

Figura 12: División creada abriendo dos actividades simultáneamente. Una actividad es un marcador de posición.

Para crear una división con un marcador de posición, crea un marcador de posición y asócialo con la actividad principal:

<SplitPlaceholderRule
    window:placeholderIntentName=".Placeholder">
    <ActivityFilter
        window:activityName=".Main"/>
</SplitPlaceholderRule>

Cuando una app recibe un intent, la actividad objetivo se puede mostrar como la parte secundaria de una división de actividad (por ejemplo, una solicitud para mostrar una pantalla de detalles con información sobre un elemento de una lista). En pantallas pequeñas, el detalle se muestra en toda la ventana de tareas. En dispositivos más grandes, se muestra junto a la lista.

Figura 13: Actividad de detalles de vínculos directos que se muestra sola en una pantalla pequeña, pero junto con una actividad de lista en una pantalla grande.

La solicitud de inicio se debe enrutar a la actividad principal, y la actividad objetivo de detalles debe iniciarse en una división. SplitController elige automáticamente la presentación correcta (apilada o en paralelo) según el ancho de la pantalla disponible.

Kotlin

override fun onCreate(savedInstanceState Bundle?) {
    …
    splitController.registerRule(SplitPairRule(newFilters))
    startActivity(Intent(this, DetailActivity::class.java))
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    …
    splitController.registerRule(new SplitPairRule(newFilters));
    startActivity(new Intent(this, DetailActivity.class));
}

El destino del vínculo directo puede ser la única actividad que debe estar disponible para el usuario en la pila de navegación hacia atrás, y te recomendamos que evites descartar la actividad de detalles y dejar solo la actividad principal:

Pantalla grande con la actividad de lista y la actividad de detalles en paralelo.
          La navegación hacia atrás no puede descartar la actividad de detalles y dejar la actividad de lista en pantalla.

Pantalla pequeña solo con la actividad de detalles. La navegación hacia atrás no puede descartar la actividad de detalles y mostrar la actividad de lista.

En su lugar, puedes finalizar ambas actividades al mismo tiempo mediante el atributo finishPrimaryWithSecondary:

<SplitPairRule
    window:finishPrimaryWithSecondary="true">
    <SplitPairFilter
        window:primaryActivityName=".List"
        window:secondaryActivityName=".Detail"/>
</SplitPairRule>

Varias actividades en contenedores divididos

Apilar varias actividades en un contenedor dividido permite a los usuarios acceder a contenido específico. Por ejemplo, con una división de lista y detalles, es posible que el usuario necesite ir a una sección de detalles secundarios, pero mantener la actividad principal en su lugar:

Figura 14: Actividad abierta en el lugar, en el panel secundario de la ventana de tareas.

Kotlin

class DetailActivity {
    …
    fun onOpenSubDetail() {
      startActivity(Intent(this, SubDetailActivity::class.java))
    }
}

Java

public class DetailActivity {
    …
    void onOpenSubDetail() {
        startActivity(new Intent(this, SubDetailActivity.class));
    }
}

La actividad de detalles secundarios se coloca sobre la actividad de detalles, y la oculta:

Luego, el usuario puede regresar al nivel anterior de detalles navegando hacia atrás en la pila:

Figura 15: Se quitó la actividad de la parte superior de la pila.

El apilado de actividades unas sobre otras es el comportamiento predeterminado cuando estas se inician desde una que se encuentra en el mismo contenedor secundario. Las actividades iniciadas desde el contenedor principal dentro de una división activa también terminan en el contenedor secundario en la parte superior de la pila de actividades.

Actividades en una tarea nueva

Cuando las actividades en una ventana dividida de tareas inician actividades en una tarea nueva, esta última es independiente de la que incluye la división y se muestra en una ventana completa. En la pantalla Recents, se muestran dos tareas: la que está en la división y la nueva.

Figura 16: Se inicia la actividad C en una tarea nueva desde la actividad B.

Reemplazo de actividades

Las actividades se pueden reemplazar en la pila del contenedor secundario (por ejemplo, cuando se usa la actividad principal para la navegación de nivel superior y la actividad secundaria es un destino seleccionado). Cada selección de la navegación de nivel superior debe iniciar una actividad nueva en el contenedor secundario y quitar las actividades que antes estaban allí.

Figura 17: La actividad de navegación de nivel superior del panel principal reemplaza las actividades de destino del panel secundario.

La navegación hacia atrás puede resultar confusa cuando se contrae la división (cuando se pliega el dispositivo) si la app no finaliza la actividad en el contenedor secundario cuando cambia la selección de navegación. Por ejemplo, si tienes un menú en el panel principal y las pantallas A y B apiladas en el panel secundario, cuando el usuario pliegue el teléfono, B estará sobre A, y A estará sobre el menú. Cuando el usuario navegue hacia atrás desde B, aparecerá A en lugar del menú.

En esos casos, se debe quitar la pantalla A de la pila de actividades.

El comportamiento predeterminado cuando se realiza un inicio lateral en un contenedor nuevo sobre una división existente es colocar los nuevos contenedores secundarios en la parte superior y conservar los antiguos en la pila de actividades. Puedes configurar las divisiones de modo que se borren los contenedores secundarios anteriores mediante clearTop y que se inicien las actividades nuevas con normalidad.

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

Kotlin

class MenuActivity {
    …
    fun onMenuItemSelected(selectedMenuItem: Int) {
        startActivity(Intent(this, classForItem(selectedMenuItem)))
    }
}

Java

public class MenuActivity {
    …
    void onMenuItemSelected(int selectedMenuItem) {
        startActivity(new Intent(this, classForItem(selectedMenuItem)));
    }
}

De forma alternativa, usa la misma actividad secundaria y, desde la actividad principal (el menú), envía nuevos intents que se resuelvan en la misma instancia, pero que activen un estado o una actualización de la IU en el contenedor secundario.

Divisiones múltiples

Las apps pueden proporcionar navegación profunda de varios niveles iniciando actividades adicionales a un lado.

Cuando una actividad en un contenedor secundario inicia una actividad nueva a un costado, se crea una división nueva sobre la existente.

Figura 18: La actividad B inicia la actividad C al costado.

La pila de actividades contiene todas las actividades que se abrieron antes, por lo que los usuarios pueden navegar a la división A/B después de finalizar C.

Actividades A, B y C en una pila. Las actividades se apilan en el siguiente orden de arriba hacia abajo: C, B y A.

Para crear una nueva división, inicia la actividad nueva al lado del contenedor secundario existente. Declara las configuraciones para las divisiones A/B y B/C, e inicia la actividad C normalmente desde B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
    <SplitPairFilter
        window:primaryActivityName=".B"
        window:secondaryActivityName=".C"/>
</SplitPairRule>

Kotlin

class B {
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

Java

public class B {
    …
    void onOpenC() {
        startActivity(new Intent(this, C.class));
    }
}

Cómo reaccionar a los cambios de estado de divisiones

Las diferentes actividades de una app pueden tener elementos de la IU que realizan la misma función (por ejemplo, un control que abre una ventana que contiene la configuración de la cuenta).

Figura 19: Diferentes actividades con elementos de la IU funcionalmente idénticos.

Si dos actividades que tienen un elemento de la IU en común están en una división, mostrar el elemento en ambas actividades resultará redundante y podría generar confusión.

Figura 20: Elementos de la IU duplicados en la división de actividad.

A fin de saber cuándo las actividades se encuentran en una división, registra un objeto de escucha con SplitController para los cambios en el estado de la división. Luego, ajusta la IU según corresponda:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback())
}

inner class SplitInfoChangeCallback : Consumer<List<SplitInfo>> {
    override fun accept(splitInfoList: List<SplitInfo>) {
        findViewById<View>(R.id.infoButton).visibility =
            if (!splitInfoList.isEmpty()) View.GONE else View.VISIBLE
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback());
}

class SplitInfoChangeCallback extends Consumer<List<SplitInfo>> {
    public void accept(List<SplitInfo> splitInfoList) {
        findViewById<View>(R.id.infoButton).visibility =
            !splitInfoList.isEmpty()) ? View.GONE : View.VISIBLE;
    }
}

Las devoluciones de llamada se pueden realizar en cualquier estado del ciclo de vida, incluso cuando se detiene una actividad. Por lo general, los objetos de escucha deben estar registrados en onStart() y no deben registrarse en onStop().

Ventana modal completa

Algunas actividades bloquean a los usuarios para que no interactúen con la aplicación hasta que se realice una acción especificada (por ejemplo, una actividad de la pantalla de acceso, la pantalla de confirmación de la política o un mensaje de error). Se debe evitar que las actividades modales aparezcan en una división.

Una actividad puede verse forzada a llenar siempre la ventana de tareas mediante la configuración de expansión:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

Cómo finalizar actividades

Los usuarios pueden finalizar las actividades en cualquiera de los lados de la división si deslizan el dedo desde el borde de la pantalla:

Figura 21: Gesto de deslizar el dedo para finalizar la actividad B.
Figura 22: Gesto de deslizar el dedo para finalizar la actividad A.

Si el dispositivo está configurado a fin de usar el botón Atrás en lugar de la navegación por gestos, la entrada se envía a la actividad enfocada, es decir, aquella que se tocó o se inició por última vez.

El resultado de finalizar una de las actividades de la división depende de la configuración de esta última.

Configuración predeterminada

Cuando finaliza una actividad de la división, la actividad restante ocupa toda la ventana:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

División que contiene las actividades A y B. A finalizó, y toda la ventana queda ocupada por B.

División que contiene las actividades A y B. B finalizó y toda la ventana queda ocupada por A.

Cómo finalizar actividades al mismo tiempo

La actividad principal finaliza automáticamente cuando lo haga la actividad secundaria:

<SplitPairRule
    window:finishPrimaryWithSecondary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

División que contiene las actividades A y B. B finalizó, lo que también finalizó A, y la ventana de tareas queda vacía.

División que contiene las actividades A y B. A finalizó, y solo B queda en la ventana de tareas.

Finaliza la actividad secundaria automáticamente cuando lo haga la actividad principal:

<SplitPairRule
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

División que contiene las actividades A y B. A finalizó, lo que también finalizó B, y la ventana de tareas queda vacía.

División que contiene las actividades A y B. B finalizó, y solo A queda en la ventana de tareas.

Finaliza las actividades al mismo tiempo cuando se complete la principal o la secundaria:

<SplitPairRule
    window:finishPrimaryWithSecondary="true"
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

División que contiene las actividades A y B. A finalizó, lo que también finalizó B, y la ventana de tareas queda vacía.

División que contiene las actividades A y B. B finalizó, lo que también finalizó A, y la ventana de tareas queda vacía.

Cómo finalizar varias actividades en contenedores

Si se apilan varias actividades en un contenedor dividido, finalizar una de las que esté en la parte inferior de la pila no finalizará automáticamente aquellas que se encuentren en la parte superior.

Por ejemplo, si dos actividades están en el contenedor secundario, y C está sobre B:

La pila de actividades secundaria que contiene la actividad C sobre B se apila sobre la principal que contiene la actividad A.

La configuración de la división se define según la configuración de las actividades A y B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Cuando termina la actividad superior, se conserva la división.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. C finaliza y deja A y B en la división de actividad.

Finalizar la actividad inferior (raíz) del contenedor secundario no quita las actividades sobre ella. De esta manera, se conserva la división.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. B finaliza y deja A y C en la división de actividad.

También se ejecutan las reglas adicionales a los efectos de finalizar actividades al mismo tiempo, como finalizar la actividad secundaria con la principal:

<SplitPairRule
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. A finaliza, lo que también finaliza B y C.

Cuando la división se configura para finalizar la actividad principal y la secundaria al mismo tiempo, se ve lo siguiente:

<SplitPairRule
    window:finishPrimaryWithSecondary="true"
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. C finaliza y deja A y B en la división de actividad.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. B finaliza y deja A y C en la división de actividad.

División con la actividad A en el contenedor principal y las actividades B y C en el secundario, con C apilada sobre B. A finaliza, lo que también finaliza B y C.

Cómo cambiar las propiedades de las divisiones durante el tiempo de ejecución

No se pueden cambiar las propiedades de una división que se encuentre visible y activa. El cambio en las reglas de división afecta los inicios de actividades adicionales y los contenedores nuevos, pero no las divisiones existentes y activas.

Para cambiar las propiedades de las divisiones activas, finaliza las actividades laterales o presentes en la división y vuelve a iniciarlas al lado con una configuración nueva.

Cómo extraer una actividad de una división a una ventana completa

Crea una nueva configuración que muestre la ventana completa de la actividad lateral y, luego, reinicia la actividad con un intent que se resuelva en la misma instancia.

Cómo comprobar la compatibilidad con las divisiones durante el tiempo de ejecución

La incorporación de actividades es una función de Android 12L (nivel de API 32), pero también está disponible en algunos dispositivos con versiones de plataforma anteriores. A fin de comprobar la disponibilidad de la función en el tiempo de ejecución, usa el método SplitController.isSplitSupported():

Kotlin

val splitController = SplitController.Companion.getInstance()
if (splitController.isSplitSupported()) {
    // Device supports split activity features.
}

Java

SplitController splitController = SplitController.Companion.getInstance();
if (splitController.isSplitSupported()) {
  // Device supports split activity features.
}

Si no se admiten las divisiones, las actividades se iniciarán sobre las existentes (como se hace en el modelo normal).

Limitaciones, restricciones y advertencias

  • Solo la app host de la tarea, que se identifica como propietaria de la actividad raíz de esta, puede organizar e incorporar otras actividades en la tarea. Si las actividades que admiten incorporación y divisiones se ejecutan en una tarea que pertenece a una aplicación diferente, la incorporación y las divisiones no funcionarán para esas actividades.
  • Las actividades solo se pueden organizar dentro de una misma tarea. Iniciar una actividad en una tarea nueva siempre la coloca en una ventana expandida nueva fuera de cualquier división existente.
  • Solo se pueden organizar y dividir las actividades que formen parte de un mismo proceso. La devolución de llamada SplitInfo solo informa actividades que pertenecen al mismo proceso, ya que no hay manera de conocer aquellas que existan en diferentes procesos.
  • Cada regla de actividad individual o de vinculación se aplica solo a los inicios de actividad que ocurran después de que se registre la regla. Por el momento, no hay forma de actualizar las divisiones existentes ni sus propiedades visuales.
  • La configuración del filtro de vinculación de divisiones debe coincidir con los intents que se usan cuando se inician actividades por completo. La coincidencia se produce cuando se inicia una actividad nueva desde el proceso de la aplicación, por lo que es posible que no conozcas los nombres de los componentes que se resuelven más adelante en el proceso del sistema cuando se usan intents implícitos. Si no se conoce el nombre de un componente al momento del inicio, se puede usar un comodín ("*/*") y realizar el filtrado según la acción de intent.
  • Por el momento, no hay forma de mover las actividades entre contenedores ni dentro o fuera de las divisiones después de crearlas. La biblioteca WindowManager solo crea las divisiones cuando se inician actividades nuevas con reglas que coincidan, y las divisiones se destruyen cuando finaliza la última actividad de un contenedor de divisiones.
  • Las actividades pueden volver a iniciarse cuando cambia la configuración, de modo que, cuando se crea o quita una división y cambian los límites de la actividad, esta puede pasar por la destrucción completa de la instancia anterior y la creación de la nueva. Como resultado, los desarrolladores de apps deberían tener cuidado con cuestiones como el inicio de actividades nuevas a partir de devoluciones de llamada de ciclo de vida.

Recursos adicionales