Le sens de l’orientation

La gestion des changements d’orientation de l’appareil constitue un point épineux du développement d’applications mobiles. Détecter les basculements entre mode portrait et paysage et ensuite s’y adapter peut demander un effort de développement important si on ne veut pas se retrouver la tête à l’envers !

Maison-a-l-envers-321160

Certaines applications résolvent ce problème de manière drastique en ne supportant qu’une seule orientation. L’api de Flex 4.5 propose différents points d’entrée pour détecter les changements de résolution, mais les tests m’ont montré que le comportement varie entre ADL (l’environnement de test depuis l’IDE), iOS et Android !

Pas facile de s’y retrouver.

Connaître la taille de l’écran

Commençons par récapituler les fonctions retournant de l’information sur la taille de l’écran, de l’application ou de la scène.

flash.system.Capabilities :

screenResolutionY et screenResolutionX

Retournent la résolution verticale et horizontale maximale de l’écran. Ne prend pas en compte la mise à l’échelle automatique.

FlexGlobals.topLevelApplication :

« Cette propriété est définie sur une référence à l’application de plus haut niveau dans le constructeur de l’application de plus haut niveau. »

height et width retournent les dimensions de cette application.

« Classe SystemManager qui gère une fenêtre d’application. Chaque application qui s’exécute sur le bureau ou dans un navigateur comporte une zone d’affichage des effets visuels de l’application. Il peut s’agir d’une fenêtre dans le système d’exploitation ou d’une zone au sein du navigateur. Il s’agit d’une « fenêtre d’application », ce qui est différent d’une occurrence de la classe mx.core.Application, qui correspond à la fenêtre principale, ou de niveau supérieur, d’une application. »

systemManager.screen.height et systemManager.screen.width

Ces deux catégories de propriétés tiennent compte de la mise à l’échelle automatique

stage

« La classe Stage représente la zone de dessin principale.

Pour le contenu SWF s’exécutant dans le navigateur (dans Flash® Player), la scène représente la zone entière où le contenu Flash est affiché. Pour le contenu s’exécutant dans AIR sur des systèmes d’exploitation de bureau, chaque objet NativeWindow a un objet Stage correspondant.

Il est impossible d’accéder globalement à l’objet Stage. Vous devez y accéder à l’aide de la propriété stage d’une occurrence d’objet DisplayObject. »

height et width

stageHeight et stageWidth

fullScreenHeight et fullScreenWidth

Les différentes propriétés de stage ne prennent pas en compte la mise à l’échelle automatique

Attention stage n’est pas toujours disponible, particulièrement sur viewActivate : il faut attendre que l’événement addedToStage ait été émis.

Détecter les changements d’orientation

Il existe plusieurs approches pour détecter les changements d’orientation :

  • utiliser les états landscape et portrait et les événements remontés automatiquement lors des changements d’état;
  • se mettre à l’écoute de l’événement resize émis lors d’un redimensionnement de stage;
  • gérer les événements StageOrientationEvent. Attention, sur Android StageOrientationEvent.ORIENTATION_CHANGING n’est pas émis.

 

    Le projet

    C’est cette dernière approche que nous allons étudier dans un projet simple composé d’une seule vue.

    Afin de mettre en évidence les différentes valeurs retournées, nous utilisons une mise à l’échelle automatique de l’application en fixant la propriété applicationDPI= »160″.

    Sur l’événement addedToStage de la vue, nous nous mettons à l’écoute des changements d’orientation.

    private function addedToStageHandler(event:Event):void{

    stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE,orientationChangeHandler);

    }

    Dans la fonction orientationChangeHandler, nous utilisons des fonctions statiques d’une classe utilitaire pour tracer les valeurs obtenues par les différentes fonctions retournant des informations de largeur et de hauteur (voir paragraphe précédent).

    private function orientationChangeHandler(e:StageOrientationEvent):void{

    Mobile.traceResolution();

    Mobile.traceStageDimensions();

    trace(« orientationChangeHandler: « +e.afterOrientation+ » « +Mobile.getWidth());

    }

    Tests

    iOS

    Commençons par effectuer des tests pour iOS.

    Nous testons dans l’ordre :

    • une simulation d’iPhone dans ADL
    • sur un iPhone 4
    • sur un iPad

    ADL : iPhone 4, 640×960

    Chargement initial

    viewActivateHandler: IOS Adobe Macintosh

    commentaire : la troisième valeur informe du périphérique réel, ici un MacBook.

    Capabilities height / width: 1200 1920

    commentaire : correspond aux dimensions de l’écran réel connecté au MacBook

    systemManager screen.height / screen.width: 480 / 320

    commentaire : mise à l’échelle automatique

    topLevelApplication height / width: 480 / 320

    stage.fullScreenHeight / stage.fullScreenWidth: 960 / 640

    stageHeight / stageWidth: 960 / 640

    stage.height / stage.width: 960 / 640

    commentaire : les dimensions retournées par stage ne sont pas mises à l’échelle


    Rotation de l’écran

    systemManager screen.height / screen.width: 320 / 480

    topLevelApplication height / width: 320 / 480

    stage.fullScreenHeight / stage.fullScreenWidth: 640 / 960

    stageHeight / stageWidth: 640 / 960

    stage.height / stage.width: 640 / 960

    orientationChangeHandler: rotatedRight 480

    la nouvelle largeur détectée est 480

     

      iPhone 4, 640×960

      viewActivateHandler: IOS Adobe iOS

      Capabilities height / width: 960 640

      commentaire : véritables dimensions de l’écran

      systemManager screen.height / screen.width: 480 / 320

      topLevelApplication height / width: 480 / 320

      commentaire : dimensions après application de la mise à l’échelle


      Rotation de l’écran

      systemManager screen.height / screen.width: 320 / 480

      topLevelApplication height / width: 320 / 480

      stage.fullScreenHeight / stage.fullScreenWidth: 640 / 960

      stageHeight / stageWidth: 640 / 960

      stage.height / stage.width: 640 / 960

      orientationChangeHandler: rotatedRight 480

      la nouvelle largeur détectée est 480

       

      iPad, 768×1024

      viewActivateHandler: IOS Adobe iOS

      Capabilities height / width: 1024 768

      systemManager screen.height / screen.width: 1024 / 768

      topLevelApplication height / width: 1024 / 768

       

      Rotation de l’écran

      systemManager screen.height / screen.width: 768 / 1024

      topLevelApplication height / width: 768 / 1024

      stage.fullScreenHeight / stage.fullScreenWidth: 768 / 1024

      stageHeight / stageWidth: 768 / 1024

      stage.height / stage.width: 768 / 1024

      orientationChangeHandler: rotatedRight 1024

      la nouvelle largeur détectée est 1024

       

      Conclusion iOS

      Pas de problème détecté sur iOS, les valeurs retournées sur les véritables périphériques sont conformes aux valeurs attendues et à celles obtenues dans ADL.

       

      Android

      Pour Android, les tests seront effectués avec :

      • une simulation d’HTC Desire dans ADL
      • un HTC Desire en Geingerbread 2.3
      • un Eee Pad Transformer en Honeycom 3.2.1

       

        ADL, HTC Desire 480×800

        Capabilities height / width: 1200 1920

        systemManager screen.height / screen.width: 533.3333333333334 / 320

        topLevelApplication height / width: 533.3333333333334 / 320

        commentaire : ici également, dimensions après mise à l’échelle automatique

        stage.fullScreenHeight / stage.fullScreenWidth: 800 / 480

        stageHeight / stageWidth: 800 / 480

        stage.height / stage.width: 800 / 480

         

        Rotation de l’écran

        systemManager screen.height / screen.width: 320 / 533.3333333333334

        topLevelApplication height / width: 320 / 533.3333333333334

        stage.fullScreenHeight / stage.fullScreenWidth: 480 / 800

        stageHeight / stageWidth: 480 / 800

        stage.height / stage.width: 480 / 800

         

        HTC Desire 480×800

        viewActivateHandler: AND Android Linux

        Capabilities height / width: 800 480

        systemManager screen.height / screen.width: 533.3333333333334 / 320

        topLevelApplication height / width: 533.3333333333334 / 320

         

        Rotation de l’écran

        systemManager screen.height / screen.width: 533.3333333333334 / 320

        commentaire : les valeurs retournées sont celles AVANT la rotation

        topLevelApplication height / width: 320 / 533.3333333333334

        stage.fullScreenHeight / stage.fullScreenWidth: 480 / 800

        stageHeight / stageWidth: 480 / 800

        stage.height / stage.width: 800 / 800

        commentaire : le smartphone est devenu carré !

        orientationChangeHandler: rotatedRight 533.3333333333334

        La fonction statique Mobile.getWidth() va se charger d’effectuer la correction.

         

        Tablet Asus EeePad 800×1280

        viewActivateHandler: AND Android Linux

        Capabilities height / width: 1232 800

        systemManager screen.height / screen.width: 1232 / 800

        topLevelApplication height / width: 1232 / 800

         

        Rotation de l’écran

        systemManager screen.height / screen.width: 1232 / 800

        commentaire : les valeurs retournées sont celles AVANT la rotation

        topLevelApplication height / width: 1232 / 800

        commentaire : les valeurs retournées sont celles AVANT la rotation

        stage.fullScreenHeight / stage.fullScreenWidth: 752 / 1280

        stageHeight / stageWidth: 752 / 1280

        stage.height / stage.width: 1232 / 800

        commentaire : les valeurs retournées sont celles AVANT la rotation

        orientationChangeHandler: default 800

        commentaire : faux !


        Les boutons systèmes

        Vous avez peut-être noté la variation de la hauteur active sur la tablette d’Asus, elle est toujours inférieure à la taille attendue :

        • en paysage : stageHeight / stageWidth: 752 / 1280

        soit une différence de 48 pixels

        • en portrait : stage.height / stage.width: 1232 / 800

        soit une différence également de 48 pixels.

        Cet écart de 48 pixels s’explique par la gestion de la barre de boutons système.

        Sur le HTC Desire en Android Gingerbread (2.3), les boutons systèmes sont physiques, au-dessous de l’écran.

        291_8134_2

         Sur la tablette Asus en Android Honeycomb, les boutons systèmes sont virtuels et sont toujours affichés au bas de l’écran quelle que soit l’orientation, diminuant ainsi la surface utile.

        P20111023182322

        Il faudra en tenir compte dans nos corrections.

         

        Corrections dans Mobile.getWidth()

        L’algorithme est le suivant :

        • sur iOS on ne fait rien : les valeurs retournées sont correctes;
        • sur Android, on vérifie que la largeur retournée est cohérente avec l’orientation détectée. Pour connaître l’orientation, on fait appel aux fonctions stage.fullScreenHeight() et stage.fullScreenWidth() qui retournent toujours une valeur correcte, mais ne sont pas utilisables directement car non mise à l’échelle.
        • si une incohérence est détectée, on intervertit hauteur et largeur.
        • enfin, on recalcule la hauteur de la barre système afin de la retrancher ou de l’ajouter suivant le cas à la dimension obtenue.
        • Ce dernier point est problématique :
          • pour calculer la hauteur de la barre système, il faut mettre à l’échelle une dimension retournée par une propriété stage. Nous calculons le facteur de mise à l’échelle scaleFactor=_app.runtimeDPI/_app.applicationDPI;
          • nous n’avons pas trouvé de propriétés renvoyant les dimensions hors barre système. Le calcul s’effectue donc lors du changement d’orientation en se basant sur la hauteur dans l’orientation précédente ! Si la hauteur de la barre système diffère entre portrait et paysage, ce hack ne fonctionnera plus…

        Voici un exemple de calcul :

        public static function getWidth():Number{

        var newWidth:Number=_app.width;

        var stage:Stage=FlexGlobals.topLevelApplication.stage;

        if(stage && !Mobile.iOS()){// bug uniquement Android

        var systemBarHeight:Number=0;

        var isPortrait:Boolean=(stage.fullScreenHeight>stage.fullScreenWidth);

        if(isPortrait){

        if(_app.width>_app.height){

        newWidth=_app.height;

        // ajoute hauteur barre system

        systemBarHeight=_app.width-(stage.fullScreenHeight/Mobile.calculateScale());

        trace(« systemBarHeight : « +systemBarHeight);

        newWidth+=systemBarHeight;

        }

          }

        else{

        if(_app.height>_app.width){

        newWidth=_app.height;

        // ajoute hauteur barre system

        systemBarHeight=(stage.fullScreenWidth/Mobile.calculateScale())-_app.height;

        trace(« systemBarHeight : « +systemBarHeight);

        newWidth+=systemBarHeight;

        }

        }

        }

        trace(« Corrected width « +Math.round(newWidth)+ » was « +_app.width);

        return Math.round(newWidth);

        }

         

        Bibliographie

        • La gestion de l’orientation de l’écran est abordée dans le chapitre 14 de mon livre « Flex 4.5 pour mobiles« . Mais la présentation est incomplète car je n’avais pas constaté au moment de la rédaction la problématique sur la tablette Eee Pad. J’ai généré une nouvelle version du projet final comprenant cette correction, voir le lien dans le paragraphe Téléchargements.
        • Voir l’article de Michael Chaize Tips for Flex Mobile Apps pour une utilisation de l’événement ResizeEvent de la vue.

        Téléchargements

        • le projet CheckOrientation utilisé dans cet article.
        • le projet WesternsTabletCSS qui, en acceptant de la redondance de code, adapte l’exemple du livre Flex 4.5 pour mobiles au fonctionnement sur tablette. L’architecture multi-écrans et hors du cadre de l’ouvrage.

         

        Laisser un commentaire

        Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

        Logo WordPress.com

        Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

        Image Twitter

        Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

        Photo Facebook

        Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

        Photo Google+

        Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

        Connexion à %s

        %d blogueurs aiment cette page :