Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

MaterialButton

Обновлено: 01.02.2020, 7 мая 2024

Цвет для недоступной кнопки
Другие формы
MaterialButtonToggleGroup

В AndroidX есть новый компонент, который может заменить стандартную кнопку.

Добавим зависимости (обычно они уже есть).


implementation(libs.androidx.appcompat)
implementation(libs.material)

Компонент MaterialButton наследуется от AppCompatButton и поддерживает тему Material без добавления стилей. Поэтому для примера следует указать, что приложение использует тему из семейства Theme.MaterialComponents. Я использовал следующий вариант в styles.xml.


<style name="AppTheme" parent="Theme.MaterialComponents.Light">
    <!-- Customize your theme here. -->
    ...
</style>

Сейчас студия использует Material3, поэтому выбираем семейство Theme.Material3.DayNight.NoActionBar и ему подобные.

Настало время добавить кнопку на экран.


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.button.MaterialButton
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="MaterialButton"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="Стандартная кнопка"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />

</androidx.constraintlayout.widget.ConstraintLayout>

Визуально разницы между кнопками нет, хотя формально это объекты разных классов. На данный момент используются кнопки с круглыми углами, в Material 2 внешний вид был немного другой. MaterialButton поддерживает стили Material Design без необходимости устанавливать отдельные стили. Сейчас фон заливается стилем colorPrimary.

MaterialButton

Чтобы изменить фон на собственный, используйте атрибут app:backgroundTint и app:backgroundTintMode="multiply". Ни в коем случае не используйте атрибут android:background. Если вы это сделаете по ошибке, внешний вид кнопок изменится с потерей стилей (либо программа закроется с ошибкой).

Программно можно изменить фон следующим образом (один из вариантов).


button.backgroundTintList = ColorStateList.valueOf(Color.YELLOW)
button.backgroundTintMode = PorterDuff.Mode.SRC_ATOP

Атрибут app:icon добавляет значок к надписи на кнопке.

Другие полезные атрибуты.

  • app:iconTint - цвет значка
  • app:iconPadding - отступы для значка
  • app:iconSize - размер значка
  • app:additionalPaddingLeftForIcon - левый отступ значка от начала кнопки
  • app:additionalPaddingRightForIcon - правый отступ
  • app:rippleColor - цвет для ripple-эффекта при нажатии
  • app:strokeColor - цвет обводки
  • app:strokeWidth - ширина обводки
  • app:cornerRadius - радиус закругления углов кнопки (программно button.cornerRadius = 40)
  • app:iconGravity - местоположение значка (ближе к левому или правому краю кнопки, рядом с текстом, ICON_GRAVITY_START, ICON_GRAVITY_TEXT_START и др.)

Далее будут идти примеры со старыми стилями из семейства MaterialComponents, меняйте на новый Material3 самостоятельно. Например, Widget.MaterialComponents.Button.OutlinedButton нужно заменить на Widget.Material3.Button.OutlinedButton.

Можете использовать стиль style="@style/Widget.MaterialComponents.Button.OutlinedButton", чтобы получить другой внешний вид кнопки. Можете добавить атрибуты strokeWidth и strokeColor, если хотите подчеркнуть обводку у кнопки.


<com.google.android.material.button.MaterialButton
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="OutlinedButton"
    app:icon="@drawable/ic_android_black_24dp"
    style="@style/Widget.MaterialComponents.Button.OutlinedButton"
    ... />
MaterialButton

Попробуйте другие стили:

  • style="@style/Widget.MaterialComponents.Button.TextButton"
  • style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
  • style="@style/Widget.MaterialComponents.Button.Icon"
  • style="@style/Widget.MaterialComponents.Button.TextButton.Icon"
  • style="@style/Widget.MaterialComponents.Button.Icon"
  • style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"

Вдобавок к общему атрибуту cornerRadius можно создать стиль и указать собственный радиус для каждого угла.


<style name="MyButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
    <item name="shapeAppearance">@style/MyShapeAppearance</item>
</style>
<style name="MyShapeAppearance">
    <item name="cornerFamilyTopLeft">rounded</item>
    <item name="cornerFamilyBottomLeft">rounded</item>
    <item name="cornerFamilyTopRight">cut</item>
    <item name="cornerFamilyBottomRight">cut</item>
    <item name="cornerSize">8dp</item>
</style>

Цвет для недоступной кнопки

Если вы делаете кнопку недоступной, то система сама выберет необходимые цвета для этого состояния. Но если вы хотите видеть собственный цвет, то нужно немного постараться.

Сначала создадим файл ресурса material_button_state.xml в папке res/colors.


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#D3212D" android:state_enabled="true" />
    <item android:color="#D3D3D3" android:state_enabled="false" />
</selector>

Применяем созданный ресурс к кнопке и делаем её недоступной.


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    val states = arrayOf(
        intArrayOf(android.R.attr.state_enabled), // enabled
        intArrayOf(-android.R.attr.state_enabled) // disabled
    )
    val colors = intArrayOf(
        Color.parseColor("#545AA7"), // enabled color
        Color.parseColor("#E6E6FA") // disabled color
    )
    val colorStates = ColorStateList(states, colors)

    button.backgroundTintList = colorStates
    button.isEnabled = false
}

Другие формы

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


<com.google.android.material.button.MaterialButton
    ...
    app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Cut" />
Cut MaterialButton

В Material 3 названия поменялись. Попробуйте, например, стиль app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Material3.Corner.Bottom".

MaterialButton

MaterialButtonToggleGroup

Кнопки можно сгруппировать с помощью MaterialButtonToggleGroup.


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.button.MaterialButtonToggleGroup
        android:id="@+id/toggleGroup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.button.MaterialButton
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button 1" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button 2" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button 3" />
    </com.google.android.material.button.MaterialButtonToggleGroup>

</androidx.constraintlayout.widget.ConstraintLayout>

В этом случае скругление происходит только у крайних кнопок с их левой или правой стороны.

Все дочерние кнопки в группе становятся выбираемыми, как переключатели, как будто им присвоили атрибут android:checkable="true". Вы можете выбрать режим переключения - можно выбрать только одну кнопку или несколько (app:singleSelection). Лучше использовать стиль OutlinedButton, чтобы видеть выбранную кнопку.


<com.google.android.material.button.MaterialButtonToggleGroup
    ...
    app:selectionRequired="true"
    app:singleSelection="true">

    <com.google.android.material.button.MaterialButton
        android:id="@+id/button1"
        style="@style/Widget.MaterialComponents.Button.OutlinedButton"
        ... />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/button2"
        style="@style/Widget.MaterialComponents.Button.OutlinedButton"
        ... />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/button3"
        style="@style/Widget.MaterialComponents.Button.OutlinedButton"
        ... />
</com.google.android.material.button.MaterialButtonToggleGroup>

На рисунке представлен вариант выбора двух кнопок (app:singleSelection="false"):

MaterialButtonToggleGroup

Можно выбрать кнопку заранее через XML через атрибут app:checkedButton с указанием идентификатора кнопки:


<com.google.android.material.button.MaterialButtonToggleGroup
    ...
    app:selectionRequired="true"
    app:checkedButton="@id/button1"
    app:singleSelection="false">

Все эти операции можно проделать программно.


val checkedId = toggleGroup.checkedButtonId // Will return View.NO_ID if singleSelection = false
val checkedIds = toggleGroup.checkedButtonIds // Potentially an empty list
toggleGroup.check(R.id.button1) // Checks a specific button
toggleGroup.uncheck(R.id.button1) // Unchecks a specific button
toggleGroup.clearChecked() // Unchecks all buttons

Можно повесить слушатель на изменение состояния кнопки. Не забывайте удалять слушатели через методы removeListener() или clearListeners.


toggleGroup.addOnButtonCheckedListener { group, checkedId, isChecked ->
    // Do something for checked change
    if(checkedId == R.id.button2 && isChecked){
        println("Button 2 is checked")
    }
}

MaterialButtonToggleGroup происходит от LinearLayout, поэтому мы можем поменять ориентацию у группы кнопок с горизонтальной на вертикальную. Но при этому между кнопками образуются отступы. Чтобы их избежать, каждой кнопке добавьте дополнительные атрибуты.


<com.google.android.material.button.MaterialButtonToggleGroup
    ...
    android:orientation="vertical"
    ...
    app:singleSelection="false">

    <com.google.android.material.button.MaterialButton
        ...
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:minHeight="36dp"
        android:text="Button 1" />

    ...
</com.google.android.material.button.MaterialButtonToggleGroup>
MaterialButton
Реклама