Привет, Хабрахабр! Не так давно мы подводили итоги конкурса по Material Design, и в комментариях нас просили показать реально популярные и красивые Material-приложения. Что же, встречайте: «Сбербанк Онлайн» в новом, современном интерфейсе. Про процесс создания приложения интереснее узнать от самих создателей.
Мы передаём слово команде разработчиков Android-приложения Сбербанка, чтобы вы услышали об опыте создания такой сложной штуки, как UI мобильного банк-клиента, из первых уст. Большую часть поста написал freeuser, так что спасибо говорите ему. 😉
Предыстория
Android 5, основанный на Material Design, доступен пользователям уже полтора года. 18 месяцев — срок немалый, и сейчас можно с уверенностью сказать, что и пользователям, и разработчикам пришлась по душе философия визуального языка представления информации от Google.
Сегодня мы расскажем, как в нашей расширяющейся и развивающейся команде протекал процесс перехода приложения "Сбербанк Онлайн" для Android на Material Design, и осветим сопутствующие технические аспекты. Процесс непосредственно создания интерфейса будет подробно описан в следующей статье.
Постановка задачи
Наша основная задача — сделать удобное и понятное приложение, которым было бы приятно пользоваться. Вместе с тем, переход на Material Design позволил бы качественно улучшить и процесс разработки. То есть, программисты и дизайнеры меньше будут отвлекаться на «процесс», больше — на результате.
Единый визуальный язык, который разработали в Google, позволил меньше задумываться о том, «что делать», сосредоточившись на вопросе «как делать». И если предыдущая попытка (Holo) не остановила калькирование интерфейсов с iOS или придумывание велосипедов, то новая система (MD) практически полностью «убила» зоопарк дизайнерских изысков. Приложения не стали скучнее или однообразнее, но стали единообразнее: MD позволяет крайне свободно использовать имеющиеся инструменты и возможности визуального языка, вместе с тем сохраняя преемственность интерфейсов, что удобно и пользователям, и разработчикам. Когда все приложения работают одинаково и различные элементы интерфейса в них работают одинаково, вам не требуется привыкать к новым приложениям. Берёте и пользуетесь.
Простота, удобство для пользователя и эффективность — вот ключевые понятия, от которых мы отталкивались в работе над новым приложением.
Имеющиеся проблемы
Общие проблемы для разработки почти всегда одни и те же:
- Программисты не всегда внимательно относятся к макетам;
- Дизайнеры слабо осведомлены о том, насколько трудозатратна для программиста реализация того или иного варианта верстки;
- Изменения в одной разметке (layout) не масштабируются автоматом на другие.
В корне этих «общих» проблем лежат вполне конкретная беда, которую, увы, очень тяжело уладить. Дизайнеры и программисты говорят на своих языках и не понимают друг друга. В чём заключается недопонимание?
- Программист не может смоделировать мышление дизайнера, из-за чего не может установить от чего ему отталкиваться в верстке;
- Дизайнер не знает, какую именно информацию выдавать программисту для того, чтобы тот сверстал масштабируемо и с точным соответствием макету.
Рассмотрим частный случай типичного рабочего процесса:
На макетах экранов приложения дизайнеру необходимо отметить размеры и цвет текста, ширину и высоту элементов. При нулевой коммуникации (и, следовательно, нулевом понимании потребностей и возможностей друг друга) задача решается в лоб: на макетах зашиваются непосредственно значения — размер, цвет.
Далее, исполнитель верстки либо жестко забивает значения в разметку, либо заводит новый ресурс (size, color), либо пытается по значению отыскать уже имеющийся ресурс.
Жесткое зашивание значений в разметку либо несистематизированное заведение ресурсов влекут за собой невозможность повторного использования одних и тех же значений: нарушается и принцип DRY, и вообще сама логика разделения конструкции и её стилей. Работать над таким приложением примерно также «приятно», как над веб-страницей, на которой половина значений стилей указана прямо внутри div’ов.
Если встает необходимость изменить цвет текста на информационных ячейках, то необходимо пройтись по каждой разметке (при этом есть ненулевая вероятность пропустить какой-либо файл). Стоимость операции возрастает многократно при экспериментах «Последовательно проверить несколько цветов/отступов».Наконец, если псевдоним заведен для ресурса не системным образом, то, скорее всего, его название неактуально для нового экрана, и программист вместо использования готового ресурса создаст новую сущность.
Чтобы всего этого бардака избежать, требуется наладить здоровую атмосферу в рабочем коллективе:
- Между программистами и дизайнерами должна быть налажена коммуникация: они должны понимать и принимать потребности и возможности друг друга;
- Список требуемых данных для построения универсальной и масштабируемой вёрстки приложения должен быть чётко закреплён: так программисты получат все необходимые для них значения, а дизайнеры смогут отталкиваться от имеющихся значений при построении новых экранов.
Doing it right way
Своеобразным «первым мостиком» в построении коммуникации между программистами и дизайнерами выступили Google Material Guidelines. Гайдлайны достаточно гибки, чтобы не стеснять творческую мысль дизайнера и одновременно они определяют закономерности, удобные для моделирования программистом.
Для масштабируемой верстки в OS Android используется такой инструмент как стили. Стили — это набор пар «атрибут-значение». Значениями атрибутов могут выступать цвета, ColorStateList, Drawable, текст, размеры и другие стили.
При исследовании ресурсов последних платформ и библиотеки AppCompat можно обнаружить, что стили делятся на следующие группы:
- TextAppearance — внешний вид текстового поля. Определяют цвета текста и ссылок, подсказок в текстовом поле, фон выделенного текста, стиль и гарнитуру шрифта;
- Widget-стиль — стиль View. Определяют специфические для конкретного виджета атрибуты (например, progressDrawable для ProgressBar), виджет-стили TextView и его наследников могут указывать используемый TextAppearance;
- Theme — тема. Темы — это своего рода «стили стилей», стили активити; Определяют ключевые атрибуты, характерные для данного экрана, стили виджетов. Грамотно составив тему, мы избежим прямых ссылок на цвета и стили виджетов внутри разметок (layout), установим единообразный внешний вид для View в рамках приложения;
- ThemeOverlay — занимают промежуточное положение между виджет-стилями и темами. Если обычный виджет-стиль применяется только к одному View, тема — ко всем View в пределах экрана, то ThemeOverlay проставляют на определенный контейнер (ViewGroup) в разметке. В дальнейшем мы покажем, где именно он применяется и как.
В нашем случае нормально построенный рабочий процесс выглядит так:
- Программисты и дизайнеры, основываясь на Google Material Guidelines, формируют единые таблицы ресурсов (далее в статье вы их увидите);
- Каждому ресурсу (размеру, стилю, цвету) присваивается псевдоним;
- Дизайнер в макете указывает именно псевдоним. По этому псевдониму программист находит ресурс (размер, стиль) и указывает его в разметке.
Свойства стилей
Прежде чем мы займемся настройкой стилей, давайте рассмотрим свойства этого инструмента поближе — чтобы воспользоваться им с максимальной эффективностью.
Наследование стилей
Стили могут наследоваться друг от друга двумя способами. Вот первый:
<style name="Theme"> <item name="android:isLightTheme">false</item> <item name="android:colorBackground">@color/bright_foreground_dark</item> </style> <style name="Theme.Material"> <item name="android:colorBackground">@color/foreground_material_dark</item> </style>
Theme.Material в таком случае унаследует у родительской темы Theme значение атрибута isLightTheme и переопределит значение colorBackground.
Второй способ — указать родителя явно.
<style name="Theme.Material"> <item name="android:isLightTheme">false</item> </style> <style name="Theme.AppCompat.Light.NoActionBar"> <item name="android:isLightTheme">true</item> </style> <style name="Theme.Material.Sbrf" parent="Theme.AppCompat.Light.NoActionBar"> </style>
В таком случае Theme.Material.Sbrf унаследует значение атрибута isLightTheme у Theme.AppCompat.Light.NoActionBar.
Ссылки на значения атрибутов
Стили низкого уровня (TextAppearance и Widget-стили) могут ссылаться на значение атрибута, установленного темой.
Допустим, в приложении есть 2 темы: красная и желтая. Нам необходимо, чтобы на экранах с желтой темой некоторый TextAppearance задавал своим TextView желтый бэкграунд, а на экранах с красной темой — красный бэкграунд.
<style name="TextAppearance.Example" parent="TextAppearance.AppCompat.Title"> <item name="android:textColor">?attr/colorPrimary</item> </style> <style name="Theme.Example" parent="Theme.AppCompat.Light.NoActionBar"> </style> <style name="Theme.Example.Red"> <item name="colorPrimary">@color/color_primary_red</item> <item name="colorPrimaryDark">@color/color_primary_red_dark</item> </style> <style name="Theme.Example.Yellow"> <item name="colorPrimary">@color/color_primary_yellow</item> <item name="colorPrimaryDark">@color/color_primary_yellow_dark</item> </style>
Пусть TextAppearance.Example проставлен на некоторый TextView.
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:textAppearance="@style/TextAppearance.Example" android:text="Test Text"/>
Тогда при применении TextAppearance для этого виджета Андроид подставит в качестве значения textColor именно то значение colorPrimary, которое прописано в данной теме.
Настройка стилей и тем
Основные цвета приложения
Рассмотрим атрибуты, указывающие основные цвета приложения.
Атрибут | Назначение |
colorPrimary | Главный цвет приложения. Обычно в него красят AppBar |
colorPrimaryDark | Более темная версия главного цвета — используется для статус-бара |
colorAccent | Акцентный цвет приложения. Используется косвенным образом для кнопок, SeekBar, ProgressBar |
android:colorBackground | Дефолтный цвет фона |
android:colorForeground | Противоположность вторичному цвету фона |
android:colorForegroundInverse | Вторичный цвет фона |
dividerHorizontal | Цвет горизонтального разделителя |
dividerVertical | Цвет вертикального разделителя |
У Сбербанка есть брендбук, в соответствии с которым требуется оформлять всё: печатную продукцию, интернет-рекламу, сайты, визитки, пакеты и т.д. Официальные цвета регламентированы, их «ближайшие аналоги» — тоже. В соответствии с брендбуком Главным цветом (colorPrimary) для нашего приложения стал зелёный, а Акцентным (colorAccent) — оранжевый. Кроме того, colorPrimary относился к позитивным действиям (подтверждение, покупка), а colorAccent, как правило, — к негативным (отмена).
Стилизация TextView
Настало время разобраться с цветами для текстов, которые предлагаются библиотекой AppCompat. Всего есть две темы (тёмная и светлая), в каждой из которых есть две группы цветов: стандартные и инвертированные. Для тёмной темы (наследницы Theme.AppCompat) стандартными будут светлые оттенки, инвертированными — тёмные. Для светлой темы (которая также наследует значения у Theme.AppCompat), соответственно, наоборот.
Заданные библиотекой цвета имеют свои «названия» и функциональные назначения:
- textColorPrimary — наиболее «сочная» форма цвета.
- textColorSecondary — чуть более бледная форма цвета.
- textColorTertiary — еще более бледная форма.
- textColorHint — цвет подсказок в EditText.
- textColorLink — цвет ссылок.
- textColorHighlight — цвет фона выделенного текста.
- textColorPrimaryActivated — в обычном состоянии то же, что и textColorPrimary, в активированном состоянии — textColorPrimaryInverse
- textColorSecondaryActivated — аналогично textColorPrimaryActivated, только со вторичным цветом.
Кроме того, есть цвета с «жутковатыми» названиями типа textColorPrimaryDisableOnly (используются для текста CompoundButton’ов: радиокнопки, чекбоксы), или textColorPrimaryNoDisable, textColorSecondaryDisableOnly, textColorSecondaryNoDisable — использование последних трех в стилях и Text Appearance обнаружить не удалось.
Мы составили таблицу цветов, используемых в приложении. Каждому атрибуту мы поставим значением ColorStateList — пару из цветов для доступного элемента и недоступного:
Атрибут | АЦвет обычного состояния | АЦвет недоступного состояния (disabled-элементов) |
textColorPrimary | #E6000000 (90% черного) | #44000000 (26% черного) |
textColorSecondary | #CC000000 (80% черного) | #44000000 (26% черного) |
textColorTertiary | #88000000 (53% черного) | #44000000 (26% черного) |
textColorHint | #61000000 (38% черного) | #19000000 (10% черного) |
textColorPrimaryInverse | #FFFFFFFF (100% белого) | #44FFFFFF (26% белого) |
textColorSecondaryInverse | #E8FFFFFF (91% белого) | #44FFFFFF (26% белого) |
textColorTertiaryInverse | #96FFFFFF (59% белого) | #44FFFFFF (26% белого) |
textColorHintInverse | #61FFFFFF (38% белого) | #19FFFFFF (10% белого) |
Для наглядного отображения текстовой информации AppCompat работает не только с цветами, но и с размером шрифта и его начертанием. Данный набор параметров называется TextAppearance.
Название | АЦвет текста | АРазмер шрифта | АГарнитура шрифта |
Caption | Secondary | 12sp | Regular |
Body1 | Primary | 14sp | Regular |
Body2 | Primary | 14sp | Medium |
Button | Primary | 14sp | Medium |
Subhead | Primary | 16sp | Regular |
Menu | Primary | 16sp | Medium |
Title | Primary | 20sp | Medium |
Headline | Primary | 24sp | Regular |
Display1 | Secondary | 34sp | Regular |
Display2 | Secondary | 45sp | Regular |
Display3 | Secondary | 56sp | Regular |
Display4 | Secondary | 112sp | Light |
Хоть в таблице это и не указано, но для Title и для Menu определены также инвертированные Text Appearance.
Наглядные примеры правильно сформированных Text Appearance можно увидеть в спецификации гайдлайнов Google Material Design. На основе этих стилей и информации из гайдлайнов разработчики совместно с дизайнерами создали следующее семейство Text Appearance:
Название | Цвет текста | Размер шрифта | Гарнитура шрифта |
Caption | Tertiary | 12sp | Regular |
Hint | Hint | 14sp | Regular |
Subheader | Tertiary | 14sp | Medium |
Button | Primary | 14sp | Medium |
Body0 | Tertiary | 14sp | Regular |
Body1 | Secondary | 14sp | Regular |
Body2 | Primary | 16sp | Regular |
Body3 | Secondary | 16sp | Medium |
Input1 | Primary | 20sp | Regular |
Input2 | Tertiary | 20sp | Regular |
Title | Primary | 20sp | Medium |
Headline | Primary | 24sp | Regular |
Display1 | Primary | 32sp | Regular |
Display2 | Tertiary | 32sp | Regular |
Из каждого TextAppearance, приведенного выше (за исключением Button), мы формируем Inverse, Primary и Accent-варианты (для цвета текста используются заданные ранее значения атрибутов textColor*Inverse, colorPrimary, colorAccent).
Когда дизайнеры передают нам макеты, то напротив текстовых полей (TextView) они указывают сокращенное имя «Title Default» соответствует TextAppearance.Material.Sbrf.Title, а «Subheader Inverse» соответствует TextAppearance.Material.Sbrf.Subheader.Inverse.
Стилизация EditText
С цветами и оформлением текста мы более-менее разобрались, и осталось ещё одно место, в котором надо всё привести в порядок: речь идёт о элементах EditText и TextInputLayout. Проблема в том, что «тема по умолчанию» выглядит неаккуратно, и её надо причесать по фен-шую. 🙂
Для невнимательных поясним:
- Линия EditText в несфокусированном и задисейбленном состоянии должна быть бледнее;
- Линия EditText в сфокусированном состоянии должна быть сплошной зелёной (colorPrimary), а не черной-оранжевой;
- Ошибка выделяется оранжевым цветом (colorAccent), а не красным;.
- Размер шрифта EditText-а должен быть крупнее.
Внутри библиотеки AppCompat мы обнаружили что при создании View из разметки AppCompatActivity подменяет определенные виджеты на их AppCompat-наследников. Делается это, судя по всему, для поддержки background tinting и подтягивания стилей из AppCompat-тем. В частности, EditText заменяется на AppCompatEditText. (Следовательно, если вам нужен кастомный виджет, то создавать подкласс нужно не непосредственно из EditText, а из AppCompatEditText).
Стиль AppCompatEditText-у задается в теме через атрибут editTextStyle. По умолчанию стиль — Widget.AppCompat.EditText, задающий определенный TextAppearance, фон и цвет текста (то есть, цвет из TextAppearance игнорируется).
Взглянем на разметку бэкграунда EditText-а для версии 5.0 (для старых версий background в целом похож, разница только в том, где происходит его подкрашивание (tinting) — в самой разметке, как на 5.x, или в конструкторе виджета на более ранних версиях).
<inset xmlns:android="http://schemas.android.com/apk/res/android" android:insetLeft="@dimen/edit_text_inset_horizontal_material" android:insetRight="@dimen/edit_text_inset_horizontal_material" android:insetTop="@dimen/edit_text_inset_top_material" android:insetBottom="@dimen/edit_text_inset_bottom_material"> <selector> <item android:state_enabled="false"> <nine-patch android:src="@drawable/textfield_default_mtrl_alpha" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" /> </item> <item android:state_pressed="false" android:state_focused="false"> <nine-patch android:src="@drawable/textfield_default_mtrl_alpha" android:tint="?attr/colorControlNormal" /> </item> <item> <nine-patch android:src="@drawable/textfield_activated_mtrl_alpha" android:tint="?attr/colorControlActivated" /> </item> </selector> </inset>
А вот сами ресурсы textfield_default_mtrl_alpha и textfield_activated_mtrl_alpha:
Как видно из разметки, тонкая линия подкрашивается цветом, являющимся значением атрибута colorControlNormal. Заменим этот цвет.
<color name="color_control_normal">#1A000000</color> <style name="Theme.Material.Sbrf" parent="@style/Theme.AppCompat.Light.NoActionBar"> <item name="colorControlNormal">@color/color_control_normal</item> </style>
За сфокусированное состояние (а также за цвет курсора и text-select-handle-ов) отвечает colorControlActivated — изменим и его, пусть он имеет то же значение, что и colorPrimary:
<style name="Theme.Material.Sbrf" parent="@style/Theme.AppCompat.Light.NoActionBar"> <item name="colorControlActivated">?attr/colorPrimary</item> </style>
Размер шрифта EditText-а — задаётся через TextAppearance.
<style name="TextAppearance.Material.Sbrf.EditText" parent="@style/TextAppearance.Material.Sbrf.Input1"> </style> <style name="Widget.Material.Sbrf.EditText" parent="@style/Widget.AppCompat.EditText"> <item name="android:textAppearance">@style/TextAppearance.Material.Sbrf.EditText</item> </style> <style name="Theme.Material.Sbrf" parent="@style/Theme.AppCompat.Light.NoActionBar"> <item name="editTextStyle">@style/Widget.Material.Sbrf.EditText</item> </style>
Результат:
Со стилизацией EditText покончено. Настало время TextInputLayout. По умолчанию он применяет к себе стиль Widget.Design.TextInputLayout, извлекая из него следующие атрибуты:
- hintTextAppearance — TextAppearance плавающего лейбла (на нашем скрине в нем написано слово «Подсказка») в сфокусированном состоянии;
- android:textColorHint — цвет плавающего лейбла в несфокусированном состоянии, цвет подсказки в EditText. Если значение атрибута у TextInputLayout не указано, то подтянется значение этого же атрибута у EditText;
- android:hint — собственно текст плавающего лейбла, перегружает подсказку самого EditText;
- hintAnimationEnabled — применять ли анимацию при «переходе» подсказки из EditText-а в плавающий лейбл.
- errorTextAppearance — TextAppearance лейбла с ошибкой. В данном случае подкрашивается линия EditText-а;
- errorEnabled — следует ли при отрисовке TextInputLayout заранее закладывать место под лейбл с ошибкой;
- counterTextAppearance — TextAppearance счетчика длины текста.
- counterEnabled — следует ли при отрисовке TextInputLayout показывать счетчик длины текста.
- counterMaxLength — максимально допустимая длина текста.
- counterOverflowTextAppearance — TextAppearance индикатора ошибки превышения длины текста.
Составим таблицу свойств TextAppearance вспомогательных TextView. Все они наследуются от TextAppearance.AppCompat.Caption (textColorSecondary, 12sp, regular), изменяется только цвет.
Название Цвет текста |
?attr/colorControlActivated |
TextAppearance.Design.Error | #FFDD2C00 |
TextAppearance.Design.Counter | ?attr/colorControlActivated |
TextAppearance.Design.Counter.Overflow | #FFDD2C00 |
Дальше всё просто: заменим цвет текста на colorAccent:
<style name="TextAppearance.Material.Sbrf.Error" parent="@style/TextAppearance.Material.Sbrf.Caption"> <item name="android:textColor">?attr/colorAccent</item> </style>
Применим этот TextAppearance в стиле TextInputLayout:
<style name="Widget.Material.Sbrf.TextInputLayout" parent="@style/Widget.Design.TextInputLayout"> <item name="errorTextAppearance">@style/TextAppearance.Material.Sbrf.Error</item> </style>
Применим свежесозданный стиль к TextInputLayout в разметке. Вот что у нас получилось:
Напоследок внесем важное улучшение. Обычно для каждого виджета существует атрибут в теме, по которому он в своем конструкторе может взять актуальный для данной темы стиль. TextInputLayout (как и другие виджеты библиотеки Design) такого атрибута в теме не имеет. Введем его на стороне приложения:
<declare-styleable name="Theme.Material.Sbrf"> <attr name="textInputLayoutStyle" format="reference"/> </declare-styleable>
Внедрим его в тему:
<style name="Theme.Material.Sbrf" parent="@style/Theme.AppCompat.Light.NoActionBar"> <item name="textInputLayoutStyle">@style/Widget.Material.Sbrf.TextInputLayout</item> </style>
Теперь мы можем по этому атрибуту подтягивать стиль виджета в разметке:
<android.support.design.widget.TextInputLayout style="?attr/textInputLayoutStyle" android:layout_width="match_parent" android:layout_height="wrap_content">
Стилизация AppBar-а
С текстовыми полями, шрифтами и цветами мы справились. Осталась ещё пара важных элементов, в которых надо навести порядок: AppBar и Toolbar. В типичном Activity при использовании Material-дизайна верхняя панель по стилю отличается от контента Activity: она отличается другим фоном, другим цветом текста. Google предполагает, что мы для этого будем пользоваться атрибутом actionBarTheme. С ним и будем работать.
Атрибут actionBarTheme ссылается на ThemeOverlay — своего рода мини-тему, применяемую к отдельному ViewGroup, его дочерним элементам, их дочерним элементам и так далее. При применении ThemeOverlay одноименные атрибуты темы Activity получают новое значение. Для большей наглядности рассмотрим 3 скриншота: на первом к верхнему бару и виджетам в нем не применяются ни стиль, ни ThemeOverlay, на втором применяются зелёный стиль и ThemeOverlay с белым текстом, на третьем — белый стиль и ThemeOverlay с зелёным текстом.
Цвет фона AppBarLayout. В ресурсах библиотеки Design указано, что Background этого виджета красится в первичный цвет приложения (colorPrimary). Создадим стили для AppBarLayout:
<style name="Widget.Material.Sbrf.AppBarLayout" parent="@style/Widget.Design.AppBarLayout"> </style> <style name="Widget.Material.Sbrf.AppBarLayout.Colored"> </style> <style name="Widget.Material.Sbrf.AppBarLayout.White"> <item name="android:background">?android:attr/colorForegroundInverse</item> </style>
По аналогии с TextInputLayout создадим в теме атрибут appBarLayoutStyle, чтобы по нему в разметке получать актуальный стиль виджета.
Стиль TabLayout. Решается аналогично задаче с AppBarLayout.
Цвет иконок в toolbar’е задается посредством атрибута colorControlNormal. Однако, здесь есть некоторые проблемы, которые надо решить. Цвет colorControlNorman мы использовали для линии EditText-а в несфокусированном состоянии (автоматом цвет также применился для цвета незаполненного прогресса в детерминированном ProgressBar и в SeekBar). ThemeOverlay с белым текстом переопределяет colorControlNormal как #FFFFFF (белый). ThemeOverlay с зелёным переопределяет colorControlNormal как ?attr/colorPrimary (у нас он, как вы помните, зелёный).
Определим ThemeOverlay.
<style name="Theme.Material.Sbrf" parent="@style/Theme.AppCompat.Light.NoActionBar"> <item name="colorControlNormal">@color/color_control_normal</item> <item name="appBarLayoutStyle">@style/Widget.Material.Sbrf.AppBarLayout</item> </style> <style name="Theme.Material.Sbrf.Colored"> <i></i> <item name="actionBarTheme">@style/ThemeOverlay.Material.Sbrf.ActionBar.Colored</item> <item name="appBarLayoutStyle">@style/Widget.Material.Sbrf.AppBarLayout.Colored</item> </style> <style name="Theme.Material.Sbrf.WhiteActionBar"> <i></i> <item name="actionBarTheme">@style/ThemeOverlay.Material.Sbrf.ActionBar.White</item> <item name="appBarLayoutStyle">@style/Widget.Material.Sbrf.AppBarLayout.White</item> </style> <style name="ThemeOverlay.Material.Sbrf" parent="@style/ThemeOverlay.AppCompat.Light"> <item name="colorControlNormal">@color/color_control_normal</item> </style> <style name="ThemeOverlay.Material.Sbrf.ActionBar"> <i></i> <item name="searchViewStyle">@style/Widget.AppCompat.SearchView.ActionBar</item> </style> <style name="ThemeOverlay.Material.Sbrf.ActionBar.Colored"> <i></i> <item name="colorControlNormal">?android:attr/textColorPrimaryInverse</item> </style> <style name="ThemeOverlay.Material.Sbrf.ActionBar.White"> <i></i> <item name="android:colorForeground">@color/color_foreground</item> <item name="android:colorForegroundInverse">@color/color_foreground_inverse</item> <item name="colorControlNormal">?attr/colorPrimary</item> </style>
И применим их в разметке:
<android.support.design.widget.AppBarLayout android:id="@+id/app_bar_layout" style="?attr/appBarLayoutStyle" android:layout_width="match_parent" android:layout_height="wrap_content" app:theme="?attr/actionBarTheme"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content"/> <i></i> <android.support.design.widget.TabLayout android:id="@+id/tab_layout" style="?attr/tabLayoutStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" android:paddingLeft="0dp" app:tabMode="fixed"/> </android.support.design.widget.AppBarLayout>
Отступ текста в toolbar’е от левого края. Google Material Guidelines предписывают его выставлять в 72dp, если есть кнопка Up, и 16dp, если её нет. Как вариант, мы можем в двух разных темах для таких Activity проставить два разных стиля toolbar’а с двумя разными отступами. Мы решили не менять весь стиль из-за одного незначительного параметра, а менять сам этот параметр.
Заведем новый атрибут в теме:
<<b>declare-styleable name="Theme.Material.Sbrf"</b>> <<b>attr name="toolbarContentInsetStart" format="dimension"</b>/> </<b>declare-styleable</b>>
Определим новый стиль toolbar’а:
<style name="Widget.Material.Sbrf.Toolbar" parent="@style/Widget.AppCompat.Toolbar"> <item name="contentInsetLeft">?attr/toolbarContentInsetStart</item> <item name="contentInsetStart">?attr/toolbarContentInsetStart</item> </style>
Определим новый атрибут в 2 темах, в родительской теме переопределим стиль toolbar’а.
<style name="Theme.Material.Sbrf" parent="@style/Theme.AppCompat.Light.NoActionBar"> <item name="toolbarContentInsetStart">72dp</item> <item name="toolbarStyle">@style/Widget.Material.Sbrf.Toolbar</item> </style> <style name="Theme.Material.Sbrf.Colored"> <i><!-- Здесь мы переопределим различные атрибуты, специфичные для данной темы --></i> </style> <style name="Theme.Material.Sbrf.Colored.IconlessList"> <i></i> <item name="toolbarContentInsetStart">16dp</item> </style>
Теперь, в зависимости от указанной темы, текст будет иметь различный отступ от левого края. То, что и требовалось сделать по гайдлайнам.
Цвет текста в toolbar’е. В стилях AppCompat по умолчанию задан цвет для текста: textColorPrimary. В самой теме textColorPrimary заведён как 90% черный. зелёный ThemeOverlay переопределяет textColorPrimary как 100% белый. Белый ThemeOverlay переопределяет его как ?attr/colorPrimary.
Ограничения ThemeOverlay. Не применяются к View фрагментов, лежащих внутри AppBarLayout.
Заключение
Приложение стало доступно в Google Play на днях, и самое время подвести итоги наших реформ.
Мы значительно сократили время, необходимое на вёрстку приложения: благодаря унифицированным TextAppearance, отступам и стилям создание новых экранов и редактирование старых происходит быстро и эффективно. Больше никаких бессмысленных трат времени на ручное измерение отступов, на проверку цветов в ColorPicker-е (оттуда еще поди достань альфа-канал для цвета): посмотрел на псевдонима ресурса в макете, выбрал соответствующий ресурс, работаешь дальше.
Программисты понимают, от чего отталкиваются и что подразумевают дизайнеры при решении UX-задач. Они учитывают это при стилизации виджета. Например, если дизайнер отрисовал зелёную кнопку на макете, то в приложении должен быть не безликий кирпич-заглушка, а кнопка, реагирующая на нажатие. Дизайнеры осознают, какой элемент для платформы Андроид родной, а какой — требуется сильно дорабатывать и сложно поддерживать.
Налаженное взаимопонимание между программистами и дизайнерами позволило выдавать результат быстрее и надежнее. Правки в верстку вносились по щелчку пальцев: чего стоит одна только замена зелёного аппбара на белый (достаточно добавить новый стиль AppBarLayout, новый ThemeOverlay, соединить их в одной теме и назначить её основной для всего проекта).
Естественно, некоторые идеи оказались неудачными. Мы сделали неверное предположение, что нам хватит 5 минимальных высот ячеек; на практике же оказалось, что дизайнеры при проектировании не используют такую характеристику, а отталкиваются непосредственно от контента. Система именований отступов оказалось неудачной и не наглядной.
Несмотря на это, основная масса принятых правил оказалась чрезвычайно эффективной на практике. С помощью новых инструментов мы создали гармоничное приложение в стиле Material. Приглашаем всех в Google Play, где можно на деле оценить результаты нашей работы.
Спасибо за внимание, в следующем выпуске мы расскажем про процесс создания интерфейса нашего приложения.
ссылка на оригинал статьи http://habrahabr.ru/post/273945/
Добавить комментарий