Tailles et densités sous Android
Note : cet article est encore en cours de rédaction.Â
Si vous êtes intégrateur/développeur Android, vous vous êtes déjà pris la tête avec ça… Et avec la nécessité de rendre ses sites web responsive, c'est au tour des développeurs web de s'arracher les cheveux ! Comment faire en sorte qu'une application s'affiche correctement sur tous les devices ? Quelle est la vraie taille d'un écran ?
En effet, pour deux devices annonçant 800px de large (par exemple un Nexus 7 et un Samsung Note), l'affichage sera différent, car la densité n'est pas la même !
Encore mieux. Vous testez votre site internet sur un écran de Samsung Galaxy S2 qui annonce fièrement ses 480px de large. Sauf que votre image de 480px déborde de l'écran !
Il existe des milliers de devices sous Android, avec des tailles d'écran différentes. De la tablette à la montre connectée… Le challenge est de réaliser une application qui s'affiche correctement sur tous.
Et il y a de quoi se perdre entre pouces, pixels, dpi, dip… Commençons par un petit point sur les différentes unités utilisées.
Quelques définitions
On distingue les unités physiques : ce sont des dimensions concrètes, physiquement mesurables (mm, in, pt, px) des unités virtuelles, non directement mesurables, qui permettent de s'affranchir du type d'appareil.
Résolution : nombre de pixels disponibles sur l'affichage. Ex 480 x 800 pour un Samsung Galaxy S2
Densité : combien de pixels s'affichent dans une zone de taille constante de l'affichage. Unité : dots per inch = dpi
La valeur Screen density est le ratio de la résolution et de la taille d'affichage.
Ex : sur un écran à 240 dpi, il faut 240 pixels pour faire un pouce
On voit également fréquemment une autre unité, le ppi (pixels per inch). J'ai eu beaucoup de mal à saisir la différence. En fait les dpi sont des valeurs générales (théoriques, un genre de catégories, équivalent de mpdi, xdpi…) tandis que les ppi sont une valeur réelle (on parle de densité hardware)
Taille : quantité d'espace physique disponible pour afficher une interface. Unité : diagonale de l'écran en pouces (inch)
Android a groupé toutes les tailles d'écran en 4 grandes catégories : small, normal, large, and extra-large
Density-independent pixel : pixel virtuel indépendant de la densité de l'écran. La plus utilisée dans Android, c'est la taille en pixels qu'aurait l'interface sur un écran à 160 dpi. Unité : dp ou dip
On parle aussi de clientWidth
Scale-independent pixel = sp
Cette taille est basée sur les dp. Utilisé pour les textes.
Densités
Dans le cas d'un développement Android, il faut désormais prévoir 5 versions pour toutes ses icônes (je dis 5 parce que le ldpi, on peut désormais laisser tomber). Voir en fin d'article pour les dimensions…
Classe | Nom | Densité | Facteur | Conversion | Dossier drawable |
---|---|---|---|---|---|
ldpi | low density | 120 dpi | 3/4 | sp = 3/4 * dp | drawable-ldpi |
mdpi | medium density | 160 dpi | 1 | sp = dp | drawable-mdpi |
hdpi | high density | 240 dpi | 1.5 | sp = 1.5 x dp | drawable-hdpi |
xhdpi | extra high density | 320 dpi | 2 | sp = 2 x dp | drawable-xhdpi |
xxhdpi | extra extra high density | 480 dpi | 3 | sp = 3 x dp | drawable-xxhdpi |
xxxhdpi | extra extra extra high density | 640 dpi | 4 | sp = 4 x dp | drawable-xxxhdpi |
Le mdpi est considéré comme la baseline, c'est à dire la densité de base.
Tailles et résolutions d'écrans
Le Samsung Galaxy S2 est donc considéré comme un écran de taille normale (on est contents pour lui).
Taille | ldpi (120) | mdpi (160) | hdpi (240) |  xhdpi (320) |
---|---|---|---|---|
small | QVGA (240×320) | 480×640 | ||
normal | WQVGA400 (240×400) WQVGA432 (240×432) |
HVGA (320×480) | WVGA800 (480×800) WVGA854 (480×854) 600×1024 |
640×960 |
large | WVGA800 (480×800) WVGA854 (480×854) |
WVGA800 (480×800) WVGA854 (480×854) 600×1024 |
||
xlarge | 1024×600 | WXGA (1280×800) 1024×768 1280×768 |
1536×1152 1920×1152 1920×1200 |
2048×1536 2560×1536 2560×1600 |
Quelques exemples d'appareils
Appareil | Android | Taille | clientWidth | Resolution | screen.width | devicePixelRatio |  densité ppi | |
---|---|---|---|---|---|---|---|---|
Samsung Galaxy Ace | 2.2 | 3,5 » | 320×480 | mdpi | 165 | |||
Nexus S | 4.1.1Â API 16 | 4″ | 320Â dips | 480×800 | 480 px | 1.5 | hdpi | 233 |
Samsung Galaxy S2 | 2.3.7 API 10 | 4,3″ | 320Â dips | 480×800 | 480 px | 1.5 | hdpi | 217 |
Moto X | 4.4.2Â API 19 | 360Â dips | 720×1280 | 360 px | 2 | xhdpi | 312 | |
Galaxy Nexus | 4.3 | 360Â dips | 720×1280 | 360 px (?) | 2 | xhdpi | 315 | |
Nexus 4 | 4.2.2 API 17 | 4″ | 384Â dips | 768×1280 | 384 px (?) | 2 | xhdpi | 318 |
Nexus 4 | 4.3 API 18 | 4″ | 360Â dips | 768×1280 | 768 px | 2 | xhdpi | 318 |
Nexus 7 2012 | 4.4.2 API 19 | 7″ | 601Â dips | 800×1280 | 601 px | 1.331 | hdpi | 216 |
Nexus 7 2013 | 4.4.2 API 19 | 7″ | 600 dips | 1200×1920 | 1200 px | 2 | xhdpi | 323 |
Samsung  Galaxy Note | 4.1.1 API 16 | 5,3″ | 400 dips | 800×1280 | 800 px | 2 | xhdpi | 285 |
Samsung  Galaxy S3 | 4.3 API 18 | 720×1280 | 2 | xhdpi | 306 | |||
Nexus 5 | 4.4.2 API 19 | 5″ | 360Â dips | 1080×1920 | 3 | xxhdpi | 455 | |
Samsung Galaxy S4 | 4.4.4Â API 19 | 5″ | 360Â dips | 1080×1920 | 3 | xxhdpi | 441 | |
Nexus 5X | 6.0 API 23 | 5,2″ | 411 dips | 1080×1920 | 3 | xxhdpi | 423 | |
Samsung Galaxy S5 | 4.4.4Â API 19 | 5,1″ | 1080×1920 | 3 | xxhdpi | 432 | ||
Samsung Galaxy Note 4 | 4.4.4 API 19 | 480 dips | 1440×2560 | 3 | xxhdpi | 515 | ||
LG G3 | 5,5″ | 360 dips | 1440×2560 | 4 | xxxhdpi | 538 | ||
Samsung Galaxy S6, S6 Edge, S7 | 5,1″ | 1440×2560 | 4 | xxxhdpi | 577 | |||
Samsung Galaxy S8 |  7.0 | 5,8″ | 1440×2960 | 4 | xxxhdpi | 571 | ||
Xperia Z5 Premium | Â 5.1 | 5,5″ | 2160×3840 | xxxhdpi | 806 |
Plus les pixels sont serrés, plus l'image est belle… On disait que l'oeil nu ne pouvait identifier des densités supérieures à 300 ppi. Et puis sont arrivés les devices xxxhdpi (le Nexus 5, le Nexus 6, LG G3). Là , c'est carrément de la super haute résolution.
Quand on voit ce tableau, on comprend mieux certaines choses :
- une tablette Nexus 7 (modèle 2012), donc quelque chose d'assez large (taille 7 pouces) avec sa résolution de 800 x 1280 aura le même nombre de ppi qu'un petit Samsung Galaxy S2 de 4,3″ et 480 x 800.
- Alors que si on compare cette même Nexus 7 à un Samsung  Galaxy Note : la résolution des deux appareils est la même (800 x 1280), mais les pixels seront beaucoup plus serrés sur les 5,3″ du Galaxy Note, le résultat sera donc plus agréable à l'oeil sur ce dernier.
A noter que le record de clientWidth en smartphone Android est actuellement détenu par le Google Nexus 6P : 412 dips. Tandis qu'on peut considérer que le plus bas est 320dips (mon Samsung Galaxy S2). Donc, pour afficher un site web sur un device Android en orientation portrait, garder en mémoire une fourchette de largeurs allant de 320 à 412px. Au-delà , on est en paysage, ou sur une tablette.
Voici l'illustration avec des émulateurs Genymotion et une application que je suis en train de développer. On va s'intéresser au player sur fond noir semi-transparent en bas de chaque écran.
Â
A gauche un émulateur Nexus S (caractéristiques équivalentes à mon Galaxy S2 : 480×800, hdpi donc densité 1.5 soit une largeur réelle de 320 dips). A droite, l'émulateur de la tablette Nexus 7 (800×1280, hdpi aussi).
Ces deux devices ont une densité théorique de 240 dpi (1,5 fois 160). Donc, comme j'ai défini sur la charte graphique que les boutons play et avance devaient mesurer 30dp, ils sont affichés avec une taille de 45px (1,5 fois 30). Et Android est allés les chercher dans le répertoire drawable-hdpi/
La tablette mesure 800px de large (soit 601 dips) contre 480px (soit 320 dips), donc elle offre presque deux fois plus d'espace pour le mon player s'étale. C'est pour cette raison que c'est tout tassé dans le player affiché dans le Nexus S : le « now playing » est sur 3 lignes, le compte à rebours sur 2 lignes… pas terrible !
Â
2e exemple. A gauche, un émulateur Samsung Galaxy Note. A droite, toujours la tablette Nexus 7. Cette fois-ci, les deux ont une résolution de 800×1280. Mais le Galaxy Note a une densité théorique de 320 dpi (xhdpi).
Mes boutons play et avance définis à 30dp sont donc affichés avec une taille de 60px sur le Galaxy Note. Et là , Android est allé les chercher dans drawable-xhdpi. C'est pourquoi ils apparaissent plus grands (mais pas pixellisés pour autant).
De même, si on ramène la largeur en dips (clientWidth), on a 601 dpi pour le Nexus 7 (à droite) et 400 dpi (à gauche) pour le Galaxy Note. On comprend pourquoi les éléments du player semblent bien plus au large dans la fenêtre du Nexus 7.
Pour l'imageView qui affiche la photo de la sleeve, j'ai défini comme largeur wrap_content. Les photos importées font 100px sur les deux devices.
J'ai également fait un test avec un émulateur Nexus 4 (768px de large, 320 dpi donc 360 dips) et l'émulateur Samsung Galaxy Note (800px de large, 320 dpi donc 400 dips). A cause de ces 80 dips de différence, on n'est plus assez large : sur le Nexus 4 je me retrouve encore avec le compte à rebours sur 2 lignes !
Comment accéder aux valeurs
En java
Selon la version du SDK, le code n'est pas le même…
screen.width
public static int getWindowWidth(Context context){ int width; if(android.os.Build.VERSION.SDK_INT >= 13) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); width = wm.getDefaultDisplay().getWidth(); } else { width = context.getResources().getDisplayMetrics().widthPixels; } return width; }
Densité en dpi
density = context.getResources().getDisplayMetrics().densityDpi;
devicePixelRatio
devicePixelRatio= context.getResources().getDisplayMetrics().density;
On a également accès à une autre valeur, la scaledDensity, qui renvoie la même chose sur tous les devices que j'ai testés. J'ignore quelle est la différence…
La densité de pixels (les dpi ou les ppi)
xdpi =Â context.getResources().getDisplayMetrics().xdpi;
Et pour obtenir la densité verticale, utiliser la propriété ydpi.
Sur un vrai device, on récupère les ppi (217 ppi pour mon Samsung Galaxy S2). Sur un émulateur, on récupère les dpi (240 , 360…)
La valeur de référence
Plutôt que de saisir 160 en dur, on peut récupérer la valeur de la baseline (les 160 dpi) avec cette constante
DisplayMetrics.DENSITY_DEFAULT
En javascript (dans une webview en Cordova, ou le navigateur pour un site)
- screen.width renvoie la résolution horizontale en pixels  (mais en fait, pas toujours… sous les versions d'Android inférieures à 4.4, on obtient le clientWidth !).
- window.devicePixelRatio renvoie le ratio entre les pixels et les dips
- document.documentElement.clientWidth donne la densité en dips (certains disent la taille du viewport), c'est à dire la vraie largeur disponible
Quelques formules
Calcul du devicePixelRatio
Formule : physical pixels / dips
Calcul des dpi
Formule : 160 x devicePixelRatio
Calcul de la « vraie » densité de pixels (les ppi) ou densité hardware
Pour calculer la densité de pixel d'un écran, on part de sa résolution (l pixels de large x L pixels de long) pour obtenir sa diagonale (remember Pythagore ?), et de sa taille t en pouces.
Formule : √(l² + L²) / t
Ex
- pour un écran de tablette Retina de 9,7′ titrant 2048 x 1536 pixels : √(2048² + 1536²) / 9,7 = 264 ppi
- pour mon écran de Samsung Galaxy S2 de 4,3′ titrant 480 x 800 : √(480² + 800²) / 4,3 = 217 ppi
Retrouvez une liste assez complète (mais qui commence à être ancienne) de devices et de leurs ppi
Sur dpiLove, vous avez même un outil de calcul en ligne de ces ppi
Density Independant Pixels (clientWidth)
Ou dip. Ne pas confondre avec les dpi !
Formule : pixels x 160/ dpi (ou pixels / devicePixelRatio)
Ex pour le Samsung Galaxy S2 ou le Nexus SÂ : 480 x 160 / 240 = 320 dip
Et pour obtenir la largeur en pixels : dp * dpi / 160
Scale Independant Pixels
Formule 1 dp = f x sp
Selon la situation, f  est le facteur
- d'agrandissement du texte choisi par l'utilisateur dans Paramètres > Accessibilité > Taille de police (normal par défaut)
- par rapport à la baseline de 160 dpi (un hdpi, soit 240 dpi, c'est 1,5x plus que le mdpi qui est considéré comme la valeur de base)
Une police taille 42 pt dans Photoshop, utilisée sur un écran xxhdpi de 480 dpi reviendra à du 14 sp. Car 1sp = 3pt pour cette densité.
Tailles des icônes
Là encore, jolie prise de tête. Selon qu'on souhaite utiliser une image comme icône de menu, de lancement ou d'actionbar, la taille sera différente.
ldpi | mdpi | hdpi | xhdpi | xxhdpi | xxxhdpi | |
---|---|---|---|---|---|---|
Echelle | 1 x | 1 x | 1.5 x | 2 x | 3 x | 4 x |
DPI | 120 dpi | 160 dpi | 240 dpi | 320 dpi | 480 dpi | 640 dpi |
Icône de lancement | 36 px | 48 px | 72 px | 96 px | 144 px | 192 px |
Icône pour Action bar, boîtes de dialogue, onglets | 24 px | 32 px | 48 px | 64 px | 96 px | 128 px |
Petites icônes (contextuelles) | 16 px | 16 px | 24 px | 32 px | 48 px | 64 px |
Icônes de barre de notification |  18 px | 24 px | 36 px | 48 px | 72 px | 96 px |
Au fait, une icône d'action bar Holo Light, est de couleur #666666 avec une transparence de 60%
Sources
Understanding Density Independence in Android
Android Icon Size Guide Made Simple
Android: Screen Densities, Sizes, Configurations, and Icon Sizes
Une liste de devices avec leur résolution en pixels per inch
Une autre liste de devices avec leurs dimensions physiques, leur devicePixelRation et leur ppi
Calculer la densité de pixels de son écran