Как стать автором
Обновить

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

70 разных цветов — это точно хороший дизайн?
Статья не об этом ведь
Под «70 цветов» подразумевается количество констант в colors.xml. Самих цветов обычно 15-20. Например, для текстовых полей используется 7 констант:
  • 2 для фона (градиент);
  • 3 для рамки (есть фокус, нет фокуса, введено неверное значение);
  • 2 для текста (обычный и подсказки).

Но цвет текста обычно соответствует цвету заголовков, цвет рамки в фокусе – основному цвету темы, фон – белый без градиента.

Это сделано для гибкой настройки. Так как один из заказчиков может захотеть выделить кнопки в диалоге отличным от основной темы цветом. Другой – увидеть градиент в Action Bar, но только там, чтобы остальные элементы были без.
Спасибо за пояснение. Я бы, наверное, в таком случае генерировал этот XML c 70-ю цветами из базовых 5-10 цветов, чтобы и поменять нюансы можно было, и поменять всю палитру на другой базовый цвет было попроще.
Так и делалось. Есть скрипт, который парсит данные, предоставленные заказчиком, и создает нужные xml (там не только цвета).
Но за последнее время были случаи, когда заказчик хотел изменить, например, цвет заголовков в списках или логотип в ActoinBar. Ради этих изменений приходится перевыпускать приложение. Для Android это не особо критично, а вот с iOS беда.
Отсюда всё и пошло. Хотя пока этот функционал ещё не внедрен и мне не особо хочется нагружать им приложение.
Насколько мне известно, нормальная практика смены темы — вынос всех ресурсов в стили для последующего переключения между ними
Обычно да.
Если в рамках одного приложения, то через стили.
Если твое приложение имеет несколько вариаций (код один, но приложения разные, собраны для разных заказчиков, различаются содержанием и цветовой схемой), то можно использовать productFlavors.

Но в данном случае суть в том, что заказчик может изменить цветовую схему на сервере (вдруг решил, что черный шрифт — плохо, хочет темно-синий) и она поменяется у тебя в приложении без перевыпуска его в Market
Решал похожую задачу просто перекрашиваем вью активити на уровне java кода. Shape drawable можно перекрашивать после создания. Был интерфейс IAppConfiguration, поставляющий все цвета, иконки и размеры. А все экраны перекрашивали свои вью используя его. Получалось достаточно гибки. Мне кажется, что это меньший велосипед, чем пытаться переопределить цвета в resources, ведь ресурсы — для загрузки ресурсов с учетом текущей конфигурации.

Еще это удобно если конфиг нужно получать в текущей сессии: к примеру при из firebase или longpolling с сервера. Все экраны, подписанные на изменения конфига, просто вызывают функцию, ответственную за перекраску, без пересоздания всего вью из ресурсов.

Если писать на dls вроде anko, так вообще будет выгладить однородно.
По сути, сейчас сделано похожим образом. Просто перекраска происходит внутри LayoutInflater, в нем есть доступ к создаваемым View и их аттрибутами из xml. Как следствие, внутри самих Activity надо дописать только обертку надо контекстом.

С shape drawable были проблемы в плане получения id цветов для них, так как сам shape их не хранит. Была идея, что каждый id будет ссылаться на уникальный цвет, а потом программно изменять его на нужный. Но в итоге для перекраски shape тянется его xml и id цветов берётся оттуда.

От велосипеда в Resources я отказался поностью.

Касаемо второго вашего способа. Спасибо. Я отстал от жизни и databinding не использовал вообще. Метод гибкий и его можно поставить в альтернативу LayoutInflater. На этой неделе посмотрю.
Так-же можете попробовать использовать databinding.
В том-же xml вы передаете объект со всеми стилями, необходимыми для текущего экрана. На уровне java для каждого экрана вы пишите маппинг из конфига в локальные стили. И в итоге при изменениях конфига будет пересоздаваться объект локальных стилей и сетиться с помощью databinding во вью. Почти react-redux.

В xml можете дефолтные значения задавать, которые будут ссылаться уже на статичные ресурсы. Кода чуть больше, но это еще более гибко и более android-way (учитывая движение в сторону mvvm в android architecture components)
Делаю у себя в проекте именно так как вы описали (включая дефолтные значения), работает прекрасно. Используются дополнительные адаптеры, но в сумме выглядит, на мой взгляд, аккуратно и без лишней неявности. В xml задается примерно так (Color — enum c доступными для изменения цветами, colorScheme — map со значениями цветов):
Пример
<!--suppress AndroidUnknownAttribute -->
<data class="SomeFragmentBinding">
    <import type="package.Color"/>
    <variable name="colorScheme" type="package.ColorScheme"/>
</data>

<FrameLayout
    ...
    android:background="@{colorScheme[Color.CONTENT_BACKGROUND]}">

    <GridView
        ...
        app:colorScheme="@{colorScheme}"/>

    <TextView
        ...
        android:textColor="@{colorScheme[Color.DEFAULT_TEXT]}"/>

</FrameLayout>

Собирался задействовать такой способ, но у него оказался один минус. Databinding работает только в рамках layout, в стилях его не применить. Например, в приложении более 100 textview, размер, тип и цвет стандартизирован и вынесен в стили. Чтобы задействовать databinding, надо выносить цвет из стилей.

Оставил эту идею, как резервную, на случай, если текущая реализация подведет.
Это может звучать как перебор, но можно использовать include и передавать изменяемые параметры через те же databinding. Хотя стили для ViewGroup так не задать, конечно.
Я рассматривал аналог стилей, реализованный на databinding. Т.е. делаем свой адаптер и внутри прописываем нужные параметры. Но решил, что пока это перебор.
Да, я об этом думал, но пока идей, как сделать это аккуратно, не было.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории