Das Thema Font-Styling in Android-Apps begleitet mich als UI/UX-ler schon eine ganze Weile und auch im aktuellen Projekt ist es jetzt erneut wieder Thema geworden. Auslöser war in diesem Fall die UI-Abnahme einer Android-Entwicklung, in der ein UI-Defekt sichtbar wurde, der nicht alle Tage zu sehen ist, und deswegen auch schon einmal in Vergessenheit geraten ist. Doch das UI/UX-Imperium schlägt zurück, und ist schon allein auf Grund seiner visuellen Präsenz besonders gnadenlos.
Wie lautet der Task?
Aufgrund eines Redesigns einer App wurde in den Font-Style-Definitionen von ehemals Arial, respektive Arial-Bold auf einen anderen Font, ihr kennt ihn alle, es ist Roboto, umgestellt. Soweit ist das grundsätzlich unkompliziert und ein Developer geht dabei in bewährter Manier vor. Man kann, und das kennen sicher viele Developer, über Android-Studio problemlos bspw. einen Überschriften-Style auf eine System-Ressource referenzieren lassen. Das ist ein einfacher und durchaus gängiger Weg, der in aller Regel zu Erfolg versprechenden Ergebnissen führt. Da ist man gefühlt immer auf der richtigen Seite, denn schliesslich wird Roboto schon seit einer halben Ewigkeit auf Android eingesetzt und ist somit auf den Devices auch abruf- und verfügbar. Den meisten Android-Developern ist es nach meiner Erfahrung nicht untergekommen, das ein anderer Font als Roboto in den App-Projekten verwendet wird. Andere Lebensläufe und Expertise seien davon unberührt, ist klar.
Es führt folgende Syntax grundsätzlich in die richtige Richtung:
<item name="android:fontFamily">sans-serif-black</item>
Ist der Default-Font für die Ausprägung „sans-serif-black“ bspw. Roboto-Black, wird damit das Ziel erreicht. Voraussetzung ist lediglich, dass die referenzierte Font-Ressource systemseitig vorhanden ist. Bei aktuellen Devices ist das also kein Problem. Selbst wenn man weiter zurück geht und eine alte API zitiert, dürfte bis API16 eine Roboto als Ergebnis ausgeliefert werden. Das passt in diesem Fall auch besonders gut, da die kleinste unterstützte Version ebenfalls API16 lautet.
API16 ist das Problem?
Erst ab API16 wurde der Roboto-Font mit ausgeliefert, sodass eine Referenz darauf erst dann greifen kann. Bei älteren APIs würde demnach Droid als Standard-System-Schriftart angezeigt, was heute wirklich keiner mehr will, oder? In unserem Fall hilft das allerdings nicht weiter, da Roboto grundsätzlich gefunden wird und angesprochen werden kann.
Zitat aus der Developer-Dokumentation für API16 Android 4.1 https://developer.android.com/about/versions/android-4.1.html#Fonts
Font families
Android 4.1 adds several more variants of the Roboto font style for a total of 10 variants, and they’re all usable by apps. Your apps now have access to the full set of both light and condensed variants.
The complete set of Roboto font variants available is:
- Regular
- Italic
- Bold
- Bold-italic
- Light
- Light-italic
- Condensed regular
- Condensed italic
- Condensed bold
- Condensed bold-italic
You can apply any one of these with the new fontFamily
attribute in combination with the textStyle
attribute.
Supported values for fontFamily
are:
"sans-serif"
for regular Roboto"sans-serif-light"
for Roboto Light"sans-serif-condensed"
for Roboto Condensed
You can then apply bold and/or italic with textStyle
values "bold"
and "italic"
. You can apply both like so: android:textStyle="bold|italic"
.
You can also use Typeface.create()
. For example, Typeface.create("sans-serif-light", Typeface.NORMAL)
.“
Damit können wir sicher sein, dass unsere Apps immer richtig dargestellt werden.
Aber halt!
Wir sind dennoch nicht am Ziel angelangt!
Wieso weicht die View dennoch von der Style-Vorgabe ab? Die Antwort liegt wie so oft in einem für ungeübte Augen winzigen Detail. In den Stylevorgaben die über Zeplin zur Verfügung gestellt wurden und die eindeutig sind, wurde bei einem speziellen Überschriften-Style der zweiten Kategorie, vergleichbar mit H2 aus dem Web, ein Styling vorgenommen, welches zwar auf Roboto aber in einer besonderen Ausprägung genannt Roboto-Black referenziert. Vergleiche dazu auch die Fonts-Ressource von Google-Fonts, siehe https://fonts.google.com/specimen/Roboto
OK, das mag vielleicht einfach ein Fehler sein, der auf Nachlässigkeit oder Fehleinschätzung zurückzuführen ist. Bleibt zu prüfen ob eine Zuweisung über sans-serif-black erfolgreich sein würde, denn schließlich ist dies im Android-Studio möglich.
android:fontFamily="sans-serif-black" // roboto black
Aber das war nicht das Problem, die Zuweisung war korrekt durchgeführt worden. Ein Versuch zeigt eine einwandfreie Darstellung auf den Test-Devices mit höheren APIs. Nur ein Device weicht ab, es ist ein API16-Device.
Was war los? Eine Lösung greift nicht?
Die Antwort liegt im API-Level und seinem Auslieferungszustand begründet. API16 verfügt einfach nicht über den Schriftschnitt Roboto-Black.
Aus dieser Tatsache resultieren mehrere Fragen:
- Ab welcher API-Version kann man zielsicher auf Roboto-Black referenzieren
- Wie kann ich die API-Abhängigkeit lösen?
Zu 1.: Die erste Frage ist schnell beantwortet: Roboto-Black wird ab Android 5/API 21 also Lollipop ausgeliefert. Das ist für uns ein Problem, da wir ab API16 unterstützen wollen.
android:fontFamily="sans-serif-black" // roboto black (android 5.0)
android:fontFamily="sans-serif-medium" // roboto medium (android 5.0)
Zitat aus https://stackoverflow.com/questions/19691530/valid-values-for-androidfontfamily-and-what-they-map-to
„Compatibility
Based on the log of fonts.xml and the former system_fonts.xml, you can see when each font was added:
- Ice Cream Sandwich:
- Roboto regular, bold, italic, and bold italic
- Jelly Bean:
- Roboto light, light italic,
- Roboto condensed, condensed bold, condensed italic, and condensed bold italic
- Jelly Bean MR1:
- Roboto thin and thin italic
- Lollipop:
- Roboto medium, medium italic, black, and black italic
- Noto Serif regular, bold, italic, bold italic
- Cutive Mono
- Coming Soon
- Dancing Script
- Carrois Gothic SC
- Noto Sans
- Oreo MR1: Roboto condensed medium“
Zu 2.: Die zweite Frage erfordert die Einbeziehung aller Möglichkeiten der Entwicklungsumgebung. Zum einen kann eine Fallback-Lösung die Diskrepanz auf ein Minimum beschränken, indem die Fallback-Lösung für API16-20 an das Zieldesign angenähert wird. Dazu muss man allerdings weitere XML-Deklarationen parallel pflegen. Das riecht nach viel Aufwand und man schießt dann doch knapp am Ziel vorbei. Aus Roboto-Black würde also Roboto-Bold werden, damit hätten wir nur wenig gewonnen.
Am Besten ist…
Eine weitere Lösung ist die Einbindung der Font-Ressource direkt in das Projekt, bspw. in einen Fonts-Ordner. Um API16-Apps damit zu unterstützen benötigt man die Support-Library 26. Details dazu findest du hier https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml Danach ist eine simple Referenz über @fonts/roboto-black möglich und erfolgreich.
<item name="android:fontFamily">@fonts/roboto-black</item>
Das Ergebnis jedoch kann sich wirklich sehen lassen und das Ziel wird 100%ig erreicht.
Fazit
Die Einhaltung von Stylevorgaben ist nach diesem Beispiel uneingeschränkt möglich, auch wenn Fonts nicht auf den Zielsystem vorliegen. Eine doppelte Führung von Styles.xml für die abweichende APIs 16/17 und ist nicht erforderlich.
Links
- https://stackoverflow.com/questions/21023304/how-do-i-specify-eg-roboto-medium-or-roboto-black-in-styles-xml
- https://proandroiddev.com/font-typeface-in-android-application-572766edddd6
- https://medium.com/google-design/the-android-developers-guide-to-better-typography-97e11bb0e261