Русский
Русский
English
Статистика
Реклама

Стилизация

5 подходов к стилизации React-компонентов на примере одного приложения

16.02.2021 12:20:24 | Автор: admin


Доброго времени суток, друзья!

Сегодня я хочу поговорить с вами о стилизации в React.

Почему данный вопрос является актуальным? Почему в React существуют разные подходы к работе со стилями?

Когда дело касается разметки (HTML), то React предоставляет в наше распоряжение JSX (JavaScript и XML). JSX позволяет писать разметку в JS-файлах данную технику можно назвать HTML-в-JS.

Однако, когда речь идет о стилях, то React не предоставляет каких-либо специальных инструментов (JSC?). Поэтому каждый разработчик волен выбирать такие инструменты по своему вкусу.

Всего можно выделить 5 подходов к стилизации React-компонентов:

  • Глобальные стили все стили содержатся в одном файле (например, index.css)
  • Нативные CSS-модули для каждого компонента создается отдельный файл со стилями (например, в директории css); затем эти файлы импортируются в главный CSS-файл (тот же index.css) с помощью директивы "@import"
  • Реактивные CSS-модули (данная техника используется не только в React-проектах; реактивными я назвал их потому, что библиотека css-modules в настоящее время интегрирована в React, т.е. не требует отдельной установки, по крайней мере, при использовании create-react-app) для каждого компонента создается файл Component.module.css, где Component название соответствующего компонента (обычно, такой файл размещается рядом с компонентом); затем стили импортируются в JS-файл в виде объекта, свойства которого соответствуют селекторам класса (например: import styles from './Button.module.css'; <button style={styles.button}>Нажми на меня</button>)
  • Встроенные (инлайновые) стили элементы стилизуются с помощью атрибутов style со значениями в виде объектов со стилями (например, <button style={{ borderRadius: '6px'; } }>Нажми на меня</button>)
  • CSS-в-JS библиотеки, позволяющие писать CSS в JS-файлах; одной из таких библиотек является styled-components: import styled from 'styled-components'; const Button = styled`какой-то css`; <Button>Нажми на меня</Button>

На мой взгляд, лучшим решением является последний подход, т.е. CSS-в-JS. Он выглядит самым логичным с точки зрения описания структуры (разметки), внешнего вида (стилей) и логики (скрипта) компонента в одном файле получаем нечто вроде Все-в-JS.

Шпаргалку по использованию библиотеки styled-components можно найти здесь. Возможно, вам также интересно будет взглянуть на шпаргалку по хукам.

Ну, а худшим подходом, по моему мнению, являются встроенные стили. Стоит, однако, отметить, что определение объектов со стилями перед определением компонента и последующее использование этих объектов напоминает CSS-в-JS, но остаются camelCase-стиль, атрибуты style и сами встроенные стили, которые затрудняют инспектирование DOM.

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

Исходный код GitHub.

Песочница:


Выглядит приложение так:



Приложение состоит из трех компонентов: Title заголовок, Counter значение счетчика и информация о том, каким является число: положительным или отрицательным, четным или нечетным, Control панель управления, позволяющая увеличивать, уменьшать и сбрасывать значение счетчика.

Структура проекта следующая:

|--public  |--index.html|--src  |--components    |--Control      |--Control.js      |--Control.module.css      |--package.json      |--styles.js    |--Counter      |--Counter.js      |--Control.module.css      |--package.json      |--styles.js    |--Title      |--Title.js      |--Title.module.css      |--package.json    |--index.js  |--css    |--control.css    |--counter.css    |--title.css  |--App.js  |--global.css  |--index.js  |--nativeModules.css  |--reactModules.css...

Пройдемся по некоторым файлам, находящимся в директории src:

  • index.js входная точка JavaScript (в терминологии бандлеров), где импортируются глобальные стили и рендерится компонент App
  • App.js основной компонент, где импортируются и объединяются компоненты Control, Counter и Title
  • global.css глобальные стили, т.е. стили всех компонентов в одном файле
  • nativeModules.css файл, где импортируются и объединяются нативные CSS-модули из директории css (control.css, counter.css и title.css)
  • reactModules.css глобальные стили для реактивных CSS-модулей
  • components/Control/Control.js три реализации компонента Control (с глобальными стилями/нативными CSS-модулями, c реактивными CSS-модулями и стилизованными компонентами), а также пример объекта со встроенными стилями
  • components/Control/Control.module.css реактивный CSS-модуль для компонента Control
  • components/Control/styles.js стилизованные компоненты для компонента Control (когда стилизованных компонентов много, я предпочитаю выносить их в отдельный файл)
  • components/Control/package.json файл с main: "./Control", облегчающий импорт компонента (вместо import Control from './Control/Control' можно использовать import Control from './Control'
  • components/index.js повторный экспорт, позволяющий разом импортировать все компоненты в App.js

Как всегда, буду рад любой форме обратной связи.

Благодарю за внимание и хорошего дня.
Подробнее..

Стилизуя нестандартно

15.04.2021 20:09:13 | Автор: admin

Вот есть у нас приложение. Серьезное, большое, взрослое. Обходимся практически без стилей, но без беспорядка; используем себе виджеты из AppCompat, но уже затянули тему из Material Design Components (MDC) и подумываем о полноценной миграции.

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

Но не все так просто:

  • Redesign предполагается по частям. То есть в приложении будут как экраны со старым, так и с новым внешним видом

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

  • Presentation слой разбит на отдельные ui модули. Причем некоторые из них используются другим приложением. Учитывая, что обходимся без стилей, для стилизации в таких модулях некоторые свойства спрятаны за атрибуты: цвета, текстовые стили, строки и многое другое

  • Существует налаженная схема на предмет того, как работать с вышеупомянутыми ui модулями. В частности с атрибутами. А значит и с цветами, текстовыми стилями, строками и прочим. А при MDC хотелось бы использовать стили

Далее делюсь опытом того, как справиться с этими трудностями: как при переезде на MDC частично стилизовать Android приложение с независимыми ui модулями, абстрагироваться от дизайн системы и при этом ничего не сломать. Бонусом - советы и разбор сложностей, с которыми я столкнулся.

лего равно стилилего равно стили

Про ui модули

Есть ui модули. Они не зависят от проекта. Лежат отдельно от него.

Внутри каждого из проектов есть корневой модуль. Назовем его core-presentation. Он зависит от тех ui модулей, которые используются в данном приложении. Подключаются модули как обычная gradle зависимость.

Возникает вопрос. А как стилизовать-то? Если коротко, то с помощью атрибутов. Внутри каждого такого ui модуля определены используемые атрибуты, которые должны быть реализованы темой приложения:

<resources><!-- src --><attr name = "someUiModuleBackgroundSrc" format = "reference" /><!-- string --><attr name = "someUiModuleTitleString" format = "reference" /><attr name = "someUiModuleErrorString" format = "reference" /><!-- textAppearance --><attr name = "someUiModuleTextAppearance1" format = "reference" /><attr name = "someUiModuleTextAppearance2" format = "reference" /><attr name = "someUiModuleTextAppearance3" format = "reference" /><attr name = "someUiModuleTextAppearance4" format = "reference" /><attr name = "someUiModuleTextAppearance5" format = "reference" /><attr name = "someUiModuleTextAppearance6" format = "reference" /><attr name = "someUiModuleTextAppearance7" format = "reference" /><attr name = "someUiModuleTextAppearance8" format = "reference" /><!-- color --><attr name = "someUiModuleColor1" format = "reference" /><attr name = "someUiModuleColor2" format = "reference" /></resources>

Используются они примерно так:

<androidx.appcompat.widget.AppCompatTextViewandroid:background = "?someUiModuleBackgroundSrc"android:text = "?someUiModuleErrorString"android:textAppearance = "?someUiModuleTextAppearance5".../>

Ближе к "теме" (стилю)

У меня появился план. Простой, но от того не менее гениальный. План базировался на нескольких принципах, а я, в свою очередь, его придерживался.

Собственно, принципы:

  • Так как MDC тема уже затянута, ничто не мешает использовать виджеты из MDC. Никакого AppCompat'a. И хоть под капотом framework компоненты переопределяются в аналоги из MDC, явное использование последних компонент все же нагляднее:

    <TextView.../><!-- Bad --><androidx.appcompat.widget.AppCompatTextView.../><!-- Bad --><com.google.android.material.textview.MaterialTextView.../><!-- Good -->
    
  • Все компоненты (классы, ресурсы, атрибуты) нового ui в названии содержат какой-нибудь одинаковый префикс или постфикс (например, v2)

  • Стиль - это единственный способ изменить внешний вид View. Иными словами, каждая View обладает стилем (либо через style в xml, либо через дефолтный атрибут стиля посредством defStyleAttr), и только этот стиль определяет её внешний вид. Примеры:

    <!-- Good --><com.google.android.material.appbar.MaterialToolbarstyle = "?toolbarStyleV2"/><!-- Bad --><com.google.android.material.appbar.MaterialToolbarandroid:background = "?primaryColorV2"/>
    
  • Название стиля не должно раскрывать его внешний вид. При этом оно должно базироваться на названии компонента дизайн системы. Примеры:

    <item name = "filledTextInputStyleV2">@style/V2.Widget.MyFancyApp.TextInputLayout.Filled</item> <!-- Bad --><item name = "searchTextInputStyleV2">@style/V2.Widget.MyFancyApp.TextInputLayout.Search</item> <!-- Good --><item name = "blackOutlinedButtonStyleV2">@style/V2.Widget.MyFancyApp.Button.BlackOutlined</item> <!-- Bad --><item name = "primaryButtonStyleV2">@style/V2.Widget.MyFancyApp.Button.Primary</item> <!-- Good --><item name = "secondaryButtonStyleV2">@style/V2.Widget.MyFancyApp.Button.Secondary</item> <!-- Good --><item name = "textButtonStyleV2">@style/V2.Widget.MyFancyApp.Button.Text</item> <!-- Ok. Based on Figma component name -->
    
  • Все ресурсы, включая имплементации стилей, лежат внутри core-presentation

Как итог:

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

  • UI модули не содержат никаких ресурсов

  • Пересечение именований компонентов старого и нового ui исключено вследствие префикса-постфикса


Вроде не сложно: используй только стили; определяй нужные цвета в этих стилях. Но так ли это все просто на практике?

Да. Но ровно до тех пор, пока не нарвешься на TextView. А как быть здесь? Ровно также. Использовать стили. Проблема лишь в том, что таких стилей будет до бесконечности много. Почти под каждый TextView нужно заводить отдельный стиль. В защиту такого решения отмечу, что из статьи про MDC можно косвенно сделать вывод, что тривиальный текст - тоже отдельный стиль:

While TextAppearance does support android:textColor, MDC tends to separate concerns by specifying this separately in the main widget styles

Примеры:

<item name = "v2TextStyleGiftItemPrice">@style/V2.Widget.MyFancyApp.TextView.GiftItemPrice</item><item name = "v2TextStyleGiftItemName">@style/V2.Widget.MyFancyApp.TextView.GiftItemName</item>...<style name = "V2.Widget.MyFancyApp.TextView.GiftItemPrice">    <item name = "android:textAppearance">?v2TextAppearanceCaption1</item>    <item name = "android:textColor">?v2ColorOnPrimary</item></style><style name = "V2.Widget.MyFancyApp.TextView.GiftItemName">    <item name = "android:textAppearance">?v2TextAppearanceCaption1</item>    <item name = "android:textColor">?v2ColorOnPrimary</item>    <item name = "textAllCaps">true</item>    <item name = "android:background">?v2ColorPrimary</item></style>...<com.google.android.material.textview.MaterialTextViewstyle = "?v2TextStyleGiftItemPrice".../><com.google.android.material.textview.MaterialTextViewstyle = "?v2TextStyleGiftItemName".../>

Если приглядеться, то можно заметить, что для всех названий атрибутов стилей в примере используется постфикс v2 (например, primaryButtonStyleV2), а для текстовых стилей - префикс (v2TextStyleGiftItemName). Сделано это для того, чтобы упростить навигацию при автоподстановке IDE.


По итогу, после таких переделок файл с атрибутами в новом ui модуле выглядит примерно так:

<resources><!-- Общие стили --><attr name = "cardStyleV2" format = "reference" /><attr name = "appBarStyleV2" format = "reference" /><attr name = "toolbarStyleV2" format = "reference" /><attr name = "primaryButtonStyleV2" format = "reference" />...<!-- Стили для TextView --><attr name = "v2TextStyleGiftCategoryTitle" format = "reference" /><attr name = "v2TextStyleGiftItemPrice" format = "reference" /><attr name = "v2TextStyleSearchSuggestion" format = "reference" /><attr name = "v2TextStyleNoResultsTitle" format = "reference" />...<!-- Иконки --><attr name = "ic16CreditV2" format = "reference" /><attr name = "ic24CloseV2" format = "reference" /><attr name = "ic48GiftSentV2" format = "reference" />...<!-- Строки --><attr name = "shopTitleStringV2" format = "reference" /><attr name = "shopSearchHintStringV2" format = "reference" /><attr name = "noResultsStringV2" format = "reference" />...<!-- styleable кастомных View --><declare-styleable name = "ShopPriceSlider"><attr name = "maxPrice" format = "integer" /></declare-styleable></resources>

Почти все зашито в стили. Исключение составляют строки и иконки. Они имеют отношение к контенту, а не к внешнему виду.

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

Что касается иконок, то, в целом, под них тоже можно завести отдельные стили. Все на стилях.

А как быть с android:background, когда просто нужна какая-нибудь подложка? Цвет или форма там какая-нибудь. Об этом чуть позже. Спойлер - через стили.


Рассмотрим несколько стилей:

<style name = "V2.Widget.MyFancyApp.TextView.GiftItemName">    <item name = "android:textAppearance">?v2TextAppearanceCaption1</item>    <item name = "android:textColor">?v2ColorOnPrimary</item></style><style name = "V2.Widget.MyFancyApp.Button.Primary" parent = "Widget.MaterialComponents.Button">...</style><style name = "V2.Widget.MyFancyApp.Button.Primary.Price">...<item name = "icon">?ic16CreditV2</item></style>

Можно заметить, что текстовые стили (android:textAppearance) и цвета используются через атрибуты. Также и иконки. И это все в core-presentation, где, собственно, все это доступно и напрямую (через @color/, @style/, @drawable/). Так зачем же?

Ответ: для гибкости. Такой подход дает преимущества в случае появления новых тем. Примеры:

  • Темная (или любая другая, отличная от оригинальной по палитре) тема. В новой теме просто меняем значения атрибутов цветов на нужные

  • "Тематические" темы (Halloween, Christmas, Easter и так далее). Переопределяем иконки и шрифты под саму тематику. Разобраться с тем, как и когда использовать такие темы, - дело третье

Подводные камни, сложности, советы

MaterialThemeOverlay

Если вдруг вам потребуется определить android:theme в дефолтном стиле кастомной View, то ничего у вас не выйдет. Просто не сработает. Хотя для любого другого, не дефолтного стиля все отлично работает. Подробнее проблема разобрана в этой статье.

Но отчаиваться не стоит, ведь и для данного проблемного случая есть решение. Меняем android:theme на materialThemeOverlay, оборачиваем контекст через MaterialThemeOverlay.wrap(...) и все работает.

Где-то в xml:

<item name = "achievementLevelBarStyleV2">@style/V2.Widget.MyFancyApp.AchievementLevelBar</item><style name = "V2.Widget.MyFancyApp.AchievementLevelBar" parent = ""><item name = "materialThemeOverlay">@style/V2.ThemeOverlay.MyFancyApp.AchievementLevelBar</item></style>

Сама кастомная View:

class AchievementLevelBar @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = R.attr.achievementLevelBarStyleV2) : LinearLayoutCompat(MaterialThemeOverlay.wrap(context, attrs, defStyleAttr, 0), attrs, defStyleAttr) {init {View.inflate(context, R.layout.achievement_level_bar, this)...}...}

И это не работает. А не работает это из-за того, что манипуляции в init {} блоке осуществляются с исходным context, а не с обернутым. Отсюда вырисовывается очень простое правило: никогда не использовать исходный context при инициализации. Для того, чтобы в данном примере materialThemeOverlay заработал, необходимо context заменить на getContext(). Просто оставлю кусок MaterialButton здесь:

  public MaterialButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {    super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr);    // Ensure we are using the correctly themed context rather than the context that was passed in.    context = getContext();

(А если так сделать в Kotlin, то Lint будет ругаться на name shadowing. Грусть)

Light status bar

У нас на проекте для подложки под status bar используется кастомная StatusBarView. В идеале, такой штуки быть не должно (потому что edge-to-edge), но пока что она присутствует. Довольствуемся тем, что есть.

Так вот, в старом дизайне status bar повсеместно translucent. Что это значит: есть какой-то полупрозрачный темный overlay (причем везде разный), а цвет контента - белый или около того. В новом же дизайне status bar может быть светлым (light): со светлым background и темным контентом.

Слева - translucent; справа - lightСлева - translucent; справа - light

Собственно задача заключается в том, чтобы уметь поддерживать light status bar наравне с translucent через кастомную StatusBarView. Нюансы:

  • Для поддержки light status bar необходима 23я версия SDK (или выше). Для всех версий, что ниже, можно отображать дефолтный translucent status bar (идея взята отсюда)

  • Translucent status bar достигается с помощью выставления флага FLAG_TRANSLUCENT_STATUS; overlay без полупрозрачности (для light) - с помощью FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS

  • Чтобы менять цвет контента, понадобятся следующие методы:

fun setLightStatusBar() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {var flags = window.decorView.systemUiVisibilityflags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BARwindow.decorView.systemUiVisibility = flags}}fun clearLightStatusBar() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {var flags = window.decorView.systemUiVisibilityflags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()window.decorView.systemUiVisibility = flags}}
  • Без FLAG_TRANSLUCENT_STATUS кастомная StatusBarView не залазит под status bar. Исправляется это примерно так:

class StatusBarView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {init {...systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}}
  • Чтобы окончательно использовать кастомную StatusBarView для light status bar, нужно задать прозрачный statusBarColor

  • Возвращаясь к стилям, всю эту логику с light / translucent status bar можно зашить в кастомный атрибут StatusBarView

Color State List (CSL)

В MDC статье про цвета для полупрозрачных оттенков какого-либо цвета советуется использовать CSL. Дело в том, что с 23й версии SDK для CSL доступны атрибуты. И свойство android:alpha. А если соединить, то получится любой цвет с любой прозрачностью.

Выглядит это примерно так:
color/v2_on_background_20.xml

<selector xmlns:android = "http://schemas.android.com/apk/res/android"><item android:alpha = "0.20" android:color = "?v2ColorOnBackground" /></selector>

Используются такие цвета не через атрибут, а напрямую, через @color/ . Мощь данного подхода в том, что такой CSL зависит от какого-то цвета. Что внутри v2ColorOnBackground не имеет никакого значения. Без CSL пришлось бы лезть в палитру и добавлять для каждого v2ColorOnBackground аналог с 20% прозрачностью:

<color name = "black">#000000</color> <!-- v2ColorOnBackground --><color name = "black_20">#33000000</color> <!-- v2ColorOnBackground 20% opacity -->

Хоть это все и здорово, но есть свои заморочки:

  • Как уже писал ранее, для поддержки необходима 23я версия SDK и выше. Но вообще, для MDC виджетов все работает нормально и с 21й версии. Если же так получилось, что нужно дернуть такой CSL через атрибут (например, в кастомной View для кастомного атрибута), то на помощь приходит метод MaterialResources.getColorStateList(). Вот только это является частью Restricted API, но кого это останавливало

  • CSL не работает в качестве android:background и схожих. Но ничто не мешает сделать так:

<style name = "V2.Widget.MyFancyApp.Divider" parent = ""><item name = "android:background">@drawable/v2_rect</item><item name = "android:backgroundTint">@color/v2_on_background_15</item>...</style>

Подложка и android:background

Сразу к делу. Никаких </shape> через xml. Вот v2_rect.xml из примера выше - это единственный допустимый случай. MDC отказался от этого. И всем следует.

А если нужна подложка, то почему бы не посмотреть в сторону ShapeableImageView (ну или на крайний случай MaterialCardView)? Здесь и способов кастомизации больше. Как пример:

<com.google.android.material.imageview.ShapeableImageViewstyle = "?shimmerStyleV2"  .../><item name = "shimmerStyleV2">@style/V2.Widget.MyFancyApp.Shimmer</item><style name = "V2.Widget.MyFancyApp.Shimmer"><item name = "srcCompat">@drawable/v2_rect</item><item name = "tint">@color/v2_on_background_15</item><item name = "shapeAppearance">@style/V2.ShapeAppearance.MyFancyApp.SmallComponent.Shimmer</item></style>

Стили компонент ViewGroup

Рассмотрим пример:

<com.google.android.material.appbar.AppBarLayoutstyle = "?appBarStyleV2"...><my.magic.path.StatusBarViewstyle = "?statusBarStyleV2".../><com.google.android.material.appbar.MaterialToolbarstyle = "?toolbarStyleV2".../></com.google.android.material.appbar.AppBarLayout>

Представим, что такая конструкция встречается почти на каждом новом экране. Учтем, что здесь определено три атрибута стиля.

Вдруг появляется нестандартный экран. На нем все три стиля отличаются. Вопрос: сколько новых атрибутов потребуется? Правильный ответ - один, для AppBarLayout(назовем новый атрибут secondaryAppBarStyleV2 ). Для всего остального есть ThemeOverlay:

<item name = "secondaryAppBarStyleV2">@style/V2.Widget.MyFancyApp.AppBarLayout.Secondary</item><style name = "V2.Widget.MyFancyApp.AppBarLayout.Secondary"><item name = "materialThemeOverlay">@style/V2.ThemeOverlay.MyFancyApp.AppBarLayout.Secondary</item>...</style><style name = "V2.ThemeOverlay.MyFancyApp.AppBarLayout.Secondary" parent = ""><item name = "statusBarStyleV2">@style/V2.Widget.MyFancyApp.StatusBar.Secondary</item><item name = "toolbarStyleV2">@style/V2.Widget.MyFancyApp.Toolbar.Secondary</item></style>

Пример конкретный, но применять такое можно к любой ViewGroup. В частности, к кастомной View. Если есть уверенность в том, что какая-то View (и ее стиль) будет использоваться исключительно в контексте определенной ViewGroup, то можно не имплементировать атрибут ее стиля на уровне темы приложения, а сделать это на уровне ThemeOverlay ViewGroup.

MaterialToolbar и Toolbar из AppCompat

Под капотом многие framework виджеты при inflate преобразуются в соответствующие из MDC. Чтобы ничего случайно не сломать виджетами из MDC, при затягивании темы (то есть до начала сего рассказа) все framework виджеты были заменены аналогами из AppCompat. Примерно так:

<!-- Было --><Toolbar.../><!-- Стало --><androidx.appcompat.widget.Toolbar.../>

И это нормально-таки себе работало. Таким образом получили следующее: в новых скринах используется MaterialToolbar , в старых - Toolbarиз AppCompat.

Здесь возник один интересный баг. Для стиля MaterialToolbar был определен атрибут navigationIconTint. Этот атрибут не поддерживается Toolbarиз AppCompat. Тем не менее, при переходе с нового скрина на старый, navigationIcon в Toolbarкаким-то образом красился с помощью navigationIconTint. Помог лишь полный переезд на MaterialToolbar.

Стили и размеры

Вот есть такая штука в Material Design Guidelines, как Dense text fields. По сути это TextInputLayoutс высотой в 40dp. Есть даже стили под него (Widget.MaterialComponents.TextInputLayout.*.Dense). Ограничений (в Guidelines) на предмет наличия иконок (в начале или в конце) нет; более того, даже есть пример с иконкой.

Берем TextInputLayout, выставляем ему Dense стиль, добавляем start icon и... это ничем не отличается от обычного, не Dense стиля. Копаем в сторону того, а как же тогда получить высоту в 40dp. Надеемся на лучшее, в нужных стилях выкручиваем в 0 вертикальные padding. Не помогает.

Причина оказалась в design_text_input_start_icon.xml, где для start icon установлены минимальные размеры в 48dp. Тем не менее, если выставить для TextInputLayout 40dp в android:layout_height, все выглядит как нужно.

Не будем забывать про стили. Dense - это про стиль. Следовательно, android:layout_height должен в этом случае лежать внутри стиля. А это плохо тем, что в каждом месте использования TextInputLayoutс таким стилем придется выпилить android:layout_height из разметки (ответ на вопрос, почему так):

<item name = "searchTextInputStyleV2">@style/V2.Widget.MyFancyApp.TextInputLayout.Search</item><style name = "V2.Widget.MyFancyApp.TextInputLayout.Search" parent = "Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"><item name = "android:layout_height">40dp</item>...</style><!-- Не сработает --><com.google.android.material.textfield.TextInputLayoutstyle = "?searchTextInputStyleV2"android:layout_width = "match_parent"android:layout_height = "wrap_content"/>  <!-- Сработает --><com.google.android.material.textfield.TextInputLayoutstyle = "?searchTextInputStyleV2"android:layout_width = "match_parent"/>

Возможно это просто баг и в дальнейшем такого workaround получится избежать.


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

Используйте средства стилизации по максимуму. Это не сложно. Спасибо за прочтение.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru