I am using a ripple effect on my navigation drawer. I have set it like this and applied it to my ListView:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:drawable="#color/black_200" />
<item android:state_pressed="true" android:color="#color/black_200">
<ripple android:color="#color/black_400" />
</item>
<item android:drawable="#color/white" />
</selector>
I want the ripple background to remain the same like the normal activated state. Even when I define the color to be the same as the activated state, it gets darker and the ripple bubble gets even more dark. How can I color the background to be the same like the activated state and the ripple bubble to be the color I told it to be?
It's a lot easier to do with styles:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
<item name="colorControlHighlight">#color/ripple_material_dark</item>
...
</style>
You can control the colour of the RippleDrawable by doing the following:
RippleDrawable rippleDrawable = (RippleDrawable)view.getBackground(); // assumes bg is a RippleDrawable
int[][] states = new int[][] { new int[] { android.R.attr.state_enabled} };
int[] colors = new int[] { Color.BLUE }; // sets the ripple color to blue
ColorStateList colorStateList = new ColorStateList(states, colors);
rippleDrawable.setColor(colorStateList);
Or, via XML:
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#FFFF0000"> <!-- The ripple will be red -->
<!-- the normal bg color will be light grey -->
<item>
<color android:color="#FFDDDDDD" />
</item>
<!-- make sure the ripple doesn't exceed the bounds -->
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<solid android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
EDIT
After seeing #i.shadrin's answer below, I must admit it's a much simpler approach (using styles). If this is an option for you, I would recommend it.
add these two line of code inside of your view to give ripple effect .
android:clickable="true"
android:background="?attr/selectableItemBackground"
If you are using the custom button color then use below code for ripple effect.
Save the file in drawable with name my_ripple.xml
and set in background of button.
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="#color/ripple_color"
tools:targetApi="lollipop">
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<solid android:color="#color/ripple_color"
/>
</shape>
</item>
<item
android:id="#android:id/background"
android:drawable="#color/button_color" />
</ripple>
An example ripple to use with a selector on api lvl 21 and above
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight">
<!-- ripple mask -->
<item android:id="#+id/mask">
<color android:color="#android:color/white"/>
</item>
<!-- background shape or color -->
<item android:drawable="#drawable/rectangle_rounded_steel_blue"/>
</ripple>
Related
I created custom button based on a MaterialButton that is semi-transparent (primaryColorDisabled) when pressed and disabled. Now, I would like to have its ripple effect to turn the button from its primaryColor to the primaryColorDisabled instead of the MaterialButton's default highlighted color.
My colors only differ only in alpha and are defined:
<color name="primaryColor">#283593</color>
<color name="primaryColorDisabled">#A0283593</color>
To my custom button I apply the following style:
<style name="CustomButton" parent="Widget.MaterialComponents.Button">
<item name="android:background">#drawable/custom_background</item>
</style>
Below are the recursive definitions of my drawables. I removed not pertinent bits like namespace, padding, margins, etc.
#drawable/custom_background:
<selector>
<item android:state_enabled="false"
android:drawable="#drawable/custom_disabled" />
<item android:state_pressed="true"
android:drawable="#drawable/custom_disabled" />
<item android:drawable="#drawable/custom_ripple" />
</selector>
#drawable/custom_disabled:
<inset>
<shape
android:shape="rectangle"
android:tint="#color/primaryColorDisabled" >
<solid android:color="#color/primaryColor" />
</shape>
</inset>
#drawable/custom_ripple:
<ripple android:color="?attr/colorControlHighlight">
<item android:drawable="#drawable/custom_enabled" />
</ripple>
#drawable/custom_enabled:
<inset>
<shape
android:shape="rectangle" >
<solid android:color="#color/primaryColor" />
</shape>
</inset>
Now, I tried changing in #drawable/custom_ripple the color from ?attr/colorControlHighlight to my #color/primaryColorDisabled, but then when I click on my button nothing changes: no ripple and no even 'state_pressed' look. Any ideas how to achieve that?
I want to have a background color that changes color according to the state of the view.
I tired creating it exactly like the example from Google (except I'm using colors from my resources instead of hardcoding the hex values :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:color="#color/transparent" />
<item
android:color="#color/gray_3"
android:state_selected="true" />
</selector>
(this is located in res/color)
then I set this as a background to my view.but when I run the code, I get a crash with this error:
Binary XML file line #3: <item> tag requires a 'drawable' attribute or child tag defining a drawable
It's telling me that the "item" tag in my selector needs drawable, not color to define the color.
when I change it to a drawable, as such:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable ="#color/transparent" />
<item
android:drawable ="#color/gray_3"
android:state_selected="true" />
</selector>
that crash goes away, but Android Studio gives me a lint warning saying that "color" is required
my only suspicion is that maybe I'm not supposed to set the view background as a color selector
Try this:
drawable xml code:-
transparent color use like this
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#android:color/transparent"" />
<stroke
android:width="1dp"
android:color="#color/selected" />
<corners android:radius="4dp" />
</shape>
Define #color/selected in color xml.
As Google's documentation here, you should not define the default statement in the first item. Here's the example they provide.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:color="#ffff0000"/> <!-- pressed -->
<item android:state_focused="true"
android:color="#ff0000ff"/> <!-- focused -->
<item android:color="#ff000000"/> <!-- default -->
</selector>
Try using hex color codes instead of color values.
i want to change background of my view when the state is "activated" and i want to preserve the effects(ripple) of ?attr:selectableItemBackground. Is it possible to extend or combine selector of ?attr:selectableItemBackground?
You can use a LayerDrawable in order to draw the ripple effect drawable (?attr:selectableItemBackground) over your activated state color.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<selector>
<item android:state_activated="true">
<color android:color="?attr/colorPrimary"/>
</item>
<item>
<color android:color="#android:color/transparent"/>
</item>
</selector>
</item>
<item android:drawable="?attr/selectableItemBackground"/>
</layer-list>
Edit:
As it's not possible to use theme attributes in an XML drawable before API 21, it seems to be better to put the ripple effect drawable as a foreground drawable, and the activated color selector drawable as a background drawable.
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/yourView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foreground="?attr/selectableItemBackground"
android:background="#drawable/activated_color_selector">
With res/drawable/activated_color_selector.xml containing:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true">
<!-- Can't use the ?attr/colorPrimary before API 21 -->
<color android:color="#color/primaryColor"/>
</item>
<item>
<color android:color="#android:color/transparent"/>
</item>
</selector>
For those who only care about API >= 21 now in 2020, here's a simpler solution :
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?colorControlHighlight">
<!-- š”” the ripple's color (1) -->
<!-- š”£ no `id` so our <shape> will be drawn and not just used as mask -->
<item>
<shape>
<corners android:radius="9dp" />
<solid android:color="#color/white" />
</shape>
</item>
</ripple>
(1) If you don't override colorControlHighlight in your theme, the ripple's color will be Android's default. If you do override it in your theme but want to use Android's default for a specific case use ?android:colorControlHighlight instead.
To change ripple color throughout the app you can ad this in your app theme
<item name="colorControlHighlight">#color/ripple</item>
Unfortunately, the only way I found is to have additional view in your layour that will emulate the selected state. Like so (works on pre-Lollipop as well):
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight"
android:background="?attr/selectableItemBackground">
<View
android:id="#+id/item_selected"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorControlHighlight"/>
<android.support.v7.widget.AppCompatTextView
android:id="#+id/item_title"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:textAppearance="?attr/textAppearanceListItem"/>
</FrameLayout>
Then in your adapter you set visibility of item_selected to either View.VISIBLE or View.GONE, based on the need to make this particular item selected.
P.S. Obviously, this solution uses AppCompat support library, so in order to use it the following line should be added to build.gradle file:
implementation 'com.android.support:appcompat-v7:28.0.0'
I solved this problem by swapping the background drawables at runtime. This code runs in onBindViewHolder in RecyclerView.Adapter<MyViewHolder>:
if (selected) {
itemView.setBackgroundResource(R.drawable.selected_background);
} else {
TypedArray typedArray = context.obtainStyledAttributes(
new int[]{android.R.attr.selectableItemBackground});
itemView.setBackgroundResource(typedArray.getResourceId(0, 0));
typedArray.recycle();
}
The R.drawable.selected_background refers to the background of a selected item:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/colorAccentLight" android:state_activated="true" />
<item android:drawable="#android:color/transparent" />
</selector>
Another solution is to have two identical item types except for different backgrounds but that seems to be an overkill.
Hi so I am little confused and wondering if anyone could point me in the right direction.
Go and use Google Play Store on Lollipop and pre-lollipop
You will see on lollipop that selectable views have the ripple effect.
On pre-lollipo, you get this highlight effect.
How is this done?
At the moment in my app, I have a drawable-v21 directory that contains this selector
It basically does the ripple on top of my background
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight">
<item android:id="#android:id/mask" android:drawable="#android:color/white"/>
<item android:drawable="#color/colorAccentWith92PercentOpacity"/>
</ripple>
However, other answers say to use
android:background="?attr/selectableItemBackground"
To get the highlight effect on pre-lollipop but this overrides my background. How could i set this on top of my current background?
Also do i have to create a ripple drawable (in drawble-v21) for every kind of button in my app? How would I do this for recycler view items?
What makes this question unique
I do not want ripple for pre-lollipop I am asking how devs efficiently make their button do ripple on lollipop and a hight light effect on pre
Option 1
Define colorControlHighlight in your theme and as long you're using default appcompat-v7 buttons the highlight color should work out-of-the-box.
Option 2
This is an example of how I backported Material button style with a bit of crossfade animation and shadows without using external libraries. May it help you on your way.
Provided the button will be white text over dark background (#color/control_normal) with light highlight:
values/themes.xml
Here I'll override default button style for the whole theme:
<style name="AppTheme" parent="Base.AppTheme">
<item name="buttonStyle">#style/Widget.AppTheme.Button</item>
</style>
values/integers.xml
<!-- Some numbers pulled from material design. -->
<integer name="button_pressed_animation_duration">100</integer>
<integer name="button_pressed_animation_delay">100</integer>
values-v21/styles.xml
Button style for Lollipop which understands theme overlays and uses ripple by default. Let's just have it color the ripple with appropriate paint:
<style name="Widget.AppTheme.Button" parent="Widget.AppCompat.Button">
<!-- On Lollipop you can define theme via style. -->
<item name="android:theme">#style/ThemeOverlay.AppTheme.Button</item>
</style>
<style name="ThemeOverlay.AppTheme.Button" parent="ThemeOverlay.AppCompat.Dark">
<!-- The magic is done here. -->
<item name="colorButtonNormal">#color/control_normal</item>
</style>
values/styles.xml
Before Lollipop it gets tricky.
<style name="Widget.AppTheme.Button" parent="Widget.AppCompat.Button">
<item name="android:background">#drawable/button_normal_background</item>
</style>
drawable/button_normal_background.xml
Thi is the composite drawable of the whole button.
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="#dimen/abc_button_inset_horizontal_material"
android:insetTop="#dimen/abc_button_inset_vertical_material"
android:insetRight="#dimen/abc_button_inset_horizontal_material"
android:insetBottom="#dimen/abc_button_inset_vertical_material">
<layer-list>
<!-- Shadow. -->
<item
android:drawable="#drawable/button_shadow"
android:top="-0dp"
android:bottom="-1dp"
android:left="-0dp"
android:right="-0dp"/>
<item
android:drawable="#drawable/button_shadow_pressable"
android:top="-0dp"
android:bottom="-3dp"
android:left="-1dp"
android:right="-1dp"/>
<!-- Background. -->
<item android:drawable="#drawable/button_shape_normal"/>
<!-- Highlight. -->
<item>
<selector
android:enterFadeDuration="#integer/button_pressed_animation_duration"
android:exitFadeDuration="#integer/button_pressed_animation_duration">
<item
android:drawable="#drawable/button_shape_highlight"
android:state_focused="true"
android:state_enabled="true"/>
<item
android:drawable="#drawable/button_shape_highlight"
android:state_pressed="true"
android:state_enabled="true"/>
<item
android:drawable="#drawable/button_shape_highlight"
android:state_selected="true"
android:state_enabled="true"/>
<item android:drawable="#android:color/transparent"/>
</selector>
</item>
<!-- Inner padding. -->
<item android:drawable="#drawable/button_padding"/>
</layer-list>
</inset>
drawable/button_shadow.xml
This is the shadow when not pressed.
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="3dp"
android:bottomRightRadius="3dp"
android:topLeftRadius="2dp"
android:topRightRadius="2dp"/>
<solid android:color="#2000"/>
</shape>
drawable/button_shadow_pressable.xml
This is the extended shadow in pressed state. The result effect will look crude when you look up close but it's good enough from distance.
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="UnusedAttribute"
android:enterFadeDuration="#integer/button_pressed_animation_duration"
android:exitFadeDuration="#integer/button_pressed_animation_duration">
<item
android:state_pressed="true"
android:state_enabled="true">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp"
android:topLeftRadius="3dp"
android:topRightRadius="3dp"/>
<solid android:color="#20000000"/>
</shape>
</item>
<item android:drawable="#android:color/transparent"/>
</selector>
drawable/button_shape_normal.xml
This is the main button shape.
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="#dimen/abc_control_corner_material"/>
<solid android:color="#color/control_normal"/>
</shape>
drawable/button_padding.xml
Just additional padding to be absolutely consistent with the Material button.
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/transparent"/>
<padding
android:left="#dimen/abc_button_padding_horizontal_material"
android:top="#dimen/abc_button_padding_vertical_material"
android:right="#dimen/abc_button_padding_horizontal_material"
android:bottom="#dimen/abc_button_padding_vertical_material"/>
</shape>
drawable/button_shape_highlight.xml
This is the highlight button shape drawn over normal button shape.
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="#dimen/abc_control_corner_material"/>
<solid android:color="#color/control_highlight"/>
</shape>
#color/control_highlight can point to
#color/ripple_material_dark - translucent white, use over dark background
#color/ripple_material_light - translucent black, use over light background
Any other color you define.
You can set a background of your views in this way:
android:background="#drawable/touch_selector"
Create a version without ripple for pre lollipop:
drawable/touch_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- State when a row is being pressed, but hasn't yet been activated (finger down) -->
<item android:state_pressed="true"
android:drawable="#color/grey"
/>
<!-- For ListView in SINGLE_CHOICE_MODE, it flags the active row -->
<item android:state_activated="true"
android:drawable="#color/light_green" />
<!-- Default, "just hangin' out" state. -->
<item android:drawable="#android:color/transparent" />
</selector>
Now do the same for lollipop and above,
but with ripple effect:
crete drawable-v21/touch_selector.xml
It will look like:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- State when a row is being pressed, but hasn't yet been activated (finger down) -->
<item android:state_pressed="true">
<ripple android:color="#color/grey" />
</item>
<!-- For ListView, when the view is "activated". In SINGLE_CHOICE_MODE, it flags the active row -->
<item android:state_activated="true"
android:drawable="#color/light_green" />
<!-- Default, "just hangin' out" state. -->
<item android:drawable="#android:color/transparent" />
</selector>
That's it.
Now you are having ripple effect at lollipop and above devices and highlight at pre lollipop.
Edit:
In case of using in a ListView - use created above as a background of ListView item
I have the following definition of an android style and selector applied to ListView, however I see no effect of gray gradient applied when I click on it. On click it immediately displays blue gradient.
As author of this example has written:
We want to apply this to our list selector. Instead of always showing
the same gradient, we want the gradient to change its start color from
grey to a light blue whenever a list item is pressed. Because we now
have two different list selectorsāone for default state one for
pressed stateāwe need to keep them in separate files.
Can someone explain how it should be displayed properly ? Or I'm missing something ?
More general, what exactly I should expect to see on display screen ? Something like fluent transition from gray gradient to blue gradient on list item click ?
Update: this is how author 'sees' it, but when I click on item it looks only like on right side of picture. May I assume that author wants to see all items in list to be gray by default ? Or this state is not applicable to list view items ?
list_item_default.xml -> GRAY GRADIENT
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#FFFF"
android:startColor="#AFFF" />
<stroke
android:width="1dip"
android:color="#CCC" />
<corners android:radius="5px" />
</shape>
list_item_pressed.xml -> BLUE GRADIENT
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#FFFF"
android:startColor="#AA66CCFF" />
<stroke
android:width="1dip"
android:color="#CCC" />
<corners android:radius="5px" />
</shape>
list_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/list_item_default" android:state_pressed="false"/>
<item android:drawable="#drawable/list_item_pressed" android:state_pressed="true"/>
</selector>
styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyMoviesTheme" parent="#android:style/Theme.Black">
<item name="android:listViewStyle">#style/MyMoviesListView</item>
<item name="android:windowBackground">#drawable/film_bg</item>
</style>
<style name="MyMoviesTextAppearance" parent="#android:style/TextAppearance">
<!--<item name="android:textStyle">bold</item>-->
</style>
<style name="MyMoviesListView" parent="#android:Widget.ListView">
<item name="android:background">#color/list_background</item>
<item name="android:listSelector">#drawable/list_selector</item>
<item name="android:cacheColorHint">#android:color/transparent</item>
<item name="android:fastScrollEnabled">true</item>
<item name="android:footerDividersEnabled">false</item>
</style>
</resources>
Try adding a sentenceļ¼
<item
android:state_enabled="true"
android:drawable="#drawable/list_item_default"/>
<item
android:state_enabled="true"
android:drawable="#drawable/list_item_pressed"
android:state_pressed="true"/>