Темы и стили в Android без магии. И как их готовить с SwitchCompat

в 17:01, , рубрики: android development, android theme, Разработка под android

Темы и стили в Android без магии. И как их готовить с SwitchCompat - 1
В предыдущей статье мы рассмотрели как использовать темы и стили на уровне кода, на примере кастомной view. В этой статье давайте разберем несколько способов стилизации стандартного ui элемента, а в частности SwitchCompat.

Содержание

Введение
Новый стиль для switchStyle
Стиль в верстке
Стиль в теме. Тема назначается через Manifest.
Стиль в теме. Тема назначается программно.
Другие View

Введение

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

Для решения задачи нам нужно:

  • Создать свой стиль для SwitchCompat.
  • Каким-то образом задать этот стиль SwitchCompat.

Назначить стиль SwitchCompat можно несколькими способами, например:

  • Указывать для каждой view в верстке экранов через атрибут style.
  • Создать тему с переопределенным атрибутом switchStyle и назначить эту тему в манифесте для всего приложения или конкретной активити. Это изменит внешний вид view для всего приложения/активити.
  • Тему также можно установить программно, в коде активити. При необходимости ее можно менять «на лету».

Новый стиль для SwitchCompat

Темы и стили в Android без магии. И как их готовить с SwitchCompat - 2
В ресурсах создадим новый стиль MySwitchStyle, наследуем оформление от Widget.AppCompat.CompoundButton.Switch, задав parent. Можно и не наследовать, но тогда придется указать все значения, даже которые мы не планируем менять.

<style name="MySwitchStyle" parent = "Widget.AppCompat.CompoundButton.Switch">
</style>

Чтобы что-то изменить, надо переопределить требуемые атрибуты. Атрибуты можно посмотреть в документации.
В документации видим несколько атрибутов. Они указаны в виде, как если бы мы обращались к ним в коде (например, вот так R.styleable.SwitchCompat_android_thumb). Я расшифрую только часть из них, чтобы не было сомнений. Назначение остальных несложно понять из документации.

В коде В xml
SwitchCompat_android_thumb android:thumb
SwitchCompat_thumbTint thumbTint
SwitchCompat_track track
SwitchCompat_trackTint trackTint

  • android:thumb — ресурс для подвижной части SwitchCompat
  • track — ресурс для неподвижной части SwitchCompat
  • thumbTint — позволяет окрашивать подвижную часть в нужные цвета в зависимости от состояния SwitchCompat
  • trackTint — позволяет окрашивать неподвижную часть в нужные цвета в зависимости от состояния SwitchCompat

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

Нам понадобится селектор в папке color наших ресурсов. Файл selector_switch_thumb.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" 
              android:color = "@android:color/holo_orange_dark"/>
    <item android:color="@android:color/holo_green_light"/>
</selector>

Теперь зададим атрибут thumbTint в нашем стиле.

<style name="MySwitchStyle" parent = "Widget.AppCompat.CompoundButton.Switch">
        <item name="thumbTint">@color/selector_switch_thumb</item>
</style>

Теперь все SwitchCompat, получившие каким-то образом стиль MySwitchStyle, будут выглядеть по-новому.
Темы и стили в Android без магии. И как их готовить с SwitchCompat - 3

Стиль в верстке

Самый тривиальный и негибкий способ.

  • Стиль применяется при inflate ресурса layout.
  • Повлиять программно мы никак не можем.
  • Указывать каждый раз в верстке неудобно. И можем забыть.

<androidx.appcompat.widget.SwitchCompat
            android:text="Themed switch"
            style="@style/MySwitchStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

Стиль в теме. Тема назначается через Manifest.

Создаем тему AppTheme и задаем значение атрибуту switchStyle. Значением является наш стиль MySwitchStyle.

<resources>
    <style name="CustomTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <!-- Задаем значение атрибуту switchStyle -->
        <item name="switchStyle">@style/MySwitchStyle</item>
    </style>
</resources>

Тема может быть указана в манифесте для всего приложения

<application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/CustomTheme"><!-- Тема для всего приложения -->
</application>

Или для конкретной активити

<activity 
           android:name=".MainActivity"
           android:theme="@style/CustomTheme"><!-- Тема для активити -->
</activity>

Теперь все SwitchCompat будут иметь новый внешний вид. Без изменения в верстке.

  • Плюсы — Можем менять внешний вид для всего приложения сразу.
  • Минусы — налету менять не получится.

Стиль в теме. Тема назначается программно.

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

Давайте менять тему активити в зависимости от состояния Switch.

class MainActivity : AppCompatActivity() {

private const val KEY_CUSTOM_THEME_CHECKED = "KEY_CUSTOM_THEME_CHECKED"

class MainActivity : AppCompatActivity() {

    private val preference by lazy {
        PreferenceManager.getDefaultSharedPreferences(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        val isCustomThemeChecked = preference.getBoolean(
            KEY_CUSTOM_THEME_CHECKED,
            true
        )

        if (isCustomThemeChecked) {
            setTheme(R.style.CustomTheme)
        } else {
            setTheme(R.style.StandardTheme)
        }
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)
        customThemeCheckbox.isChecked = isCustomThemeChecked
        customThemeCheckbox.setOnCheckedChangeListener { _, isChecked ->
            preference.edit()
                .putBoolean(KEY_CUSTOM_THEME_CHECKED, isChecked)
                .apply()
            recreate()
        }
    }
}

  1. Устанавливаем тему программно, вызвав setTheme. Метод надо вызывать до super.onCreate(savedInstanceState). В onCreate у нас происходит инициализация фрагментов (когда они есть).
  2. Задаем начальное состояние Switch в зависимости от темы.
  3. Устанавливаем листенер, который при изменении Switch меняет тему в настройках и перезапускает активити через метод активити recreate().

Результат
Темы и стили в Android без магии. И как их готовить с SwitchCompat - 4

Остальной код

<resources>
    <style name="CustomTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="switchStyle">@style/MySwitchStyle</item>
    </style>

    <style name="StandardTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="MySwitchStyle" parent="Widget.AppCompat.CompoundButton.Switch">
        <item name="thumbTint">@color/selector_switch_thumb</item>
    </style>
</resources>

<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp"
        android:orientation="vertical">

    <CheckBox
            android:text="CustomTheme"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"         
            android:saveEnabled="false"
            android:id="@+id/customThemeCheckbox"/>

    <androidx.appcompat.widget.SwitchCompat
            android:text="Themed switch"
            android:layout_marginTop="56dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/themedSwitch"/>
</LinearLayout>

Другие View

Чтобы переопределить стиль для SwitсhView для всего приложения, мы переопределили значение атрибута switchStyle, можно догадаться, что такие атрибуты есть и для других View.
Например:

  • editTextStyle
  • checkboxStyle
  • radioButtonStyle

Как их искать? Я просто смотрю исходники, через Android Studio.
Заходим в тему, зажимаем ctrl, кликаем на родителе нашей темы. Смотрим, как описывают тему ребята из Google. Смотрим, какой атрибут определяется и от какого стиля можно отнаследоваться. Пользуемся.

Кусок из темы Base.V7.Theme.AppCompat.Light.

<item name="editTextStyle">@style/Widget.AppCompat.EditText</item>
<item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
<item name="radioButtonStyle">
        @style/Widget.AppCompat.CompoundButton.RadioButton
</item>
<item name="buttonStyle">@style/Widget.AppCompat.Button</item>

Ресурсы

developer.android.com/guide/topics/ui/look-and-feel/themes

developer.android.com/reference/android/support/v7/widget/SwitchCompat.html#xml-attributes

P.S.
Статья не претендует на полный справочник. Код умышленно сокращен. Я ставил задачу дать общее понимание — как это работает и зачем это нужно. Дальше все легко ищется в документации и в стандартных ресурсах.

Автор: Дмитрий Берендеев

Источник

* - обязательные к заполнению поля