Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Обновлено: 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.

Чтобы изменить фон на собственный, используйте атрибут app:backgroundTint и app:backgroundTintMode="multiply". Ни в коем случае не используйте атрибут android:background. Если вы это сделаете по ошибке, внешний вид кнопок изменится с потерей стилей (либо программа закроется с ошибкой).
Программно можно изменить фон следующим образом (один из вариантов).
button.backgroundTintList = ColorStateList.valueOf(Color.YELLOW)
button.backgroundTintMode = PorterDuff.Mode.SRC_ATOP
Атрибут app:icon добавляет значок к надписи на кнопке.
Другие полезные атрибуты.
Далее будут идти примеры со старыми стилями из семейства 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"
... />
Попробуйте другие стили:
Вдобавок к общему атрибуту 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" />
В Material 3 названия поменялись. Попробуйте, например, стиль app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Material3.Corner.Bottom".

Кнопки можно сгруппировать с помощью 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"):
Можно выбрать кнопку заранее через 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>