Избегаем поддельных шрифтов в Android

Недавно я столкнулся с проблемой поддельного жирного и курсивного текста при использовании семейства шрифтов в Android разработке.


В этой статье хочу рассказать об этой проблеме и о её решении.


Создание семейства шрифтов


Начиная с API 26, появилась возможность объединять шрифты в семейства.
Семейство шрифтов — это набор файлов шрифтов с указанием их стиля и веса.


Вы можете создать новое семейство шрифтов как ресурс XML и обращаться к нему как к единому элементу, вместо того, чтобы ссылаться на каждый стиль и вес как на отдельные ресурсы.


Таким образом система сможет выбрать правильный шрифт в зависимости от стиля текста, который вы пытаетесь использовать.


Пример файла:


<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
        android:fontStyle="normal"
        android:fontWeight="400"
        android:font="@font/lobster_regular" />
    <font
        android:fontStyle="italic"
        android:fontWeight="400"
        android:font="@font/lobster_italic" />
</font-family>

Вариант для Support Library
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
    <font
        app:fontStyle="normal"
        app:fontWeight="400"
        app:font="@font/lobster_regular" />
    <font
        app:fontStyle="italic"
        app:fontWeight="400"
        app:font="@font/lobster_italic" />
</font-family>

Атрибут fontStyle определяет стиль начертания шрифта — обычное(normal) или курсивное(italic).
В свою очередь, fontWeight — устанавливает вес, aka насыщенность шрифта.
И конечно, font будет задавать шрифт который будет использоваться при заданном fontWeight и fontStyle.


Вес шрифта


Этот стандарт пришел с web-разработки. Значение устанавливается от 100 до 900 с шагом 100.


Следующая таблица соответствует распространенным именам насыщенности:


Значение Общее название
100 Тонкий (Волосяной)
200 Дополнительный светлый
300 Светлый
400 Нормальный
500 Средний
600 Полужирный
700 Жирный
800 Дополнительный жирный
900 Черный (Густой)

В основном, в файле семейства шрифтов, достаточно указать только шрифты для нормального начертания — 400, и стандартного жирного — 700.


Более подробно о насыщенности шрифта читайте здесь.


Поддельный курсив


Когда система не может найти подходящий шрифт для жирного или курсивного текста, Android компенсирует это, растягивая символы для поддельного жирного и наклоняя их для поддельного курсива.


Такая проблема может возникнуть, когда в семействе шрифтов не указан шрифт для нужного стиля.


Я сделал небольшое приложение которое показывает отличия искусственной стилизации от настоящей на примере Lobster Two шрифта:




Вы можете заметить, как при использовании искусственного начертания шрифта, текст получается более сплюснутым. Хуже того, затрудняется разборчивость из-за размазывания букв и неправильного наклона.


Решение


В данном примере, я создал lobster_two.xml в котором указал шрифты для обычного, курсива, жирного, и жирного курсива:


<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
    <font
        app:fontStyle="normal"
        app:fontWeight="400"
        app:font="@font/lobster_two_normal" />
    <font
        app:fontStyle="italic"
        app:fontWeight="400"
        app:font="@font/lobster_two_italic" />
    <font
        app:fontStyle="normal"
        app:fontWeight="700"
        app:font="@font/lobster_two_bold" />
    <font
        app:fontStyle="italic"
        app:fontWeight="700"
        app:font="@font/lobster_two_bold_italic" />
</font-family>

А также, lobster_two_incomplete.xml в котором указал только шрифт для обычного текста:


<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">
    <font
        app:fontStyle="normal"
        app:fontWeight="400"
        app:font="@font/lobster_two_normal" />
</font-family>

И переключался между ними по нажатию на переключатель.


Когда использовался lobster_two_incomplete.xml, вместо lobster_two.xml, то происходило искусственное растягивание и наклонение шрифта.


Чтобы избежать этого, нужно прописывать в файле семейства шрифтов всевозможные стили, а также всегда использовать этот файл в качестве шрифта.


Использование в коде

// Правильно
val typeFace = resources.getFont(R.font.lobster_two)
textView.setTypeface(typeFace, Typeface.BOLD)

// Не правильно
textView.typeface = resources.getFont(R.font.lobster_two_bold)

// Не правильно
val typeFace = resources.getFont(R.font.lobster_two_incomplete)
textView.setTypeface(typeFace, Typeface.BOLD)

// Не правильно
val typeFace = resources.getFont(R.font.lobster_two_normal)
textView.setTypeface(typeFace, Typeface.BOLD)

Используем в xml

// Правильно
<TextView
          ...
          android:fontFamily="@font/lobster_two"
          android:textStyle="bold|italic"/>

// Не правильно
<TextView
          ...
          android:fontFamily="@font/lobster_two_bold_italic"/>

// Не правильно
<TextView
          ...
          android:fontFamily="@font/lobster_two_incomplete"
          android:textStyle="bold|italic"/>

// Правильно
<TextView
          ...
          android:fontFamily="@font/lobster_two"
          android:textStyle="bold"/>

// Не правильно
<TextView
          ...
          android:fontFamily="@font/lobster_two_bold"/>

// Не правильно
<TextView
          ...
          android:fontFamily="@font/lobster_two_normal"
          android:textStyle="bold"/>

Комментарии 3

    0
    Но, если нужно использовать больше вариантов весов, чем regular/bold, то android:textStyle уже не спасет, и придется создавать отдельное семейство и использовать его (то, что указано как «неправильно»), так ведь?
      0
      Всегда лучше использовать только один файл семейства шрифтов на комплект шрифтов. Если нужно использовать больше вариантов весов, то их стоит прописать в том же файле семейства шрифтов.

      Пример
      <font
              app:fontStyle="normal"
              app:fontWeight="100"
              app:font="@font/lobster_two_thin" />
      


      Для работы с большим вариантов весов нужен API >= 28. В таком случае придётся указывать вес напрямую. Пример:

      <TextView
                ...
                android:fontFamily="@font/lobster_two"
                android:textFontWeight="100"/>
      
      0

      На самом деле способ при помощи которого другим в ногу стреляешь. Если человек не знает что нужно ставить именно через стандартные стили, будет долго гадать что пошло не так.
      Предпочитаю явно ставить всегда font

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое