Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

Design Gérer les rotations et les form factors avec les Xamarin.Forms

Détecter les form-factors ainsi que l’orientation des devices permet de concevoir de meilleurs designs, voyons comment procéder !

Design et form factors

Concevoir des Apps séduisantes sachant s’adapter à tous les form factors, du petit smartphone à la grande tablette est une gageure. Il est évident qu’on ne peut pas mettre le même contenu sur un timbre-poste et sur une page A4. Et pourtant… Passer de son smartphone à sa tablette voire à son PC est une activité régulière de l’utilisateur et ce avec les mêmes Apps si possible.

Profiter de l’espace disponible pour offrir plus d’information, tout comme préserver l’essentiel même sur une petite diagonale est un art, celui du design…

Au-delà de ces différences évidentes de tailles (et de résolutions) s’ajoute la prise en compte des rotations.

Un smartphone de 5” mis en mode portrait doit afficher l’information autrement qu’en mode paysage dont la largeur doit profiter à certaines données et dont le rétrécissement de la hauteur ne doit pas nuire à celles qui devraient apparaître…

La différence peut être encore plus marquée sur une tablette de 10” ou plus.

L’avènement de systèmes de développement cross-plateforme tel que Xamarin.Forms soulève des problèmes de design qui ne se posaient pas aux développeurs habitués aux PC ou aux Mac. C’est toute une éducation à faire !

Et cela réclame plein d’astuces et de patterns nouveaux à mettre en œuvre pour faire face à ce défi…

Détecter l’orientation de la device

Il existe plusieurs moyens de détecter le changement d’orientation d’une device, on peut le faire soit :

  • Via OnSizeAllocated
  • Via des notifications spécifiques à chaque plateforme

La première méthode est parfaitement adaptée à un projet Xamarin.Forms, la seconde à un projet mixte ou visant une cible unique avec Xamarin.Droid ou Xamarin.iOS par exemple.

image

Ces deux approches débouchent sur des mises en œuvre différentes, comme une ContentPage de base “orientation change aware” ou une série de codes natifs utilisés via les Dependency Services. Il existe ainsi des plugins Xamarin.Forms qui utilisent cette dernière méthode tout comme des exemples de ContentPage de base dont il faut hériter ensuite pour bénéficier du service de détection.

Les choses se ressemblent mais ne sont pas totalement équivalentes. Il convient de choisir correctement en fonction des spécificités du projet.

Toutefois la méthode pure Xamarin.Forms possède l’intérêt de couvrir l’ensemble des besoins de façon simple au prix, il est vrai, d’un héritage à mettre en place. Mais au gain que la méthode fonctionne pour toutes les plateformes avec un seul code...

Le site de Xamarin offre une description assez détaillée de la version la plus complexe (code natif et injection de dépendances) que vous trouverez ici : device-orientation

Concernant les plugins en voici un pour exemple : Xamarin.Plugin.DeviceOrientation

Malgré l’intérêt technique de cette façon de faire je me concentrerai ici sur la méthode full Xamarin.Forms comme je l’ai fait dans mon livre. Le but étant de montrer que les Xamarin.Forms peuvent tout faire sans jamais se frotter au code natif (sauf cas très exceptionnels).

La méthode native

Si je ne m’étendrais pas sur cette approche il convient d’en préciser le fonctionnement ce que résume ce petit croquis :

image


J’ai déjà décrit de nombreuses fois le principe utilisé qui est celui de l’injection de code natif dans un projet cross-plateforme (il y a même une vidéo sur ma chaîne Youtube qui traite de ce sujet en utilisant Xamarin et MvvmCross, c'est une ancienne vidéo, les versions des produits ont évolué depuis mais le principe démontré et ses explications restent rigoureusement les mêmes).

Chaque code natif de chaque plateforme enregistre auprès du conteneur IOC le code de détection qui est accessible dans le code commun via une Interface.

Les plugins Xamarin.Forms fonctionnent tous sur ce principe d’où l’intérêt de le comprendre.

La méthode pure Xamarin.Forms

Les ContentPage disposent d’une méthode virtuelle OnSizeAllocated, en la surchargeant il est possible de réagir aux changements d’orientation. Dans sa version la plus simple le code est le suivant :

image

On voit qu’il manque tout de même un peu de travail pour en faire une véritable solution utilisable au sein d’un projet comme par exemple :

  • Placer ce code dans une classe de base dont on fera hériter les pages pour que toutes bénéficient de la détection
  • Déclencher éventuellement un évènement
  • Classifier les modes portrait et paysage via une Enum ou des constantes
  • etc

Mais je vais y revenir plus loin.

Les stratégies de mise en page

Car avant d’aller plus loin il est essentiel de balayer les différentes stratégies de mise en page utilisées aujourd’hui principalement dans le monde mobile mais pas seulement :

image

Je vous ai donné les noms anglais car ce sont eux que vous retrouverez le plus souvent, autant les connaître…

Fluid Layout

On peut traduire cela par mise en page fluide. Il marque une différence notable avec la mise en page classique sur PC où tout est figé en fonction d’une résolution d’écran. L’arrivée de moniteurs de tailles et de ratio très différents a toutefois obligé progressivement les développeurs à adopter même sans le savoir l’approche “fluid layout”.

Cette dernière consiste dans sa version la plus simple à faire en sorte que les éléments affichés puissent grandir ou rétrécir selon la place disponible.

La plupart des contrôles XAML savent s’étendre automatiquement (selon leurs options comme HorizontalOptions et VerticalOptions) et si on y ajoute un peu de travail de conception notamment pour les conteneurs comme la grille (en utilisant des colonnes ou lignes en mode “auto” ou “star”) on obtient à très peu de frais un affichage qui répond aux exigences du fluid layout.

Pas trop d’effort… donc donc… pas un résultat magnifique non plus !

Qu’elle est rude cette espèce de loi qui veut que si c’est facile à faire ce n’est pas très performant ou très joli non plus. Il faut toujours travailler plus, même sans gagner plus, si ce n’est la reconnaissance des utilisateurs. Mais c’est déjà toucher son salaire que de voir un utilisateur heureux !

Extended Layout

Le second niveau de stratégie est représenté par l’extended layout, ou mise en page étendue. Elle reprend bien évidemment les concepts du fluid layout mais elle y ajoute une nuance d’importance : plutôt que de simplement agrandir ou rétrécir des zones en fonction de la taille disponible on va se servir de tout espace nouvellement accessible pour y afficher de nouveaux contrôles, de nouvelles informations. Et à l’inverse, plutôt que de réduire la taille des éléments affichés on commencera par supprimer les informations non essentielles. Le but étant de présenter des pages contenant toujours l’essentiel de l’information utile à l’utilisateur tout en lui offrant quelques bonus lorsque la taille écran le permet.

Si vous prenez l’exemple de la calculette iOS sur un iPhone, lorsque le téléphone est en mode portrait l’affichage montre une calculatrice classique avec les opérations de base. Mais lorsque le smartphone passe en mode paysage l’espace dédié au clavier étant largement plus grand de nouveaux boutons sont ajoutés pour transformer l’App en calculatrice scientifique…

image

Ici l’utilisateur tire profit des orientations différentes et des form factors différents. La même App utilisée sur un petit smartphone ou une tablette n’offre pas les mêmes services. Plus l’écran est grand et plus l’App fait de choses aussi. L’utilisateur obtient une sorte de prime lorsqu’il passe à de plus grandes diagonales ou qu’il utilise la rotation.

Complimentary Layout

La traduction devient ici délicate, le sens habituel de “complimentary” est “gracieux, gratuit”. Il s’agirait alors d’une mise en page “gratuite”… Rien n’est gratuit ici-bas ! Il faut alors peut être revenir au sens premier qui n’est pas “complémentaire” (complementary à ne pas confondre) mais plutôt “of the nature of, conveying, or expressinf a compliment, often one that is politely flattering, ex : a complimentary remark”.

Il s’agit donc plutôt de quelque chose qui flatte comme une remarque peut être flatteuse, comme un compliment.

On n’est pas en cours d’anglais je sais… mais c’est bien d’aller au fond des choses. Bon, mais le rapport avec le layout ?

Il est fort probable qu’il y ait une confusion avec complementary qui veut dire complémentaire à mon avis. Même les américains finissent par se prendre les pieds dans leur propre tapis Smile

Donc nous adopterons le sens de complémentaire en sachant que ce n’est pas le sens de complimentary, mais on comprendra mieux :

Le complimentary design consiste à déclencher selon l’orientation (et l’espace disponible) l’affichage d’un écran auxiliaire contenant des informations supplémentaires.

Le concept d’écran peut être vu comme équivalent à fenêtre, voire à toute zone d’affichage.

Dans ce mode là les pages affichées en portrait et en paysage peuvent être radicalement différentes et proposer une mise en page repensée. Il ne s’agit pas juste de montrer ou cacher un bouton de plus (extended layout) ou de réduire ou agrandir des zones (fluid design).

Cette forme de design est celle qui est la plus puissante puisqu’on adapte réellement le contenu et la présentation à la forme de l’écran et à l’espace disponible. Tout est pensé et repensé comme s’il s’agissait de repartir de zéro en collant au plus près des possibilités de l’orientation et de l’espace disponible.

Cela signifie en pratique qu’une page conçue selon cette stratégie réclamera en réalité deux pages XAML différentes, voire plus si on prend en compte la taille en plus de l’orientation et mieux que la taille le type d’appareil.

image

On sait notamment que les smartphones et les tablettes ne sont pas utilisées de façon identique. Les smartphones sont utilisés à 94% en mode portrait et seulement 6% du temps en mode paysage. Alors que cette différence se gomme presque totalement pour les tablettes où le mode paysage domine légèrement à 54% faisant presque jeu égal avec le mode portrait à 46%.

Il est donc judicieux de prendre en compte non seulement l’orientation mais aussi le type de matériel utilisé lorsqu’on conçoit une App.

Toutefois ces différences d’utilisation des orientations ne jouera un rôle déterminant que si l’on désire bloquer la rotation automatique, autre problématique, qui réclame de bien choisir selon le type d’appareil (on bloquera en paysage pour un smartphone sans que cela ne gêne l’utilisateur alors qu’on choisira le mode paysage s’il s’agit d’une tablette en ayant conscience que cela peut gêner plus…).

La technique de la page de base

Une fois en tête toutes ses notions de design, revenons au code…

C’est ainsi la technique de la page de page, solution 100% Xamarin.Forms, dont je vais vous parler.

image

(cliquez pour avoir l’image à 100%)

L’idée est donc simple, on crée une classe de base qui contient la définition de l’Enum Portrait/Paysage et qui surcharge OnSizeAllocated. La méthode prend soin de vérifier que la taille a bien changé (la méthode peut être appelée plusieurs fois de suite dans certains environnements) et elle appelle une méthode protégées virtuelle pour signaler le changement.

Cette méthode virtuelle n’a pas de code, charge à chaque page qui héritera de fournir une surcharge si elle a besoin de détecter le changement.

Une page conçue en mode Fluid layout ne se servira pas du tout de la nouvelle méthode, mais une page en mode Extended Layout s’en servira pour montrer/cacher certains objets et une page en Complimentary Layout prendra avantage de la détection pour charger des pages différentes selon l’orientation.

Et MVVM ?

S’agissant d’un problème d'UI pur tout le code chargé de gérer les différentes stratégies de design se trouve dans les pages XAML ou le code-behind de chacune.

Il n’y a pas de conflit avec le pattern MVVM, les ViewModels n’ayant pas à connaître l’orientation de l’écran, ce n’est définitivement pas leur responsabilité (même si le ViewModel peut contenir des propriétés qui ne seront visibles à l'écran que dans certaines résolutions ou orientations !).

En Extended ou Complimentary Design des zones nouvelles apparaissent à l’écran, elles doivent donc pouvoir être bindées à des données du ViewModel. Ce dernier doit prévoir leur mise à disposition et doit aussi être conçu pour fonctionner sans que ces zones soient affichées ou renseignées. C’est là la seule contrainte qu’impose ces designs plus fluctuants aux ViewModels.

Mais en réalité c’est encore côté designer que les choses se passeront, si un bouton de validation est indispensable sur une page par exemple, c’est au designer de se débrouiller pour le placer sur toutes les variantes d’affichage.

Donc normalement design et MVVM font très bon ménage puisqu’ils s’ignorent !

Néanmoins on peut prévoir des cas où cette belle ignorance n’est pas rentable. Par exemple à quoi bon aller chercher des données sur un serveur si l’utilisateur est dans un mode où elles ne sont pas affichées... C’est l’efficacité même de l’App qui peut en dépendre.

Dès lors et sans violer le pattern MVVM on peut supposer avoir besoin de savoir si la device change d’orientation.

Il existe plusieurs façons de mettre cela en place tout dépend du projet. J’évoquerai par exemple la possibilité d’utiliser la messagerie MVVM dans la page de base qui, en plus d’appeler la méthode virtuelle enverra un message à qui veut bien l’écouter. Charge au ViewModel qui le désire de s’abonner pour détecter les rotations.

Code Exemple

Je suis parti d’un code exemple existant que j’ai pas mal modifié car il était ancien (et exposait un projet du défunt Windows Phone et non UWP). Je n’ai conservé finalement que la partie Android ce qui est suffisant pour un exemple pur Xamarin.Forms. La stratégie par injection de code natif aurait nécessité de conserver tous les projets mais je vous laisse expérimenter cette voie vous-mêmes à titre d'exercice.

Le code permet de tester les trois stratégies de design expliquées ici. On choisit le mode de fonctionnement puis on clique sur un élément de la liste (des films avec des citations). Selon le mode l’affichage est différent lorsqu’on fait une rotation. J’ai laissé la sélection de films tel quel, personnellement j’aurais fait d’autres choix (Brazil, Star Wars, Blade Runner, …) mais ce n’est qu’une démo !

En mode Fluid Design on obtient la même chose en plus ou moins agrandi, en mode Extended Design l’affiche du film est ajoutée à la citation selon l’orientation et en mode Complimentary Design en paysage sur une tablette on obtient outre l’affichage et la citation une zone qui offre une WebView affichant toutes les citations du film en se connectant à Internet. Le luxe quoi !

Donc n’hésitez pas à tester avec des émulateurs très différents du smartphone à la tablette 10”, sélectionnez un film dans la liste et faites pivoter l’émulateur…

La solution VS2017/2019 : https://www.e-naxos.com/download/XfOrientation.zip

Conclusion

Bien comprendre comment on peut tirer profit des changements d’orientation de la device est la voie vers un meilleur design et ainsi une meilleure UX.

Techniquement les choses peuvent être très simples comme la solution 100% Xamarin.Forms que je vous ai montrée ici. Après, comme toujours, il est possible de complexifier en fonction du contexte et des besoins de l’App à développer.

L’essentiel étant d’avoir toujours en tête que la rotation fait partie des modes d’utilisation d’une App mobile, une considération que les développeurs PC ont toujours du mal à avoir bien présente à l’esprit le plus souvent. Les habitudes "mobiles" à prendre réclament toujours un temps d’adaptation, et quelques explications comme celles de Dot.Blog !

Stay Tuned !