Сегодня мобильными приложениями активно пользуются не только молодёжь, но и представители старших поколений, количество которых непрерывно растёт (Pew Research Center). Чаще всего именно эти пользователи меняют системный шрифт и настраивают контрастность для комфортного использования. Это влияет не только на пользовательский опыт работы, но ещё и формирует новые потребности в развитии операционных систем Android и iOS.

По данным ресурса Appt, многие люди предпочитают изменять стандартные настройки отображения интерфейса, однако далеко не все разработчики учитывают эту особенность и адаптируют свои интерфейсы под нужды пользователей. Из-за этого возможны ситуации, когда информация, необходимая для совершения критически важных действий (вроде заказа такси или перевода денег) либо вовсе не помещается на экране, либо оказывается расположена неудобно и не соответствует ожиданиям пользователя.

Меня зовут Игорь, я Android-разработчик в Сбере, развиваю проект СберИнвестии в команде «Портфель клиента». Расскажу про наш опыт работы с особыми возможностями Android, как мы к этому пришли, а также на практических примерах покажу улучшения UX нашего приложения. Конечно же, я также расскажу про подводные камни, на которых мы удачно поскользнулись, куда же без этого :).

У Сбера есть проект по повышению доступности цифрового окружения. Изначально руководство создавали для группы компаний Сбербанка, но мы решили поделиться им с профессиональным сообществом, поскольку хотим, чтобы и вы смогли внести свой вклад в доступность цифровой среды в России. 

Важное замечание: в статье использован стек с Android View. На момент разработки решения мы только начинали внедрять Compose в проект. Тем не менее, я уверен, что даже для пользователей Compose всё равно найдётся интересная информация, как минимум с точки зрения UX-решений! А также дайте знать в комментариях, если стоит написать ещё одну статью специально под Compose. 

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

Системное увеличение

В Android на размер шрифта могут влиять две системные настройки: размер шрифта и масштабирование экрана. 

Системный размер шрифта работает довольно просто: на основе местоположения ползунка в настройках телефона формируется определённый коэффициент увеличения, который участвует в конвертации SP-величин в PX (рисунок 1).

Рисунок 1. Системная настройка: увеличение шрифта.
Рисунок 1. Системная настройка: увеличение шрифта.

С масштабированием экрана механизм более сложный: при передвижении ползунка в настройках экрана телефона в правую сторону эмулируется повышение плотности пикселей — захватывается бОльшее количество физических пикселей (рисунок 2). Например, если в стандартном режиме масштабирования для отрисовки квадрата co стороной 48 dp использовалось 132 физических пикселя экрана, то при изменении настройки отображение тех же 48 dp может потребовать уже 144 физических пикселя, и изображение станет больше (Support different pixel densities).

Рисунок 2. Системная настройка: увеличение интерфейса.
Рисунок 2. Системная настройка: увеличение интерфейса.

Комбинирование этих д��ух способов увеличения усиливает эффект и повышает вероятность «поломки» интерфейса приложения: достаточно иметь на экране хотя бы один неудачно спроектированный виджет, чтобы впечатление от интерфейса ухудшилось. К сожалению, в нашем случае одним виджетом не обошлось: вот так стал выглядеть наш экран после применения обоих видов увеличения:

Рисунок 3. Экран «Портфель» до и после включения системного увеличения.
Рисунок 3. Экран «Портфель» до и после включения системного увеличения.

Давайте перечислим проблемы, которые появлялись на нашем экране при увеличенном интерфейсе:

  • Наименование отображаемого счёта практически упирается в правую область. При более длинном наименовании текст обрежется и потеряется важная информация;

  • Большая разрядность стоимости счёта тоже может привести к снижению читаемости.

  • В unPNL (unrealized Profit and loss) пропала информация о периоде, за который рассчитывается доходность.

  • Текст в кнопках действий («пополнить и вывести» и «история и заявки») тоже обрезан и не даёт представления о функциональности, для которой они предназначены.

  • В ячейках активов и групп активов потерялась половина информации, такая как наименование группы, её доля, а также название бумаги, её стоимость и количество.

Это не та картинка, которую хотелось бы видеть любому пользователю. К сожалению, это частая проблема для многих приложений, и СберИвестиции тут не уникальны. У моего отца настроен увеличенный шрифт, и мне доводилось видеть, как он использует многие приложения (некоторые не позволяют даже долистать до не помещающихся на экране элементов!).

Мы провели исследование своей аудитории и выяснили, что порядка 44,82 % наших пользователей пользуются системным увеличением. Это огромный пласт аудитории, которому мы неудобны! Этот дефект срочно нужно было исправлять.

Давайте рассмотрим, с помощью каких инструментов UX это можно было бы исправить.

Стратегии обработки переполнения

В UX существует несколько стратегий обработки большого текста:

  • Обрезание (ellipsize). Текст обрезается в начале, середине или конце, а скрытая часть заменяется многоточием. Это довольно популярное и простое решение, и при разработке интерфейса под Android его легко применить, например, через тег ellipsize в XML или одноимённый метод setEllipsize в коде.

  • Сокращение. Идея в том, чтобы, не теряя уровень восприятия информации пользователем уменьшить её визуальное представление: например, строка с числом «999 999 999» может отображаться как «99 млн». Этот подход можно включать и выключать в зависимости от наличия свободного пространства, однако для нас, в силу специфики приложения, такое поведение не всегда применимо: нам важно чтобы пользователь видел максимум информации, не теряя в точности. Тем не менее мы применяем этот подход в некоторых случаях, например, количество активов отображаем как «N шт».

  • Бегущая строка. Анимируется сдвиг текста внутри элемента. Я думаю, все видели подобное решение в метро. В современном UX это считается антипатерном, применяется нечасто, когда остальные подходы бессильны.

  • Перенос строк. Текст разбивается и отображается на нескольких строках. Можно указать способ разбивки текста через атрибут android:breakStrategy. При его использовании нужно убедиться, что контейнер подстроится под новую высоту контента или позволяет листать.

  • Авторазмер текста. Текст, в зависимости от предоставляемого пространства, меняет свой размер. В Android-разработке это можно сделать с помощью android:autoSizeTextType, а также указать гранулярность (шаг смены кегля) или набор разрешённых шрифтов.

  • Ограничение текста. Запрет на увеличение текста: либо отключение системного увеличения, либо ограничение максимального размера шрифта. И то и другое для отдельного элемента реализуется через отказ от SP (scaled pixels) в пользу DP (dencity independent pixels). Но тут стоит быть очень аккуратным, так как злоупотребление таким ограничением приведёт к ухудшению пользовательского опыта.

  • Прокрутка/динамическая высота контейнера. Если предыдущие подходы имели непосредственное отношение к тексту, то этот и следующий можно применять к любым контейнерам. Текущий метод борьбы с переполнением позволяет размещать элементы так, чтобы у пользователя оставалась возможность с взаимодействовать с внутренними элементами.

  • Динамическая вёрстка. Речь идёт о том, чтобы менять расположение элементов на экране в зависимости от объёма отображаем��й информации, её размера или экрана пользователя. Такой подход может быть довольно сложным в реализации, но при этом он самый гибкий.

Все описанные выше подходы (кроме разве что кроме бегущей строки) в той или иной степени нашли своё применение на нашем экране. Давайте пройдёмся по исправлениям, которые мы внесли в компоненты.

Наименование счёта

Строка состоит из двух частей: наименования счёта (в данном случае — «На счастливую жизнь») и его номера — 4200AV6. Однако сокращение должно происходить только у наименования счёта, когда номер упирается в колокольчик.

Анимация 1. Работа переполнения в поле наименования счёта
Анимация 1. Работа переполнения в поле наименования счёта

Выглядит неплохо, но как можно достичь такого поведения? Самый очевидный вариант, который мы и использовали: два TextView внутри одного LinearLayout с указанием веса. Выглядит это следующим образом:

<LinearLayout
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:importantForAccessibility="noHideDescendants">

<!-- Если ставить android:layout_width="0dp", то при обновлении значения может отображаться некорректно-->
  <TextView
    android:id="@+id/account_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="@dimen/ds_margin_xxxsmall"
    android:ellipsize="end"
    android:maxLines="1"
    android:layout_weight="1"
    tools:text="На булочки с повидлом" />

  <TextView
    android:id="@+id/account_number"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:ellipsize="none"
    android:maxLines="1"
    tools:text="68866" />
</LinearLayout>

Атрибут android:ellipsize="end" говорит о том что сокращение текста доступно и будет применяться с конца. Атрибут android:layout_weight="1" задаёт вес для элемента, который учитывается при размещении внутри ViewGroup.

Студия подсвечивает wrap_content в account_name и предлагает оптимизацию:

«Use a layout_width of 0dp instead of wrap_content for better performance».

When only a single widget in a LinearLayout defines a weight, it is more efficient to assign a width/ height of 0dp to it since it will absorb all the remaining space anyway. With a declared width/ height of 0dp it does not have to measure its own size first.

Однако у этой оптимизации в нашем случае есть существенный недостаток: если у пользователя есть два счёта с разными по длине названиями, то при листании счетов отсутствие измерения размера view приводит к некорректному отображению: между наименованием счёта и его номером появляется пространство. Комментарий — это предупреждение для тех, кто захочет воспользоваться советом Студии. Неоспоримое достоинство этого решения в его простоте и скорости реализации, а недостаток — появляется вложенность элементов.

Стоимость счёта

Стоимость счёта на нашем экране — довольно сложный элемент, который использует сразу несколько подходов к обработке ситуации по переполнению:

  • кастомизированное сокращение (ellipsize);

  • авторазмер текста (autosize).

Кастомизированное сокращение

Рисунок 4. Шапка экрана. Переполнение в стоимости счёта.
Рисунок 4. Шапка экрана. Переполнение в стоимости счёта.

Идея в том, чтобы сократить число, оставив при этом знак единицы измерения (валюта или процент). На первый взгляд тут было бы достаточно применить такое же решение, которое мы использовали с наименованием страницы, однако мы пошли другим путём и логику по сокращению заложили внутрь элемента, не создавая хитрую обёртку.

Почему так? Главная причина в том, что наша текстовка с динамическим размером текста, то есть возможны ситуации, когда размер шрифта стоимости и валюты значительно отличается, хотя компонент должен быть единым целым. Это выглядело бы нелепо. Вторая причина — такой механизм используется почти во всех текстовках цен, стоимостей, долей и тому подобного внутри портфеля, и применение каждый раз вложенности может снизить производительность.

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

Авторазмер текста

Это осуществляется довольно просто: обычно указывают минимальный и максимальный размер текста с указанием градации (величины изменения), или же, как в нашем случае, указывают набор размеров, которые может принимать текст — setAutoSizeTextTypeUniformWithPresetSizes

На первый взгляд всё классно: увеличение разрядности приводит к уменьшению шрифта, следовательно, уменьшается высота TextView, которая тянет за собой уменьшение высоты всей шапки. Однако в другую сторону этот процесс не работает: текст не может вырасти, так как ограничен высотой TextView, а она не увеличивается обратно, потому что в параметрах её высота задана как wrap_content. Это приводит к следующему поведению:

Анимация 2. Ошибка авторазмера в стоимости счёта: текст не увеличивается
Анимация 2. Ошибка авторазмера в стоимости счёта: текст не увеличивается

Строка «123 456,00 ₽» сначала помещается на экран крупным шрифтом, но после череды изменений — уже нет.

В официальной документации Google сказано, что текст с динамическим размером не рекомендуется использовать вместе с атрибутом layout_height = “wrap_content”, лучше отдавать предпочтение “match_parent”.

Нам бы подошло это решение, если бы не одно НО: в нашей вёрстке не к чему привязаться, шапка должна быть динамичной по высоте. Любое ограничение высоты, которые мы бы задали с помощью руководств, барьеров и прочего, тоже могло бы выстрелить нам в ногу (вёрстка должна адаптироваться под размер текста, указанный в настройках системы).

Эту проблему мы решили с помощью указания минимальной высоты для поля стоимости.

Рисунок 5. Шапка экрана Границы компонента «стоимость счёта».
Рисунок 5. Шапка экрана Границы компонента «стоимость счёта».

Значение рассчитывается с помощью умножения начального размера TextView на коэффициент увеличения шрифта из системных настроек. Благодаря этому уменьшение размера шрифта не приводит к уменьшению высоты шапки.

unPNL (unrealized Profit and loss)

Отображения PNL при разных уровнях переполнения
Рисунок 6. Шапка экрана. Состояния отображения unPNL при переполнении.

Для unPNL у нас разработана особая логика: элемент может занимать до трёх строк, текст переносится по частям: если весь контент не умещается в одной строке, то сначала переносится период, а затем — относительное изменение. То есть по одной строке на каждое TextView, и исходя из этого мы попробовали использовать тут FlexboxLayout. На первый взгляд это решение действительно будет работать — группируем неразрывные части вместе: абсолютное изменение и валюта, относительное изменение и знак процента и период, и складываем их внутрь Flexbox.

И всё бы хорошо, но мы недооценили некоторые состояния, отображение которых может быть непростым, например отображение баннера и текста (рисунок 7). Переносы в таких случаях должны отрабатывать стандартно, а с использованием FlexboxLayout данные могут отображаться на разных строках. Для достижения нужного эффекта нам пришлось обрабатывать это состояние отдельно, исключив применение FlexboxLayout.

Рисунок 7. Шапка экрана. Отображение ошибки в unPNL.
Рисунок 7. Шапка экрана. Отображение ошибки в unPNL.

Достоинство подхода — быстрота реализации, недостаток — отсутствие контроля над состоянием отображения и сложность поддержки. Сейчас я бы решал задачу через custom ViewGroup, с явным указанием состояния отображения (именно так мы позже сделали для ячеек активов).  

Динамическая высота шапки

Высота шапки не должна быть фиксированной. Элемент должен адаптироваться к содержимому, чтобы не обрезать вложенные View. Так, например, если мы переходим с одного счёта на тот, контент которого крупнее, то шапка должна по мере перелистывания расширяться по высоте.

Анимация 3. Анимация высоты шапки при смене счёта
Анимация 3. Анимация высоты шапки при смене счёта

Сам компонент представлен элементом ViewPager2, и такое поведение легко достигается с помощью добавления к нему ViewPager2.PageTransformer. Код реализации трансформации довольно простой: он основан на посчитанной высоте соседних страниц и смещению карусели при листании, поэтому останавливаться на нём не будем.

Однако высота шапки может увеличиваться не только при перелистывании, например, когда пользователь меняет период отображения и PNL перестраивается с однострочного отображения на двухстрочный. В таком случае, нам тоже нужно анимировать изменение высоты шапки:

Анимация 4. Анимация высоты шапки при смене периода
Анимация 4. Анимация высоты шапки при смене периода

Для этого мы создали ViewGroup, в котором расположен лишь один элемент — ViewPager2 с динамической высотой. При вызове метода onMeasure класса‑контейнера вычисляется высота текущего элемента [страницы ViewPager2] и, при необходимости, запускается анимация увеличения шапки.

Однако здесь стоит быть осторожными и убедиться, что анимация срабатывает исключительно при необходимости и не инициируется повторно при каждом измерении контейнера, снижая производительность! 

Custom ViewGroup

Теперь переходим к самому трудозатратному, но интересному подходу. Ячейки активов могут содержать много разнообразной информации и существенно различаться по степени заполнения, что может затруднять восприятие при увеличенном р��змере шрифта. Чтобы решить эту проблему, мы полностью переработали ячейки, внедрив в них перестроение. Несмотря на общую сложность это решение было оправданным — ячейка очень сложная, и в ней нет второстепенной информации, которую можно было бы спрятать.

Перестроение подразумевает гибкое расположение отдельных элементов внутри ячейки, таких как количество заблокированных, цена, относительная стоимость и отображение наименования в две строки. Стратегия переполнения направлена на сокращение длины чисел путём удаления младших разрядов.

Информация в ячейках визуально делится на две части: левую и правую. Несмотря на то, что правая часть имеет приоритет и может уменьшать пространство, отведённое под основную информацию слева, она ограничена: ей разрешено занять максимум 60 % общей ширины ячейки.

Рисунок 8. Ячейка актива.
Рисунок 8. Ячейка актива.

Если правой части недостаточно пространства, то информация в ячейках будет переноситься. У ячейки всего три возможных состояния: STANDARD, OVERFLOW_1 и OVERFLOW_2.

Рисунок 9. Ячейка актива. Состояния отображения при переполнении.
Рисунок 9. Ячейка актива. Состояния отображения при переполнении.

По-умолчанию, ячейка находится в состоянии STANDARD. Переход в OVERFLOW_1 происходит в случае, если PNL и поля description помещаются только с разбивкой в две строки (левые и правые части синхронны при переносе строк). OVERFLOW_2 наступает, когда левая и правая части в две строки не помещаются. Дополнительно, если информация всё равно не будет помешаться, сработает логика ellipsize, точно такая же, как в стоимости портфеля.

С технической точки зрения реализация проста и эффективна. В onMeasure для каждого элемента ячейки вычисляется ширина, на основе которой выбирается состояние. Затем, при компоновке (в onLayout), элементы размещаются в соответствии с выбранным состоянием. Это позволяет переложить позиционирование элементов с механизмов Layout (барьеры, constaraintStartToEndOf и другие) на логику отрисовки состояний. Такое решение снижает сложность и повышает производительность за счёт уменьшения вложенности компонентов и упрощения поддержки состояний.

И наконец, если даже при максимальном уровне переполнения (OVERFLOW_2) информации остаётся много, вступает в силу финальная мера — удаление младших разрядов, обеспечивающее дальнейшее сжатие данных:

Рисунок 10. Ячейка актива. Переполнение в полях.
Рисунок 10. Ячейка актива. Переполнение в полях.

Адаптивная вёрстка

Что произойдёт, если даже режим OVERFLOW_2 окажется недостаточным для комфортного отображения информации, особенно при увеличении размера шрифта? Ведь крупный шрифт способен сильно повлиять на восприятие содержимого ячейки. Поэтому решили ввести дополнительное состояние, рассчитанное на очень большие размеры шрифта. Идея проста: если отображаемый размер текста больше заданной границы, то мы рисуем перестроенные ячейки. Но для этого нам нужно было научиться определять такие шрифты.

Рисунок 11. Ячейка актива. Перестроение при большом шрифте
Рисунок 11. Ячейка актива. Перестроение при большом шрифте

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

Расчёты шрифта

Напомню, что на итоговый размер шрифта влияет две системные настройки: размер шрифта и масштабирование интерфейса. А ещё есть нелинейность увеличения текста, поэтому вывести формулу, позволяющую учесть нужную степень влияния каждого фактора, довольно непросто, поэтому мы решили пойти иным путём: в качестве отправной точки мы берём определённый размер текста (у нас это 15 SP) и на его основе выполняем ряд действий:

  1. Считаем размер текста в физических пикселях экрана.

  2. Получаем логический размера текста в пикселях, независимых от масштабирования (SP). Для этого делим количество пикселей, полученных в переменной titleSizePx, на плотность экрана.

  3. Для расчёта логического размера текста в пикселях, учитывающего коэффициент увеличения UI-элементов: достаточно просто перемножить этот размер на сам коэффициент. Само значение увеличения можно получить с помощью отношения плотности экрана в DPI (независимые пиксели на дюйм) к фактическому количеству пикселей на дюйм.

  4. Далее выбираем из двух значений логических размеров текста. Поскольку иногда uiScale может быть меньше 1, а мы хотим перестраховаться, выбираем наибольший из этих параметров, округляя его по математическим правилам.

fun calculateFontSize(resources: Resources): Int {
  val titleSizePx = resources.getDimensionPixelSize(initialTextSize)
  val logicDensity = resources.displayMetrics.density
  val scaleFactor = getUiScaleFactor(resources)
  val logicTitleSizeSp = titleSizePx / logicDensity
  val logicScaleTitleSizeSp = logicTitleSizeSp * scaleFactor
  return max(logicTitleSizeSp, logicScaleTitleSizeSp).roundToInt()
}

fun getUiScaleFactor(resources: Resources): Float {
  val logicDpi = resources.displayMetrics.densityDpi
  val realDpi = resources.displayMetrics.xdpi
  return logicDpi / realDpi
}

Проверка расчётов

Давайте попробуем проверить правильность расчёта шрифта, запустив этот код на разных устройствах c разными параметрами:

Рисунок 12. Расчёты текста. Pixel 5.
Рисунок 12. Расчёты текста. Pixel 5.
Рисунок 13. Расчёты текста. Планшет Freeform.
Рисунок 13. Расчёты текста. Планшет Freeform.
Рисунок 14. Расчёты текста. Телевизоры.
Рисунок 14. Расчёты текста. Телевизоры.

На основе этих расчётов получаем значение размера текста, почти не зависящее от технических характеристик устройств, но при этом учитывающее все факторы, влияющие на его увеличение 

Результаты этих расчётов мы начали использовать не только для логики выбора типа ячейки актива: стандартная или увеличенная, но также и для кнопок (для них мы ещё ввели ограничение максимального размера текста).

 Рисунок 15. Кнопки действий. Изменение отображения для разных размеров шрифтов.
Рисунок 15. Кнопки действий. Изменение отображения для разных размеров шрифтов.

Теперь всё было готово: решение разработано и протестировано на различных устройствах и конфигурациях. Мы отправили фичу в эксплуатацию, уверенные, что смогли упростить жизнь большому количеству клиентов. Каково же было наше удивление, когда мы начали получать отзывы: люди активно просили вернуться к прежнему дизайну.

Что было не так?

Причины сложившейся ситуации оказались следующими.

Большой шрифт и переполнение в некоторой степени стали для нас связанными понятиями. Тестирование полного редизайна ячеек проводилось исключительно на смартфонах. Проблема заключалась в том, что эффект новой логики проявлялся преимущественно на небольших экранах телефонов, и никто не подумал заранее оценить, как изменится внешний вид на планшетах (рисунок 19) или других больших дисплеях (рисунок 17). Планшеты мы намеренно не поддерживали, считая, что это нецелесообразно.

Рисунок 16. Отображение экрана с увеличенным размером шрифта на планшете.
Рисунок 16. Отображение экрана с увеличенным размером шрифта на планшете.
Рисунок 17. Отображение экрана с увеличенным размером шрифта на Samsung Galaxy Fold.
Рисунок 17. Отображение экрана с увеличенным размером шрифта на Samsung Galaxy Fold.

Эмуляторы Android. Вскоре выяснилось, что некоторая доля наших пользователей предпочитает пользоваться приложением через эмулятор. Такие программы имитируют реальные характеристики устройств, однако далеко не всегда идеально воспроизводят их физику (рисунок 18).

Рисунок 18. Расчёты текста. Blue Stack — бесплатный эмулятор для запуска Android‑приложений на компьютере под управлением ОС Windows или macOS.
Рисунок 18. Рас��ёты текста. Blue Stack — бесплатный эмулятор для запуска Android‑приложений на компьютере под управлением ОС Windows или macOS.
Рисунок 19. Blue Stack 4. Влияние настройки эмулятора на расчёты шрифтов.
Рисунок 19. Blue Stack 4. Влияние настройки эмулятора на расчёты шрифтов.

Например, в Blue Stack 4 есть настройка «Режим графического движка», которая изменяет «физические» данные — они оказываются далеки от тех, которые мы привыкли видеть (рисунок 19). Поэтому часть расчётных значений, полученных нами в ходе тестирования, оказалась некорректной именно на эмуляторах.

Решение

Когда мы получили такие результаты, то начали срочно искать решения. Конечно, в первую очередь мы выключили доработку с feature toggles которая включала перестроение ячеек при большом шрифте (Trunk Based Development, позволяет удалённо переключать логику приложения и отключать проблемную функциональность).

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

Другое решение — использовать Window size classes для получения фактического класса экрана приложения и запрещать «перестройку интерфейса» для случаев, когда окно определяется как «широкое». То есть для людей, использующих широкие окна, ячейки будут отрисовываться в стандартном режиме, несмотря на «увеличенный» интерфейс. Google рекомендует именно такой подход, но едва ли он поможет нам с отображением приложения на некоторых эмуляторах.

Можно предоставить пользователям самим настраивать отображение ячеек, вне зависимости от типа устройства или экрана. На внешний вид увеличенной ячейки влияет не только размер текста, но ещё и содержимое, которое у всех разное. Ещё есть личные предпочтения: некоторым удобнее работать с увеличенными ячейками даже при меньших размерах шрифта. Поиск универсального решения может оказаться очень дорогим, и шансы найти его близки к нулю — пусть каждый решает сам за себя.

Ещё один вариант, который мы рассматривали: вообще выключить системное увеличение текста в приложении. Выглядит радикально, но некоторые приложения такое практикуют. Например, Telegram не реагирует на системную настройку — размер шрифта настраивается в самом приложении. Кажется, что это всегда означает ухудшение пользовательского опыта, но лишь на первый взгляд — у этого варианта есть свои преимущества:

  • Контроль дизайна и удобства использования. Разработчики сами определяют оптимальную читаемость текста, основываясь на специфике своего продукта и дизайне интерфейса. Например, интерфейс социальных сетей зачастую строится вокруг картинок и иконок, где большой шрифт мог бы нарушать общую эстетику и удобочитаемость.

  • Совместимость между устройствами. Игнорирование системных настроек позволяет обеспечить единообразие отображения приложения на разных устройствах независимо от версий ОС или производителей смартфонов.

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

Конечно, пользователи с проблемами зрения, пожилые или владельцы старых устройств с небольшими экранами, привыкшие увеличивать шрифт, сталкиваются с неудобствами при работе с такими приложениями. Ещё это идет вразрез с рекомендациями Material Design от Google, их игнорирование создаёт ощущение несогласованности среди продуктов одного производителя или экосистемы.

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

Рисунок 20. Настройки экрана. Выбор типа ячейки для отображения активов.
Рисунок 20. Настройки экрана. Выбор типа ячейки для отображения активов.

Как видно из графика, количество пользователей, использующих увеличенную ячейку, значительно выросло и продолжает расти.

Рисунок 21. График отображения увеличенных (перестроенной) ячеек активов, по пользователям.
Рисунок 21. График отображения увеличенных (перестроенной) ячеек активов, по пользователям.

Результаты

Вот так стал выглядеть наш экран после всех доработок:

Рисунок 22. Отображение экрана при большом шрифте: до (слева) и после (справа).
Рисунок 22. Отображение экрана при большом шрифте: до (слева) и после (справа).

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

Какие выводы сделала из этой истории наша команда:

  1. Обеспечение доступности — это не опциональная функция, а неотъемлемая часть UX-проектирования, которая должна быть заложена в процесс разработки с самого начала.

  2. Изучите свою аудиторию, именно она задаёт вектор развития приложения. Не бойтесь проводить собственные исследования и тестирования пользовательского опыта. Если пользователи не требуют этого, это не означает, что они в этом не нуждаются.

  3. Не все изменения интерфейса можно или нужно автоматизировать. Иногда важно дать «ручки» самим пользователям, позволив им настроить интерфейс под свои нужды.

  4. Поддержка доступности — это не задача одного человека, это зона ответственности всей команды, и каждый разработчик должен говорить: «Мне нужно поддерживать доступность своей фичи!».

Теперь, проектируя новые экраны или компоненты, мы уделяем внимание тому, как будет выглядеть интерфейс при увеличении текста. Надеюсь, после этой статьи вы сможете учесть наш опыт и пересмотреть подходы к проектированию интерфейса, которые сложились в вашем проекте.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Уделяете ли вы внимание отображению увеличенного UI?
0%Да, конечно0
25%Не всегда. Чаще всего используются только базовые стратегии (многострочность, ellipsize)1
75%Нет3
0%Свой вариант в комментариях0
Проголосовали 4 пользователя. Воздержавшихся нет.