Влияние материала на кнопку с цветом фона

246

Я использую библиотеку поддержки Android v21.

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

 <Button
 style="?android:attr/buttonStyleSmall"
 android:background="?attr/colorPrimary"
 android:textColor="@color/white"
 android:textAllCaps="true"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Button1"
 />

Задний план Ниже приводится нормальная кнопка, и эффекты работают просто отлично.

<Button
 style="?android:attr/buttonStyleSmall"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:textAllCaps="true"
 android:text="Button1"
/>

Кнопка по умолчанию

Sathesh
источник
9
Это происходит потому, что вы заменяете фон со всеми хорошими эффектами материала сплошным цветом. Что касается правильного цвета кнопки при сохранении эффектов материала, никто на SO пока не смог этого понять.
Натан Уолтерс
2
@SweetWisher При изменении цвета фона пульсация и другие эффекты не применяются в кнопке.
Sathesh
1
@NathanWalters Но серый цвет по умолчанию уродлив. :-(
Sathesh
6
Поверьте мне, я знаю ... Мы будем в большом долгу перед тем, кто выяснит, как это сделать правильно.
Натан Уолтерс
1
Удивительный ответ stackoverflow.com/a/32238489/1318946
Пратик Бутани

Ответы:

431

Когда вы используете android:background, вы заменяете большую часть стиля и внешнего вида кнопки пустым цветом.

Обновление: По состоянию на выпуске версии 23.0.0 от AppCompat , есть новый Widget.AppCompat.Button.Coloredстиль , который использует вашу тему colorButtonNormalдля инвалидов цвета и colorAccentдля задействованного цвета.

Это позволяет применить его к вашей кнопке напрямую через

<Button
  ...
  style="@style/Widget.AppCompat.Button.Colored" />

Если вам нужен обычай colorButtonNormalили colorAccent, вы можете использовать , ThemeOverlayкак описано в этом про-наконечнике и android:themeна кнопку.

Предыдущий ответ

Вы можете использовать drawable в вашем каталоге v21 для вашего фона, таких как:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?attr/colorControlHighlight">
    <item android:drawable="?attr/colorPrimary"/>
</ripple>

Это гарантирует, что цвет вашего фона будет ?attr/colorPrimaryи будет иметь анимацию пульсации по умолчанию с использованием ?attr/colorControlHighlightнастроек по умолчанию (которые вы также можете установить в своей теме, если хотите).

Примечание: вам придется создать пользовательский селектор для менее чем v21:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/primaryPressed" android:state_pressed="true"/>
    <item android:drawable="@color/primaryFocused" android:state_focused="true"/>
    <item android:drawable="@color/primary"/>
</selector>

Предполагая, что у вас есть несколько цветов, которые вы хотели бы использовать для состояния по умолчанию, нажатия и фокусировки. Лично я снял скриншот пульсации на полпути, будучи выбранным, и вытащил из этого основное / сфокусированное состояние.

ianhanniballake
источник
13
Хороший ответ! Именно то, что я искал, однако необходимо было очистить и восстановить, прежде чем проект решил определить drawable в drawable-v21.
user1354603
4
@ianhanniballake Интересно, как совместить эту технику работы, не теряя настройки закругленных углов в этих
кнопках
3
@ianhanniballake просто комментарий, который можно рисовать в ряду в <item android: drawable ... />, не может быть прозрачным цветом. В этом случае пульсация не будет показана. Мне потребовалось время, чтобы понять это.
Иван Кушт
2
При использовании appcompat 23.1.1 и API 22 отключенное и включенное состояние принимают цвет colorButtonNormal темы.
Реми ДЭВИД
93

Есть еще одно простое решение - создать собственный фон для «плоских» кнопок, сохраняя их «материальные» эффекты.

  1. Поместите свою кнопку в ViewGroup с желаемым фоном, установленным там
  2. установить selectableItemBackground из текущей темы в качестве фона для вашей кнопки (API> = 11)

т.е.

<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/blue">
    <Button
        style="?android:attr/buttonStyleSmall"
        android:background="?android:attr/selectableItemBackground"
        android:textColor="@android:color/white"
        android:textAllCaps="true"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Button1"
        />
</FrameLayout>

Может использоваться для плоских кнопок, он работает с API> = 11, и вы получите эффект пульсации на> = 21 устройствах, сохраняя обычные кнопки до 21, пока AppCompat не будет обновлен для поддержки пульсации там.

Вы также можете использовать selectableItemBackgroundBorderless только для кнопок> = 21.

kiruwka
источник
Вы буквально задумались «из коробки» :-) Хотя это один из способов сделать это, это может увеличить связывание кода и использование стилей было бы рекомендуемым способом.
Sathesh
3
Это создает плоскую кнопку с цветным фоном, да. Но созданная кнопка выглядит как квадрат, без закругленных углов и теневых эффектов.
Санкет Берде
7
@SanketBerde Точно. Потому что это то, что "плоская" кнопка.
kiruwka
2
Оригинальный вопрос не упомянул квартиру. Кстати, если у вас есть родительский элемент <CardView> вместо <FrameLayout>, результирующая кнопка будет поднята и будет иметь тени. Это даже работает в версиях перед леденцом на палочке.
Санкет Берде
1
@SanketBerde приятно знать о CardViewподходе, спасибо, что поделились!
kiruwka
88

Вот простой и обратно совместимый способ создания эффекта ряби для рельефных кнопок с пользовательским фоном.

Ваш макет должен выглядеть так

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/my_custom_background"
    android:foreground="?android:attr/selectableItemBackground"/>
Bolein95
источник
9
Это самый простой способ сделать это. Хороший ответ.
Тайлер Пфафф
1
API 21+, но не работает. Применяется к линейному макету с clickable-true, фон - другой цвет, указывается передний план.
Алекс
3
Так как Button - это TextView, а не FrameLayout, foregroundон не действует до 23. google.com/…
androidguy
2
ОТЛИЧНО! Я даже не знал, что "передний план" был чем-то особенным!
Эрик
2
Работает как шарм ..!
Каннан
25

Я столкнулся с этой проблемой сегодня, фактически играя с библиотекой v22.

Предполагая, что вы используете стили, вы можете установить colorButtonNormalсвойство, и кнопки будут использовать этот цвет по умолчанию.

<style name="AppTheme" parent="BaseTheme">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/primaryColor</item>
    <item name="colorPrimaryDark">@color/primaryColorDark</item>
    <item name="colorAccent">@color/accentColor</item>
    <item name="colorButtonNormal">@color/primaryColor</item>
</style>

Помимо этого, вы все равно можете создать стиль для кнопки, а затем использовать ее для каждой кнопки, если вам нужен ассортимент цветов (не проверял, просто размышлял).

Не забудьте добавить android:перед именами элементов в вашем стиле v21 .

Роберт Роуз
источник
Ассортимент цветных кнопок см. В ответе другого Роберта: stackoverflow.com/a/31825439/1617737
ban-
Теперь, когда цвет фона установлен, есть ли общий атрибут, который я могу использовать в приведенном выше блоке кода, чтобы указать цвет текста кнопки?
запрет геоинженерии
24

С AppCompat (22.1.1+) вы можете добавить такой стиль:

<style name="MyGreenButton">
    <item name="colorButtonNormal">#009900</item>
</style>

И используйте его, просто применив стиль:

<android.support.v7.widget.AppCompatButton
    style="@style/MyGreenButton"
    android:layout_width="match_width"
    android:layout_height="wrap_content"
    android:text="A Green Button"
    />

Программно меняя цвет, я обнаружил, что единственный способ обновить цвет (в API 15 или 16) - это использовать «список оттенков фона». И это не удаляет хорошую радиальную анимацию на устройствах API 21:

ColorStateList colorStateList = new ColorStateList(new int[][] {{0}}, new int[] {0xFF009900}); // 0xAARRGGBB
button.setSupportBackgroundTintList(colorStateList);

Потому button.setBackground(...)и button.getBackground().mutate().setColorFilter(...)не меняйте цвет кнопки на API 15 и 16, как на API 21.

Роберт
источник
1
Большое спасибо, это отлично сработало для меня (даже если использовать Button в xml-макетах, так как AppCompat заменяет его на AppCompatButton)
Louis CAD
1
Используйте ColorStateList.valueOf( ... )для создания простого ColorStateListтолько одного цвета.
Тайг
colorAccent и rippleColor не работают в стиле
Яр
Спасибо тебе за этот пост. Что сработало для меня: ViewCompat.setBackgroundTintList(myButton, ColorStateList.valueOf(myColor); Javadoc для setSupportBackgroundTintListговорит, что это должно быть названо так.
JerabekJakub
22

Ответ @ ianhanniballake абсолютно правильный и простой. Но мне потребовалось несколько дней, чтобы понять. Для тех, кто не понимает его ответ, здесь более подробно реализация

<Button
        android:id="@+id/btn"
        style="@style/MaterialButton"
        ... />


<style name="MaterialButton" parent="Widget.AppCompat.Button.Colored">
    <item name="android:theme">@style/Theme.MaterialButton</item>
   ...
</style>


<style name="Theme.MaterialButton" parent="YourTheme">
    <item name="colorAccent">@color/yourAccentColor</item>
    <item name="colorButtonNormal">@color/yourButtonNormalColor</item>
</style>

=== Или ===

<Button
        android:id="@+id/btn"
        style="@style/Widget.AppCompat.Button.Colored"
        android:theme="@style/Theme.MaterialButton" />

<style name="Theme.MaterialButton" parent="YourTheme">
    <item name="colorAccent">@color/yourAccentColor</item>
    <item name="colorButtonNormal">@color/yourButtonNormalColor</item>
</style>
Фрэнк Нгуен
источник
5
Ваш ответ был для меня последней частью головоломки. Также можно написать colorButtonNormalв базовой теме @style/AppTheme. Это дает AppCompat цвет по умолчанию для соответствующей раскраски кнопок. И тогда можете использовать свой ответ на тему представления индивидуально.
Сиддхарт Пант
1
Это android:theme="Theme.MaterialButton"илиandroid:theme="@style/Theme.MaterialButton"
osrl
1
@osrl Это android:theme="@style/Theme.MaterialButton". Спасибо за вашу правильность.
Франк Нгуен,
12

Я использовал backgroundTint и передний план:

<Button
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:backgroundTint="@color/colorAccent"
    android:foreground="?android:attr/selectableItemBackground"
    android:textColor="@android:color/white"
    android:textSize="10sp"/>
SpyZip
источник
afaik нет необходимости в атрибуте переднего плана, так как backgroundTint не перекрывает стандартную рябь
Джулиан Ос
7

Я пришел к этому сообщению в поисках способа получить цвет фона для моего ListViewПредмета, но при этом сохранить пульсацию.

Я просто добавил свой цвет в качестве фона и selectableItemBackground в качестве переднего плана:

<style name="my_list_item">
    <item name="android:background">@color/white</item>
    <item name="android:foreground">?attr/selectableItemBackground</item>
    <item name="android:layout_height">@dimen/my_list_item_height</item>
</style>

и это работает как шарм. Я предполагаю, что та же самая техника могла бы использоваться и для кнопок. Удачи :)

Йоаким
источник
6

Если вы заинтересованы в ImageButton, вы можете попробовать эту простую вещь:

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@android:drawable/ic_button"
    android:background="?attr/selectableItemBackgroundBorderless"
/>
Амир Халифулла
источник
5

v22.1из appcompat-v7представил некоторые новые возможности. Теперь можно назначить конкретную тему только одному представлению.

Устаревшее использование app: тема для оформления панели инструментов. Теперь вы можете использовать android: theme для панелей инструментов на всех устройствах API уровня 7 и выше, а также android: theme theme для всех виджетов на устройствах API уровня 11 и выше.

Таким образом, вместо установки желаемого цвета в глобальной теме, мы создаем новую и назначаем ее только Button

Пример:

<style name="MyColorButton" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorButtonNormal">@color/myColor</item>
</style>

И используйте этот стиль как тему вашего Button

<Button
 style="?android:attr/buttonStyleSmall"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Button1"
 android:theme="@style/MyColorButton"/>
Бхаргав Благодар
источник
4

Если вы в порядке с использованием сторонней библиотеки, посмотрите traex / RippleEffect . Это позволяет вам добавить эффект Ripple к ЛЮБОМ представлению с помощью всего лишь нескольких строк кода. Вам просто нужно обернуть в свой файл макета XML элемент, который вы хотите иметь эффект ряби сcom.andexert.library.RippleView контейнером.

В качестве дополнительного бонуса требуется Min SDK 9, так что вы можете иметь согласованность дизайна в разных версиях ОС.

Вот пример, взятый из репозитория GitHub библиотек:

<com.andexert.library.RippleView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:rv_centered="true">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@android:drawable/ic_menu_edit"
        android:background="@android:color/holo_blue_dark"/> 

</com.andexert.library.RippleView>

Вы можете изменить цвет пульсации, добавив этот атрибут в элемент RippleView: app:rv_color="@color/my_fancy_ripple_color

guy.gc
источник
3
<android.support.v7.widget.AppCompatButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:backgroundTint="#fff"
    android:textColor="#000"
    android:text="test"/>

использование

xmlns:app="http://schemas.android.com/apk/res-auto"

и цвет примет на pre-lolipop

Андре Бомл
источник
2

Алекс Локвуд объясняет два подхода: http://www.androiddesignpatterns.com/2016/08/coloring-buttons-with-themeoverlays-background-tints.html :

Подход № 1. Изменение цвета фона кнопки с использованием ThemeOverlay

<!-- res/values/themes.xml -->
<style name="RedButtonLightTheme" parent="ThemeOverlay.AppCompat.Light">
    <item name="colorAccent">@color/googred500</item>
</style>

<Button
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:theme="@style/RedButtonLightTheme"/>

Подход № 2. Установка фонового оттенка AppCompatButton

<!-- res/color/btn_colored_background_tint.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Disabled state. -->
    <item android:state_enabled="false"
          android:color="?attr/colorButtonNormal"
          android:alpha="?android:attr/disabledAlpha"/>

    <!-- Enabled state. -->
    <item android:color="?attr/colorAccent"/>

</selector>

<android.support.v7.widget.AppCompatButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:backgroundTint="@color/btn_colored_background_tint"/>
makovkastar
источник
2

Программно применяя цвета:

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {

    ColorStateList colorStateListRipple = new ColorStateList(
            new int[][] {{0}},
            new int[] {Color.WHITE} // ripple color
            );

    RippleDrawable rippleDrawable = (RippleDrawable) myButton.getBackground();
    rippleDrawable.setColor(colorStateListRipple);
    myButton.setBackground(rippleDrawable); // applying the ripple color
}

ColorStateList colorStateList = new ColorStateList(
        new int[][]{
                new int[]{android.R.attr.state_pressed}, // when pressed
                new int[]{android.R.attr.state_enabled}, // normal state color
                new int[]{} // normal state color
        },
        new int[]{
                Color.CYAN, // when pressed
                Color.RED, // normal state color
                Color.RED // normal state color
        }
);

ViewCompat.setBackgroundTintList(myButton, colorStateList); // applying the state colors
Marlon
источник
-1

Я попробовал это:

android:backgroundTint:"@color/mycolor"

вместо изменения свойства фона. Это не снимает материальный эффект.

shiladitya
источник
Это не будет работать для устройств до Lollipop (70% рынка). На самом деле он был портирован, но Android Studio по-прежнему показывает ошибки при использовании без префикса Android.
Сиддхарт Пант