Как реализовать текст с декоративной заглавной буквой в Android-приложении и почему это важно для UI

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Привет! Меня зовут Юля, я Android-разработчица в продуктовой компании OBRIO. Мы ― часть экосистемы бизнесов Genesis и разрабатываем мобильные приложения и игры.

В этой статье хочу уделить внимание ценности UI для успеха приложения и рассмотрю пример компонента для текста с большой заглавной буквой. Понятие «компонент» я использую для обозначения нескольких UI-элементов, объединенных и сконфигурированных для получения желаемого результата.

Материал посвящается всем заинтересованным, а особенно начинающим программистам, которые ищут вдохновение и идеи для красивого UI простыми средствами.

Пример компонента взят из астрологического приложения Nebula, в разработке которого я принимаю участие. Продукт имеет многомиллионную аудиторию пользователей и высокий рейтинг в App Store и Google Play. Предоставляя широкий спектр экспертных услуг, мы также заботимся о комфорте пользователя, создавая красивый и удобный дизайн, чтобы пребывание в приложении было интересным и продолжительным.

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

В Nebula огромное количество интересных UI-решений. Но сегодня остановимся на UI-компоненте с увеличенной первой буквой.

Проблема

Задача — сделать заглавную букву определенного размера, чтобы остальной текст обрамлял ее.

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

Решение 1

Для изменения свойств части текста (в нашем случае размера одной буквы) обычно используется SpannableString.

Указываем относительный размер в четыре раза больше основного размера текста RelativeSizeSpan(4f). Предварительно можно сделать первую букву заглавной.

fun String.capitalizeFirst(): String = replaceFirstChar {
   when (it.isLowerCase()) {
       true -> it.titlecase(Locale.ROOT)
       else -> it.toString()
   }
}

При таком подходе:

val str = "sample text"
val capitalizeStr = str.capitalizeFirst()
val spannableString = SpannableString(capitalizeStr)
spannableString.setSpan(RelativeSizeSpan(4f), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = spannableString

Получаем такой результат:

Букву увеличили, но смотрится не так, как на дизайне.

Решение 2

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

Создадим XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_marginHorizontal="@dimen/common_padding">

   <androidx.appcompat.widget.AppCompatTextView
       android:id="@+id/letter"
       android:layout_width="75dp"
       android:layout_height="wrap_content"
       android:layout_marginTop="5dp"
       android:gravity="center"
       android:textColor="@android:color/white"
       android:textSize="44sp"
       app:layout_constraintTop_toTopOf="parent"
       app:layout_constraintStart_toStartOf="parent"/>

   <androidx.appcompat.widget.AppCompatTextView
       android:id="@+id/text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:lineSpacingExtra="3dp"
       android:textColor="@android:color/white"
       android:textSize="18sp"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

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

val n = (height / textLineHeight).toInt()

Чтобы сдвинуть n строк на ширину заглавной буквы w, реализуем свой класс, наследовавшись от LeadingMarginSpan.LeadingMarginSpan2. Нужно определить два параметра:

  • lines — количество строк для изменения отступа;
  • margin — отступ.

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

class MarginSpanHelper (private var lines: Int,
                        private var margin: Int): LeadingMarginSpan.LeadingMarginSpan2 {

  // возвращаем величину отступа для текущей строки (параметр margin или 0) 
 // first = true, если запрашивается отступ для первой строки
   override fun getLeadingMargin(first: Boolean): Int { }

  //  возвращаем количество строк, к которому будет применен отступ
 //  (параметр lines)
   override fun getLeadingMarginLineCount(): Int { } 
}

Для удобства вынесла код в extension:

firstLetterView.text = “F”
textView.makeTextAroundView(firstLetterView, text)

fun TextView.makeTextAroundView(firstLetterView: View, text: String) {
   val height = firstLetterView.measuredHeight
   val width = firstLetterView.measuredWidth
   val textLineHeight = paint.textSize
   val span = SpannableString(text)
   val linesWithMargin = (height / textLineHeight).toInt()
   val marginHelper = MarginSpanHelper(linesWithMargin, width)
   span.setSpan(marginHelper, 0, span.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
   this.text = span
}

Добавим шрифт к заглавной букве и получим желаемый результат:

firstLetterView.typeface = ResourcesCompat.getFont(context, fontId)

Вместо элемента TextView для заглавной буквы можно использовать картинку, анимацию, любой нужный вам view.

Вывод

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

Спасибо за внимание, всем красивого UI.

Сподобалась стаття? Натискай «Подобається» внизу. Це допоможе автору виграти подарунок у програмі #ПишуНаDOU

👍ПодобаєтьсяСподобалось4
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

1) Нет смысла в верстке писать androidx.appcompat.widget.AppCompatTextView, можно просто TextView, он все равно в момент inflate-а будет заменен на AppCompatTextView
2) Не вижу смысла в конструкции

when (it.isLowerCase()) {
true -> it.titlecase(Locale.ROOT)
else -> it.toString()
}

Обычный if-else тут более читабельный

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

согласна, спасибо за конструктивный комментарий)

такий інтерфейс завжди мене ставить в ступор, досі не можу чому перший рядок починається с 0 позиції, а ось другий рядок вже зміщений на перший символ

Где можно прочитать «почему это важно для UI» ? Может есть статистика, что после такого изменения кол-во пользователей, дочитавших текст до конца увеличилось на 10%, или какая-нибудь другая метрика?

Пример компонента взят из астрологического приложения Nebula

здесь метрики предсказывают астрологи

Я решила рассмотреть решения, которые могут быть полезным для разработчиков приложений на Android, не претендую на истину. Цель статьи — поделиться опытом и подсказать возможные решения разработчикам, которые столкнулись с описанной проблемой. Благодаря интересному дизайну, приложение становится привлекательнее для пользователя.

И как эта буквица сделала интерфейс «удобным»?

Ну, разработка стала стоить чуть дороже. Удобно же!

Вначале показалось, что на stackoverflow зашел

Почитай текст на первой картинке. Сразу поймёшь, что показалось.

Не сразу понял, что вот этот конденсатор под наклоном это буква F.

Это буква Г. Сразу прочитал gulfing relationship, ИЧСХ, смысла только добавилось :)

Підписатись на коментарі