Pull to refresh

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

Reading time6 min
Views16K

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

Содержание


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

Введение


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

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

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

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

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

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




В ресурсах создадим новый стиль 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, будут выглядеть по-новому.



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


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

  • Стиль применяется при 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.

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().

Результат



Остальной код
<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.

Статья не претендует на полный справочник. Код умышленно сокращен. Я ставил задачу дать общее понимание — как это работает и зачем это нужно. Дальше все легко ищется в документации и в стандартных ресурсах.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 14: ↑14 and ↓0+14
Comments1

Articles