Detalles de la adaptación de la pantalla plegable de Android 2023, es hora de iluminar nuevas habilidades

Desde que Samsung lanzó el primer Galaxy Z Fold (sin contar a Royole) en 2019, los fabricantes de Android han seguido sus soluciones plegables una tras otra. Después de eso, el mercado de teléfonos móviles con pantalla plegable ha mantenido un rápido crecimiento. Por ejemplo, en la primera mitad de En 2023, el volumen total de ventas fue de 2,27 millones de unidades, un aumento interanual del 102,0%.

Aunque en comparación con los envíos totales de teléfonos móviles de 130 millones de unidades en la primera mitad del año, solo puede considerarse como una fracción, pero es innegable que la probabilidad de que los desarrolladores de aplicaciones encuentren teléfonos móviles plegables no es baja, especialmente estos Es probable que los usuarios sean usuarios de “alto valor”.

Entonces, a partir de 2023, la adaptación de la pantalla plegable se convertirá gradualmente en uno de los KPI principales de Android, entonces, ¿qué sucede si no coincide? Si se adapta, ¿por qué medios ? Este artículo le permitirá comprender en profundidad este tema.

⚠️Este artículo es muy largo y se puede guardar para emergencias.

Modo buzón

En primer lugar, si no es adecuado, lo más probable ( no necesariamente ) es que su aplicación se muestre en modo Letterboxing y es posible que vea que la aplicación existe como se muestra en la siguiente figura, es decir, cuando la relación de aspecto de la aplicación y la pantalla La aplicación puede abrirse en modo Letterbox cuando la báscula no es compatible .

Generalmente, la aplicación bloquea la dirección de rotación y adopta el tamaño no redimensionable.

Por supuesto, ingresar al modo Letterboxing está relacionado con la versión de TargetSDK, la configuración de la aplicación y la resolución de la pantalla , y la presentación del modo Letterboxing en diferentes versiones del sistema operativo también puede ser diferente, por ejemplo:

  • Android 12 (API 31) comenzó a introducir mejoras en Letterboxing, que pueden ser configuradas y soportadas por los fabricantes de teléfonos móviles:

    • Esquinas redondeadas: Windows admite esquinas redondeadas
    • Transparencia de la barra del sistema: la barra de estado y la barra de navegación que cubren la aplicación admiten la translucidez
    • Relación de aspecto configurable: la relación de aspecto de la aplicación se puede ajustar para mejorar la apariencia de la aplicación.
  • 12L (API 32) añadido:

    • Posición configurable: en pantallas grandes, los fabricantes de dispositivos pueden colocar aplicaciones en el lado izquierdo o derecho de la pantalla.
    • Botón de reinicio: los fabricantes de dispositivos pueden darle una nueva apariencia al botón de reinicio en el modo de compatibilidad de tamaño. (El modo de compatibilidad de tamaño puede hacer que el ancho o alto de la aplicación llene la pantalla tanto como sea posible)

    Cuando el sistema determina que la visualización de Letterboxing se puede mejorar reescalando la aplicación para llenar la ventana de visualización, Android pondrá la aplicación en modo de compatibilidad de tamaño, en este momento el sistema mostrará un control de reinicio y recreará el proceso de la aplicación. recrear la actividad y volver a dibujarla después de la confirmación Adaptar.

  • Android 13 (API 33) agrega un cuadro de diálogo de aviso guiado por el usuario:

Entonces, ¿cuándo entras en modo buzón? Generalmente, puede entenderse simplemente como:

  • android:resizeableActivity=false Cuando la relación de aspecto declarada por la aplicación a continuación no es compatible con el contenedor (por ejemplo, el ancho de la pantalla excede android:maxAspectRatio).
  • setIgnoreOrientationRequest(true)Después de reducir la configuración del sistema para ignorar la orientación de la pantalla, abra la interfaz vertical forzada en orientación horizontal.

El punto central aquí es en realidad que resizeableActivityse utiliza para declarar si el sistema puede ajustar el tamaño de la aplicación para adaptarse a pantallas de diferentes tamaños. De hecho, estrictamente hablando resizeableActivity, no necesariamente hace que la aplicación entre en el modo Letterboxing . También está relacionado con la versión API:

  • La configuración del modo de pantalla dividida se introdujo en Android 7.0 (API 24) resizeableActivity.
  • En Android 11 (API 30) y versiones inferiores se utiliza para configurar si la App soporta el modo multiventana, si es false no lo soporta y entra en modo Letterboxing.
  • En Android 12 (API 31) y versiones superiores, sin importar resizeableActivitylo que esté configurado, la aplicación admitirá el modo de ventanas múltiples en pantallas grandes (sw >= 600 dp), por lo que solo se usa para especificar si la aplicación admite pantallas pequeñas (sw < 600dp) modo de ventanas múltiples.

sw >= 600dpSe puede entender simplemente que el ancho absoluto de su pantalla es superior a 600 ppp.

Entonces algunas personas dijeron, ¿ y si lo uso en Android 12 android:resizeableActivity=false y no encaja nada? Solo puedo decir: "Existe una cierta probabilidad" de que se bloquee directamente como se muestra en la siguiente figura .

¿Eso significa que si no uso una versión superior de TargetSDK, no necesito trabajar en él?

ActivityNo del todo, al menos necesitas realizar alguna configuración simple en tu aplicación o , porque ya en Android 7.0 (API 24), resizeableActivityel valor predeterminado de se cambia a verdadero .

Entonces, si no desea adaptarse a la interfaz de usuario del modo de pantalla grande, pero desea ingresar al modo Letterboxing, aún debe configurarlo manualmente en AndroidManifest applicationo en la configuración correspondiente .Activityandroid:resizeableActivity="false"

Además, el modo de visualización del modo Letterboxing maxAspectRatio también está relacionado con. Cuando la relación de pantalla excede, maxAspectRatiose llenará con bordes negros. Generalmente, la recomendación oficial es establecer maxAspectRatio en 2.4 (12: 5), y el método de configuración es también relacionado con el nivel API:

  • Android 8.0 y superior se pueden android:maxAspectRatioconfigurar mediante

    <activity android:name=".MainActivity"
              android:maxAspectRatio="2.4" /> 
    
  • Android 8.0 y versiones anteriores se pueden meta-data android.max_aspectconfigurar mediante

    <meta-data android:name="android.max_aspect" android:value="2.4" /> 
    

PD: Si resizeableActivityes cierto, maxAspectRationo tendrá efecto.

Como se muestra en la imagen, el botón de reinicio de Android 12L (API 32) mencionado anteriormente puede hacer que el final de la aplicación se adapte a la pantalla tanto como sea posible y reduzca el borde negro.

Otro punto es que cuando la pantalla plegable se despliega y se cierra, el sistema puede destruir y recrear toda la pantalla cuando la pantalla cambia Activity, por lo que debemos configurar android:configChangespara evitar el reinicio :

android:configChanges="screenLayout|smallestScreenSize|screenSize"

Finalmentesupports_size_changes , cabe señalar que si no desea admitir el modo de ventanas múltiples, pero es posible que se vea obligado a ingresar al modo de ventanas múltiples debido al sistema y luego no desea que se reinicie cada vez, puede configurar supports_size_changespara garantizar la continuidad de la operación.

<meta-data
    android:name="android.supports_size_changes" android:value="true" />

Así que aquí hay un breve resumen:

  • Cuando la relación de aspecto de la aplicación no es compatible con la relación de pantalla, la aplicación ingresará al modo Letterboxing cuando la dirección de rotación y el tamaño estén bloqueados.

  • resizeableActivityEl efecto depende principalmente de la versión de TargetSDK, Android 12 (API 31) y versiones superiores aún pueden ingresar al modo de pantalla dividida.

  • maxAspectRatioEl papel depende principalmente deresizeableActivity

  • Configurar android:configChangesy supports_size_changesevitar reinicios Activitypara garantizar la continuidad

Soporte oficial de adaptación

El siguiente paso es presentar el esquema de adaptación. Primero, veamos esta imagen. De hecho, el funcionario ha definido sugerencias de uso para nosotros de acuerdo con los escenarios de uso. La información clave es:

  • Componer
  • Incrustación de actividad
  • Diseño del panel deslizante

Además, la especificación del nivel de tamaño de la ventana se establece en la coincidencia oficial del tamaño de la pantalla, por ejemplo:

  • Compacto : dispositivos móviles comunes, ancho < 600 dp
  • Mediano : Pantalla vertical para pantalla plegable o tableta, 600 dp < ancho < 840 dp
  • Expandido : Pantalla expandida, tableta o tablet, etc., ancho > 840dp

Por supuesto, hay juicios basados ​​en la altura, pero la mayoría de las aplicaciones pueden crear una interfaz de usuario receptiva considerando solo la categoría de tamaño de ventana de ancho .

Componer

De hecho, Compose no necesita decir mucho. El diseño responsivo en sí mismo tiene ventajas inherentes al adaptarse a pantallas plegables. Con los parámetros de pantalla actuales proporcionados por Jetpack WindowManager API, puede lograr de manera flexible diferentes efectos de interfaz de usuario.

Por ejemplo, Compose puede usar material3-window-size-classla biblioteca y luego usarla para calculateWindowSizeClass()calcular la ventana actualWindowSizeClass , cambiando así el diseño de la interfaz de usuario:

import androidx.activity.compose.setContent
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass

class MyActivity : ComponentActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            // Calculate the window size class for the activity's current window. If the window
            // size changes, for example when the device is rotated, the value returned by
            // calculateSizeClass will also change.
            val windowSizeClass = calculateWindowSizeClass(this)
            // Perform logic on the window size class to decide whether to use a nav rail.
            val useNavRail = windowSizeClass.widthSizeClass > WindowWidthSizeClass.Compact

            // MyScreen knows nothing about window size classes, and performs logic based on a
            // Boolean flag.
            MyScreen(useNavRail = useNavRail)
        }
    }
}

También se puede adaptar a través decom.google.accompanist:accompanist-adaptive TwoPane .

TwoPaneSe proporcionan dos ranuras fijas, y la posición predeterminada de las dos ranuras es TwoPaneStrategydeterminada por , que puede decidir organizar las dos ranuras horizontal o verticalmente y configurar el intervalo entre ellas.

Puede encontrar más información en: https://github.com/google/accompanist/tree/3810fe1182cf52c6660787ae3226dfb7f5ad372a/sample/src/main/java/com/google/accompanist/sample/adaptive

Compose también puede usar FlowLayout para adaptarse a los cambios de plegado en diferentes escenarios . FlowLayout contiene FlowRowy FlowColumn. Cuando una fila (o columna) no puede caber en el contenido, se ajustará automáticamente. Esto también es muy útil en los escenarios de expansión y contracción de la pantalla plegable. .

También puedes consultar la Demo de Compose adaptándose a la pantalla plegable: https://github.com/android/compose-samples/tree/main/JetNews

Incrustación de actividad

Activity Embedding optimiza el soporte para pantallas grandes al dividir ventanas entre dos actividades o dos instancias de la misma actividad.

En teoría, la incrustación de actividades no requiere refactorización de código; puede determinar cómo la aplicación muestra su actividad (una al lado de la otra o apilada) creando un archivo de configuración XML o realizando una llamada a la API Jetpack WindowManager .

La incrustación de actividades mantiene automáticamente el soporte para pantallas pequeñas de forma predeterminada. Cuando la aplicación está en un dispositivo de pantalla pequeña, las actividades se apilarán una encima de la otra; en una pantalla grande, las actividades se mostrarán una al lado de la otra.

Sobre esta base, puede adaptarse a los cambios en la orientación del dispositivo y funcionar sin problemas en dispositivos plegables, apilando actividades que se desmontan cuando el dispositivo se pliega o despliega, como dividir y apilar en la lista de chat y las páginas de detalles del chat.

Ya sea que se trate de un dispositivo de pantalla grande superior a Android 12L (API 32) o una versión anterior de la plataforma de pantalla plegable, Jetpack WindowManager puede ayudar a crear un diseño de múltiples paneles de incrustación de actividades, que se basa en múltiples actividades en lugar de fragmentos o diseños basados ​​en vistas. (como SlidingPaneLayout) son la forma más sencilla de proporcionar una experiencia de usuario en pantalla grande sin refactorizar el código fuente .

Un ejemplo común es la pantalla dividida de lista y detalles. Para garantizar una representación de alta calidad, el sistema inicia primero la actividad de lista y luego la aplicación inicia la actividad de detalle inmediatamente. El sistema de transición espera hasta que ambas actividades se dibujen antes de mostrarlas juntas. Para el usuario, estas dos actividades se inician como una sola página.

Actualmente, la incrustación de actividades es compatible con la mayoría de los dispositivos de pantalla grande que ejecutan Android 12L (API 32) y versiones posteriores.

Usar Jetpack WindowManager para administrar y configurar la incrustación de actividades es bastante flexible . Las reglas XML se pueden preconfigurar o administrar y configurar directamente a través de la API. Para las reglas definidas en el archivo de configuración XML, establezca las siguientes propiedades:

  • splitRatio: establece la escala del contenedor. El valor es un número de coma flotante en el rango abierto (0,0, 1,0).
  • splitLayoutDirection: Especifica cómo se distribuyen los contenedores divididos entre sí. Los valores incluyen:
    • ltr: de izquierda a derecha
    • rtl: De derecha a izquierda
    • locale: ltr o rtlsegún lo determine la configuración local

Puede ver que Jetpack WindowManager tiene un soporte de configuración muy rico y flexible. En lugar de simplemente dividir la actividad en partes iguales, incluso puede configurar un marcador de posición en blanco para mostrar el marcador de posición.

Para utilizar la incrustación de actividades, debe depender de implementation 'androidx.window:window:xxx', luego agregue esta android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED propiedad al archivo de manifiesto de la aplicación <application>y establezca el valor en verdadero.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
            android:value="true" />
    </application>
</manifest>

Luego puede crear varias reglas de división a través de xml o API de WindowManager para crear reglas de división y llamarlas.

<!-- main_split_config.xml -->

<resources
    xmlns:window="http://schemas.android.com/apk/res-auto">

    <!-- Define a split for the named activities. -->
    <SplitPairRule
        window:splitRatio="0.33"
        window:splitLayoutDirection="locale"
        window:splitMinWidthDp="840"
        window:splitMaxAspectRatioInPortrait="alwaysAllow"
        window:finishPrimaryWithSecondary="never"
        window:finishSecondaryWithPrimary="always"
        window:clearTop="false">
        <SplitPairFilter
            window:primaryActivityName=".ListActivity"
            window:secondaryActivityName=".DetailActivity"/>
    </SplitPairRule>

    <!-- Specify a placeholder for the secondary container when content is
         not available. -->
    <SplitPlaceholderRule
        window:placeholderActivityName=".PlaceholderActivity"
        window:splitRatio="0.33"
        window:splitLayoutDirection="locale"
        window:splitMinWidthDp="840"
        window:splitMaxAspectRatioInPortrait="alwaysAllow"
        window:stickyPlaceholder="false">
        <ActivityFilter
            window:activityName=".ListActivity"/>
    </SplitPlaceholderRule>

    <!-- Define activities that should never be part of a split. Note: Takes
         precedence over other split rules for the activity named in the
         rule. -->
    <ActivityRule
        window:alwaysExpand="true">
        <ActivityFilter
            window:activityName=".ExpandedActivity"/>
    </ActivityRule>

</resources>

Se puede ver más en: https://developer.android.com/guide/topics/large-screens/activity-embedding

Diseño del panel deslizante

SlidingPaneLayoutAdmite la visualización de dos paneles uno al lado del otro en dispositivos de pantalla grande y los ajusta automáticamente, mientras que en dispositivos de pantalla pequeña como teléfonos móviles solo se muestra un panel, por lo que también es muy práctico en escenarios plegables.

SlidingPaneLayoutLa visualización de los paneles uno al lado del otro está determinada por el ancho de los dos paneles, por ejemplo:

  • Si mide y descubre que el panel de lista tiene un tamaño mínimo de 200 dp y el panel de detalles requiere 400 dp, los SlidingPaneLayoutdos paneles se mostrarán automáticamente uno al lado del otro siempre que el ancho disponible no sea inferior a 600 dp.
  • Si el ancho total de las subvistas excede SlidingPaneLayoutel ancho disponible en el archivo, las vistas se superpondrán.

Si las vistas no se superponen, SlidingPaneLayoutse admiten parámetros de diseño layout_weightpara que las subvistas especifiquen cómo dividir el espacio restante una vez completada la medición.

Por ejemplo, en este ejemplo SlidingPaneLayout, el diseño usa RecyclerView como panel izquierdo y FragmentContainerView como vista detallada principal para mostrar el contenido en el panel izquierdo. De hecho, es similar a la interfaz de usuario que usa TwoPane en Compose descrita anteriormente.

<!-- two_pane.xml -->
<androidx.slidingpanelayout.widget.SlidingPaneLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/sliding_pane_layout"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

   <!-- The first child view becomes the left pane. When the combined
        desired width (expressed using android:layout_width) would
        not fit on-screen at once, the right pane is permitted to
        overlap the left. -->
   <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/list_pane"
             android:layout_width="280dp"
             android:layout_height="match_parent"
             android:layout_gravity="start"/>

   <!-- The second child becomes the right (content) pane. In this
        example, android:layout_weight is used to expand this detail pane
        to consume leftover available space when the
        the entire window is wide enough to fit both the left and right pane.-->
   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/detail_container"
       android:layout_width="300dp"
       android:layout_weight="1"
       android:layout_height="match_parent"
       android:background="#ff333333"
       android:name="com.example.SelectAnItemFragment" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

También SlidingPaneLayoutfunciona con Navigationelementos Fragment para administrar y ahora también reconoce y se adapta a estados plegados y articulados, por ejemplo:

El uso de un dispositivo con una bisagra que cubre parte de la pantalla coloca automáticamente el contenido de la aplicación en ambos lados.

SlidingPaneLayoutEl modo de bloqueo también se introduce para permitir controlar el comportamiento de deslizamiento cuando los paneles se superponen, por ejemplo:

Para evitar que el usuario se deslice hacia un panel vacío, es necesario hacer clic en un elemento de la lista para cargar información sobre ese panel, pero permitirle volver a la lista, en dispositivos plegables o tabletas donde hay espacio para mostrar dos vistas una al lado de la otra. lado, se ignorará el modo de bloqueo.

Ver más en: https://developer.android.com/guide/topics/ui/layout/twopane?hl=zh-cn

adaptación personalizada

Además del esquema de adaptación oficial, quizás necesitemos un esquema de adaptación personalizado más flexible, por lo que lo primero que debemos saber es cómo identificar la mampara plegable.

Identificar biombo

Jetpack WindowManager, que sigue siendo el mencionado Jetpack WindowManager, FoldingFeatureproporciona tipos de información sobre pantallas plegables, que incluyen:

  • state: El estado plegado del dispositivo FLAT (completamente abierto) o HALF_OPENED(a medio camino entre los estados abierto y cerrado)
  • orientation: la dirección del pliegue o bisagra, HORIZONTALoVERTICAL
  • occlusionType: Si el pliegue o la bisagra oculta parte de la pantalla NONE(sin obstáculos) u FULL(obstruido)
  • isSeparating: Si el pliegue o la bisagra crean dos áreas de visualización, verdadero (medio abierto/pantalla doble) o falso

Android 11 oficialmente también brinda soporte para leer el ángulo de plegado: el nuevo tipo TYPE_HINGE_ANGLE admite y el nuevo puede monitorear el ángulo de la bisagra y proporcionar la medición del ángulo entre las dos partes del dispositivo:SensorEventSensorEvent

sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        hingeAngleSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)

En cuanto a la postura de la mampara plegable, podemos Jetpack WindowManagerconseguirla mediante la API de:

  • El dispositivo está en modo TableTop con la pantalla medio abierta y la bisagra en orientación horizontal.

    fun isTableTopMode(foldFeature: FoldingFeature) =
      foldFeature.isSeparating &&
              foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
    

  • Dispositivo en modo Libro con pantalla medio abierta y bisagra en orientación vertical

    fun isBookMode(foldFeature: FoldingFeature) =
      foldFeature.isSeparating &&
              foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
    

Por ejemplo, el equipo de Google Duo utiliza Jetpack WindowManager para identificar el estado de la pantalla plegada y luego ajusta la interfaz de usuario durante el proceso de reproducción de acuerdo con el estado desplegada.

Brevemente, se obtiene a través de la biblioteca WindowManager durante la inicialización Flow<WindowLayoutInfo>para informarle al teléfono que se encuentra actualmente en modo de escritorio y cómo obtener la posición plegada:

    override fun onStart() {
    
    
        super.onStart()
        initializePlayer()
        layoutUpdatesJob = uiScope.launch {
    
    
            windowInfoRepository.windowLayoutInfo
                .collect {
    
     newLayoutInfo ->
                    onLayoutInfoChanged(newLayoutInfo)
                }
        }
    }

    override fun onStop() {
    
    
        super.onStop()
        layoutUpdatesJob?.cancel()
        releasePlayer()
    }

Cada vez que obtiene nueva información de diseño, puede consultar las capacidades de la pantalla y verificar si el dispositivo tiene pliegues o bisagras en la pantalla actual:

private fun onLayoutInfoChanged(newLayoutInfo: WindowLayoutInfo) {
    
    
        if (newLayoutInfo.displayFeatures.isEmpty()) {
    
    
            // The display doesn't have a display feature, we may be on a secondary,
            // non foldable-screen, or on the main foldable screen but in a split-view.
            centerPlayer()
        } else {
    
    
            newLayoutInfo.displayFeatures.filterIsInstance(FoldingFeature::class.java)
                .firstOrNull {
    
     feature -> isInTabletopMode(feature) }
                ?.let {
    
     foldingFeature ->
                    val fold = foldPosition(binding.root, foldingFeature)
                    foldPlayer(fold)
                } ?: run {
    
    
                centerPlayer()
            }
        }
    }

Si la orientación es horizontal y FoldingFeature.isSeparating() devuelve verdadero, el dispositivo se puede usar en modo de escritorio, en cuyo caso se puede calcular la posición relativa del pliegue y mover el control a esa posición; de lo contrario, se mueve a 0 (parte inferior de la pantalla). .

    private fun centerPlayer() {
    
    
        ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0)
        binding.playerView.useController = true // use embedded controls
    }

    private fun foldPlayer(fold: Int) {
    
    
        ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)
        binding.playerView.useController = false // use custom controls
    }

adaptación del tamaño de la ventana

En la adaptación de dispositivos plegables, la adquisición del tamaño de la ventana también es muy importante . Sin embargo, de hecho, desde el desarrollo de Android, algunas API han quedado obsoletas o todavía se utilizan incorrectamente. Para la adaptación de configuraciones de pantalla grande, Debido a que existen situaciones como Letterboxing, de hecho, la antigua API ya no puede satisfacer las necesidades.

Las API de Display actualmente obsoletas y a menudo mal utilizadas son:

  • obtenerMetricas()
  • obtenerTamaño()
  • obtenerMetricasReales()
  • obtenerTamañoReal()
  • obtenerTamañoRect()
  • obtenerAncho()
  • obtener altura()

Las API de View que se utilizan de forma indebida son:

  • getWindowVisibleDisplayFrame()
  • obtenerUbicación en pantalla

Por ejemplo, y Display han quedado obsoletos en API 30 en favor de nuevos métodos.getSize() getMetrics()WindowManager

Android 12 (API 31) deja de estar disponible Displayy métodos más nuevos getRealSize()y getRealMetrics()relacionados .getMaximumWindowMetrics()

Debido a que el tamaño real de su aplicación no es necesariamente consistente con el tamaño real de la pantalla en pantallas plegables y pantallas múltiples, no puede confiar en el tamaño de la pantalla física para ubicar los elementos de la interfaz de usuario. Ahora se recomienda confiar en la API de WindowMetrics :

  • Plataforma:
    • getCurrentWindowMetrics()
    • getMaximumWindowMetrics()
  • Mochila propulsora:
    • Calculadora de WindowMetrics#computeCurrentWindowMetrics()
    • Calculadora de WindowMetrics#computeMaximumWindowMetrics()

La plataforma aquí es que Android 11 (API 30) introdujo WindowManagerel método para proporcionar límites para las aplicaciones que se ejecutan en modo de ventanas múltiples:

  • getCurrentWindowMetrics(): Devuelve el objeto de estado de ventana actual del sistema.WindowMetrics
  • getMaximumWindowMetrics(): Devuelve el estado de ventana máximo del sistema.WindowMetrics

Los métodos de la biblioteca Jetpack WindowManager computeCurrentWindowMetrics()y, computeMaximumWindowMetrics() respectivamente, proporcionan una funcionalidad similar, pero son compatibles con versiones anteriores hasta API 14.

val windowMetrics = context.createDisplayContext(display)
                    .createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION, null)
                    .getSystemService(WindowManager::class.java)
                    .maximumWindowMetrics

Por tanto, a través de esto WindowManager, podemos gestionar dinámicamente el cambio de tamaño de la ventana y reconocer el cambio de la pantalla plegable, por ejemplo, para onConfigurationChanged()configurar el diseño de la aplicación del tamaño de ventana actual:

override fun onConfigurationChanged(newConfig: Configuration) {
    
    
    super.onConfigurationChanged(newConfig)
    val windowMetrics = WindowMetricsCalculator.getOrCreate()
        .computeCurrentWindowMetrics(this@MainActivity)
    val bounds = windowMetrics.getBounds()
    ...
}

Finalmente, en cuanto a personalización y adaptación de ventanas, es un tema común, por ejemplo:

  • Utilice wrap_content, match_parent evite la codificación
  • Úselo para ConstraintLayoutcrear el diseño raíz, que es conveniente para cambios de tamaño de pantalla, y la vista se mueve y estira automáticamente
  • Establezca la propiedad de applicationo en el AndroidManifest de la aplicación para admitir el cambio de tamaño y el diseño responsivo/adaptativo.activityandroid:resizeableActivitytrue
  • res/layout/Se pueden proporcionar diseños adaptables creando directorios como layout-w600dp
  • ·····

Múltiples ventanas y ciclo de vida.

Dado que la pantalla plegable está puramente en múltiples áreas, puede haber múltiples ventanas, o incluso más de dos ventanas, en este caso, naturalmente, habrá problemas de adaptación del ciclo de vida, como que varias aplicaciones accedan a la cámara al mismo tiempo.

Con respecto al proceso de ventanas múltiples, puede presentar brevemente:

  • Android 7.0 admite pantalla dividida: muestra dos ventanas a la izquierda y a la derecha/arriba y abajo

  • Android 8.0 admite el modo de imagen en imagen. En este momento, la imagen en imagen Activityestá en primer plano, pero en Pausedestado

  • Android 9.0 (API 28) y versiones anteriores: en ventanas múltiples, solo la aplicación enfocada está en el Resumedestado y otras visibles Activitytodavía están en el Pausedestado

  • Android 10.0 (API 29): en modo de ventanas múltiples, cada una Acttivityestá en Resumedestado

Mira, el ciclo de vida es diferente en diferentes niveles de API , por lo que para resolver el problema de que solo la aplicación enfocada está en el estado de Android 9.0 e inferior Resume, se pueden agregar los siguientes atributos en el lado de la aplicación y se admiten varios elementos. agregando manualmente Resumed:

<meta-data
    android:name="android.allow_multiple_resumed_activities" android:value="true" />

Se le conoce comúnmente como estado de reanudación múltiple.

Para admitir el estado de reanudación múltiple, naturalmente se requiere una nueva devolución de llamada del ciclo de vida, es decironTopResumedActivityChanged() .

El sistema llama a este método cuando una actividad gana o pierde la posición superior de Reanudar, como cuando se utiliza un recurso único compartido, como un micrófono o una cámara:

override fun onTopResumedActivityChanged(topResumed: Boolean) {
    
    
    if (topResumed) {
    
    
        // Top resumed activity
        // Can be a signal to re-acquire exclusive resources
    } else {
    
    
        // No longer the top resumed activity
    }
}

Por ejemplo, para la escena que usa la cámara, para el paquete anterior, Android 10 (nivel de API 29) CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged()proporciona un mensaje de devolución de llamada para indicar que puede ser el momento de intentar acceder a la cámara.

Es importante tener en cuenta aquí que el uso resizeableActivity=false no garantiza el acceso exclusivo a la cámara , ya que otras aplicaciones que usan la cámara pueden estar abiertas en varios monitores (pantalla dividida).

CameraDevice.StateCallback#onDisconnected()Por lo tanto, la aplicación necesita manejar comportamientos relacionados después de recibir la devolución de llamada. Si la API aún funciona después de onDisconnected, el sistema generará un archivo CameraAccessException.

De hecho, siempre que haga un buen juicio a través de la devolución de llamada, esta experiencia de cambio de "enfoque" es perfecta.

En el modo de ventanas múltiples, Android puede deshabilitar o ignorar funciones que no son aplicables a actividades que comparten la pantalla del dispositivo con otras actividades o aplicaciones.

Además, Activity también proporciona algunos métodos para admitir el modo de ventanas múltiples:

  • isInMultiWindowMode()Ya sea para estar en modo de ventanas múltiples.

  • isInPictureInPictureMode()Si la actividad está en modo imagen en imagen.

    Nota: El modo Imagen en imagen es un caso especial del modo de ventanas múltiples; si isInPictureInPictureMode()devuelve verdadero, isInMultiWindowMode()también devolverá verdadero.

  • onMultiWindowModeChanged()El sistema llama a este método cuando la Actividad entra o sale del modo de ventanas múltiples.

    Si la Actividad ingresa al modo de ventanas múltiples, el sistema pasa a este método un valor de verdadero; si la Actividad sale del modo de ventanas múltiples, el sistema pasa a este método un valor de falso.

  • onPictureInPictureModeChanged()El sistema llama a este método cuando la Actividad ingresa o sale del modo imagen en imagen.

    El sistema pasa a este método un valor verdadero si la Actividad ingresa al modo de imagen en imagen y un valor falso si la Actividad sale del modo de imagen en imagen.

Fragment también proporciona métodos similares, como Fragment.onMultiWindowModeChanged().

Aleteo

A partir de 3.13, Flutter también agregó una nueva API para coincidir con las diversas propiedades de la pantalla #41685 , donde el nuevo FlutterView.display devuelve un objeto Display , y el objeto Display informará el tamaño físico de la pantalla, la proporción de píxeles del dispositivo y la actualización. tasa:

  
  void didChangeMetrics() {
    
    
    final ui.Display? display = _display;
    if (display == null) {
    
    
      return;
    }
    if (display.size.width / display.devicePixelRatio < kOrientationLockBreakpoint) {
    
    
      SystemChrome.setPreferredOrientations(<DeviceOrientation>[
        DeviceOrientation.portraitUp,
      ]);
    } else {
    
    
      SystemChrome.setPreferredOrientations(<DeviceOrientation>[]);
    }
  }

El objetivo principal de esta nueva API es lo que se mencionó anteriormente, porque una vez que Flutter ingresa al modo Letterboxing, MediaQueryes posible que Flutter no pueda obtener el tamaño completo de la pantalla disponible , por lo que la nueva API es proporcionar el tamaño real después de plegar y cambiar a Space. para que los desarrolladores se adapten.

Además, todavía se está sincronizando la compatibilidad con múltiples tamaños de pantalla en Flutter #125938#125939 , si está interesado, también puede prestar atención.

por fin

Se puede ver que todas las personas aquí son camaradas muy pacientes. Esta encuesta involucra mucho contenido y cubre una amplia gama de puntos de conocimiento. Algunos pueden no ser lo suficientemente profundos. En general, todavía proporciona direcciones e ideas, que involucran principalmente:

  • Representación del modo Letterboxing compatible
  • resizeableActivitycomportamiento diferente de configuraciones como
  • Esquema de adaptación para Compose /Activity Embedding /SlidingPaneLayout
  • Juicio de pantalla plegable, adaptación de ventana y compatibilidad del ciclo de vida.
  • API de aleteo

Creo que todavía hay muchas apps que no piensan adaptarse a la pantalla plegable, al fin y al cabo "no es imposible de usar", pero después de entender este artículo, al menos te puede dar algo de confianza, al menos eso parece. que si realmente quieres adaptarte, no es gran cosa. Cosas que no se pueden hacer.

Si tienes algo más que decir, bienvenido a dejar un comentario.

Supongo que te gusta

Origin blog.csdn.net/ZuoYueLiang/article/details/132451593
Recomendado
Clasificación