Resumen
Algunas apps están diseñadas solo para modo horizontal, por ejemplo una estación de cámara en una tableta. Aun así, los usuarios esperan que la imagen se vea derecha sea cual sea la posición del dispositivo: con los botones a la izquierda o a la derecha.
KlugLayout fija la orientación de pantalla de Android en horizontal por ambos lados. La app permanece en modo horizontal, pero gira 180 grados con el dispositivo. También informa de la rotación actual de la pantalla, para que una app pueda adaptar su propio contenido, como la imagen de una cámara, a cómo se sostiene el dispositivo.
Los plegables cambian la pantalla mientras la app está en ejecución. Un Galaxy Z Fold o un Pixel Fold es una tableta al abrirse y un teléfono al cerrarse; un Galaxy Z Flip pasa de su pantalla principal alargada a una pequeña pantalla exterior casi cuadrada. Cuando el dispositivo mueve la app a la otra pantalla, el escenario de AIR no siempre recibe un evento de cambio de tamaño, y los valores iniciales de tamaño de pantalla y de recorte de la cámara siguen siendo los de la primera pantalla. KlugLayout lo vigila de forma nativa e indica a la app en qué tipo de pantalla se encuentra ahora.
KlugLayout se usa a diario en nuestras apps en tabletas Android, nuestra prueba de concepto.
Plegables
KlugLayout clasifica la ventana actual en una de tres clases y envía un evento cada vez que cambia la clase:
| tablet | La pantalla grande. Ejemplo: un Galaxy Z Fold o Pixel Fold abierto. |
| phone | Una pantalla del tamaño de un teléfono. Ejemplo: un Fold cerrado o la pantalla principal de un Flip. |
| cover | Una pantalla exterior pequeña. Ejemplo: la pantalla exterior de un Galaxy Z Flip. |
Cuando un Fold está cerrado, el bloqueo horizontal se suspende para que la pantalla exterior del tamaño de un teléfono pueda usar el modo vertical, y vuelve cuando el dispositivo se abre de nuevo. La app también recibe el tamaño y el recorte de cámara de la pantalla actual, de modo que un diseño creado después de plegar encaja con la pantalla en la que se muestra.
En nuestras apps, el lado de visualización cierra sesión y vuelve a iniciarla en cada cambio de pliegue o de pantalla exterior, de modo que los streams en directo siempre empiezan limpiamente en la nueva pantalla. En la pantalla exterior del Flip, la interfaz se distribuye como en un teléfono normal y se reduce para encajar.
Probado en el emulador de Android con los perfiles de dispositivo Pixel 9 Pro Fold y Galaxy Z Flip.
iPhone Duo
El iPhone Duo es el primer plegable de Apple: un teléfono al cerrarse y una pequeña tableta al abrirse. AIR no lo sigue por sí solo. Cuando el dispositivo se abre, iOS puede mantener la app en su ventana de teléfono con bandas negras, no llega ningún evento de cambio de tamaño y los valores de pantalla iniciales siguen siendo los de la pantalla exterior.
KlugLayout detecta en iOS en qué pantalla está la app y envía el mismo evento DEVICE_TYPE_CHANGED que en Android: "phone" cuando el Duo está cerrado, "tablet" cuando está abierto. El mismo código sirve para ambas plataformas.
Estado: los diseños básicos están resueltos. Funcionan los diseños con el dispositivo cerrado y abierto, plegar y desplegar mientras la app está en ejecución, girar el dispositivo abierto y los diálogos nativos centrados. Los ajustes de streaming y de cámara aún deben probarse en el dispositivo real.
No hay motivo para esperar a AIR 52: a partir de abril de 2027, las subidas a la App Store deberán compilarse con el SDK de iOS 27 y, desde entonces, una app AIR se encuentra con el iPhone Duo como el plegable que es. KlugLayout lo gestiona hoy, con AIR 51.4.1.
Probado en el simulador de Xcode 27 (iPhone Duo) con HARMAN AIR 51.4.1.
Funciones
- Autónoma: una ANE, sin biblioteca central ni ANE de dependencias que añadir
- Modo horizontal por ambos lados, girando con el dispositivo
- Detección de plegado: tableta, teléfono o pantalla exterior del Flip, con un evento en cada cambio
- iPhone Duo: teléfono al cerrarse, tableta al abrirse, con el mismo evento que en Android
- Bloqueo horizontal liberado automáticamente mientras un Fold está cerrado
- Tamaño y recorte de cámara de la pantalla actual, también tras plegar
- Sin cambio a modo vertical
- Rotación actual de la pantalla: 0, 90, 180 o 270 grados
- Activo desde el inicio de la app
- No hace nada en otras plataformas, por lo que el código puede permanecer en una app multiplataforma
Plataformas compatibles
Primeros pasos
Añade la extensión al descriptor de tu aplicación:
<extensions>
<extensionID>com.klug.layout</extensionID>
</extensions>
Las apps dirigidas a Android 16 deberían además permitir una orientación fija en pantallas grandes con esta propiedad dentro de <application> de las adiciones al manifiesto:
<property android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY" android:value="true"/>
Ejemplos de código
Cuatro flujos completos, tomados de la API que funciona en nuestras apps. Las llamadas devuelven de inmediato; los cambios de pantalla llegan como un StatusEvent. Documentación completa →
import com.klug.layout.KlugLayout; import flash.display.StageAspectRatio; import flash.events.StageOrientationEvent; var layout:KlugLayout = KlugLayout.getInstance(); // isSupported is false on desktop platforms: every call below is then a no-op, // so this code can stay in a cross-platform app stage.autoOrients = true; stage.setAspectRatio( StageAspectRatio.LANDSCAPE ); // first AIR's landscape ... layout.setSensorLandscape(); // ... then both sides on Android // (on iOS LANDSCAPE already allows both sides) // turn your own content, such as a camera picture, with the device updatePicture(); stage.addEventListener( StageOrientationEvent.ORIENTATION_CHANGE, function( e:StageOrientationEvent ):void { updatePicture(); }); function updatePicture():void { var rotation:int = layout.displayRotation; // 0, 90, 180 or 270; -1 = unknown if ( rotation != -1 ) rotateCameraPicture( rotation ); // your app function }
import flash.events.StatusEvent; layout.addEventListener( StatusEvent.STATUS, onLayout ); layout.watchFold( stage ); // pass the stage: iOS needs it for the iPhone Duo // events report changes only; read the class once for the start var startClass:String = layout.deviceClass; // "phone", "tablet", "cover" or "" (unknown) showLayoutFor( startClass != "" ? startClass : chooseFromStage() ); // your own default function onLayout( e:StatusEvent ):void { if ( e.code != KlugLayout.DEVICE_TYPE_CHANGED ) return; showLayoutFor( e.level ); // "tablet" or "phone"; "cover" on a Galaxy Z Flip (Android) } // fold-only behaviour, kept away from normal phones and tablets if ( layout.isFoldable ) enableFoldHints(); // your app function
function showLayoutFor( deviceClass:String ):void { // size of the current window in pixels, also after the device moved the app // to its other display; -1 = unknown, then fall back to the stage var w:int = layout.windowWidth > 0 ? layout.windowWidth : stage.stageWidth; var h:int = layout.windowHeight > 0 ? layout.windowHeight : stage.stageHeight; // top inset of the current display (Android: camera cutout, from Android 9) var top:int = Math.max( 0, layout.safeInsetTop ); // -1 = unknown if ( deviceClass == "tablet" ) buildTabletViews( w, h, top ); // your app functions else { buildPhoneViews( w, h, top ); if ( deviceClass == "cover" ) scaleToFit( w, h ); // phone layout, scaled down } }
// while a Fold is closed the lock is suspended on its own and comes back when it is opened. // release() is for screens where the app itself leaves landscape (Android; no-op on iOS). function openPortraitScreen():void { layout.release(); // remove the landscape lock stage.setAspectRatio( StageAspectRatio.PORTRAIT ); // AIR decides again } function openStationScreen():void { stage.setAspectRatio( StageAspectRatio.LANDSCAPE ); layout.setSensorLandscape(); // both landscape sides again }
Resumen de la API
| KlugLayout.getInstance() | Devuelve la instancia única. |
| isSupported | Verdadero en Android e iOS. Falso en el resto; en ese caso cada llamada no hace nada. |
| setSensorLandscape() | Modo horizontal por ambos lados, girando con el dispositivo. Llámalo después de stage.setAspectRatio(StageAspectRatio.LANDSCAPE). Android; en iOS LANDSCAPE ya permite ambos lados. |
| displayRotation | La rotación actual de la pantalla en grados (0, 90, 180, 270), o -1. |
| watchFold( stage ) | Empieza a vigilar los cambios de plegado y de pantalla exterior, también sin bloqueo horizontal. En iOS se necesita el stage para el iPhone Duo. |
| isFoldable | Verdadero en hardware plegable: plegables Android (Android 11 o posterior) y el iPhone Duo. |
| release() | Elimina el bloqueo horizontal, por ejemplo cuando la app cambia a un diseño de teléfono. Android; no hace nada en iOS. |
| deviceClass | La clase actual: "phone", "tablet" o "cover"; "" mientras se desconoce. En iOS disponible tras watchFold(stage) en el iPhone Duo. |
| windowWidth / windowHeight | Tamaño de la ventana actual en píxeles, siguiendo al dispositivo a su otra pantalla; -1 si se desconoce. |
| safeInsetTop | Margen superior de la pantalla actual en píxeles: el recorte de la cámara en Android (Android 9 o posterior), el área segura superior en iOS; -1 si se desconoce. |
| DEVICE_TYPE_CHANGED | Código de StatusEvent enviado en cada cambio de clase. |
Eventos
| deviceTypeChanged | phone · tablet · cover (cover: pantalla exterior del Galaxy Z Flip, Android). Se envía tras watchFold(stage), en Android también tras setSensorLandscape(); solo cuando hay cambios. |
Compra KlugLayout
Una licencia anual para una aplicación AIR, con todas las actualizaciones durante 12 meses. Tras el pago recibes la extensión, la biblioteca ActionScript y la documentación por e-mail, normalmente en un día hábil. Licencia y términos
- Licencia para una aplicación AIR
- Todas las actualizaciones durante 12 meses
- Sin soporte
- Licencia para una aplicación AIR
- Todas las actualizaciones durante 12 meses
- Respuesta por e-mail en un plazo de 7 días
- Licencia para una aplicación AIR
- Todas las actualizaciones durante 12 meses
- Respuesta por e-mail en un plazo de 48 horas
Requisitos
| AIR SDK | 51.x (HARMAN) |
| Android | Android 6.0 (API 23) o posterior; recorte de cámara desde Android 9 (API 28) |
| iOS | iOS 13 o posterior; iPhone Duo probado con AIR 51.4.1 |
| Permisos | Ninguno |