Android Recycler View Ripple Effect not working - android

I am using the following xml drawable as the background of my recyclerview list item.
touch_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true"
android:drawable="#color/green_text_500"/>
<!-- Default, "just hangin' out" state. The base state also
implements the ripple effect. -->
<item android:drawable="#drawable/touch_selector_base" />
</selector>
touch_selector_base.xml
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/light_grey">
<item android:id="#android:id/mask" android:drawable="#color/light_grey" />
<item android:drawable="#color/dark_grey"/>
</ripple>
In the list item I am using the touch_selector.xml as follows in my list_item_quote.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="86dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:clickable="true"
android:focusable="true"
android:background="#drawable/touch_selector"
>
// layout
</LinearLayout>
SIMILARLY I have another xml drawable touch_selector_dark.xml and touch_selector_base_dark.xml
In my recycler-view-adapter I alternate between these two drawables based on the index. For even indexes I use the darker drawable and for the odd index I use the lighter background to produce an alternating effect. But the issue is that the ripple effect is not working.
Here are the colors:
light_grey = #5b5b5b
dark_grey = #212121
green_text_500 = #37863a

use this line in your recycleview list layout
android:background="?android:attr/selectableItemBackground"

try this
worked for me
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"

I think your ripple file is not valid, you can check this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#color/dark_grey"/>
</shape>
</item>
</selector>
You can use android:foreground="#drawable/touch_selector" to display the ripple effect and also use android:background="" to set any other drawable background.

Related

How to set button background drawable image with ripple effect in android

My Xml code:
<Button
android:id="#+id/link_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/google_btn" />
I am applying Default ripple effect
<Button
android:id="#+id/link_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground" />
but I need button background "#drawable/google_btn" with
"?android:attr/selectableItemBackground". it's means i need ripple effect with custom background.
In your drawable-v21 folder you can write code for ripple effect by your own. Make a drawable xml file and set starting tag by ripple. Like this :
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#color/colorAccentDark">
<item>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="#color/button_accent_dark"
android:state_checked="false"/>
<item
android:drawable="#color/button_accent"
android:state_checked="true" />
<item
android:drawable="#color/button_accent_dark" />
</selector>
</item>
</ripple>
When the button has a background from the drawable, we can add ripple effect to the foreground parameter.. Check below code its working for my button with a different background
<Button
android:layout_width="wrap_content"
android:layout_height="40dp"
android:gravity="center"
android:layout_centerHorizontal="true"
android:background="#drawable/shape_login_button"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:text="#string/action_button_login"
/>
Add below parameter for ripple effect
android:foreground="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
<?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="#drawable/btn_rectangle"/>
<item android:drawable="#drawable/btn_rect_states"/>
</ripple>
Try the above code here in drawables give your own custom drawable backgrounds as you need it.
Another option is to create a layer list drawable and set that as a background. For example:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/button_background"/>
<item android:drawable="?attr/selectableItemBackground"/>
</layer-list>

android selectableItemBackground selection

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.

How to make a ripple effect over a linear layout, without overriding the background color on its children?

I have a LinearLayout that looks like this.
I want each row to be clickable. The LinearLayout code for a row looks like this:
<LinearLayout
style="#style/home_menu_item_container"
android:id="#+id/home_menu_acronyms_button"
>
<ImageView
style="#style/home_menu_item_left"
android:background="#color/greyLight"
/>
<TextView
style="#style/home_menu_item_right"
android:text="#string/home_menu_option_2"
android:background="#color/grey"
/>
</LinearLayout>
How can I add a ripple effect that expands over the entire row (parent) - not just one child view in the row? The tricky part here is to let the ripple go over the two colored row.
So far, I found out the easiest way to do so is define a <ripple> in your drawable and then set the background of the LinearLayout to this drawable resource.
Define your drawable-v21/item_selector.xml
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/your_background_color">
<item android:id="#android:id/mask"
<!--color here doesn't matter-->
android:drawable="#android:color/white" />
</ripple>
Set the background of your LinearLayout to drawable/item_selector.
<LinearLayout
style="#style/home_menu_item_container"
android:background="#drawable/item_selector"
android:id="#+id/home_menu_acronyms_button" >
...
</LinearLayout>
Besides, if you don't have your own background color, then there is no need to define a item_selector at all. You can simply define background as android:background="?android:attr/selectableItemBackground" for your LinearLayout.
It is a little bit complicated for that what you need but I don't think there is another way,...
You need to put your ImageView's into a ListView so that every ImageView is a ListItem and then you can set the ripple but you also need to set drawSelectorOnTop="true" otherwise it won't work correctly
I too faced this problem, finally found a simple solution
In linear Layout just add android:clickable="true";
and set background with ur ripple effect as
android:background="#drawable/ripple_effect"
code:
<LinearLayout
android:clickable="true"
android:background="#drawable/ripple_effect"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Add your child views here -->
</LinearLayout>
add ripple_effect.xml in drawable
ripple_effect.xml:
<?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="#f87d05c2"
tools:targetApi="lollipop">
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<solid android:color="#f87d05c2" />
</shape>
</item>
</ripple>
<RelativeLayout
android:id="#+id/rll_privacy_policy"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="#+id/txt_privacy_policy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/layout_margin_16"
android:text="#string/privacy_policy"
android:layout_centerVertical="true"
android:textColor="#color/link_acc"
android:textSize="#dimen/txt_title_20" />
</RelativeLayout>
Another option is to make the ripple's background color transparent. This way only the ripple can be seen. The ripple's xml file (in your drawable-v21/ folder) is thus:
<-- Ripple in some ghastly color, like bright red so you can see it -->
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/red"
>
<!-- background color uses a transparent mask set to full #ffffff (white) -->
<item
android:id="#android:id/mask"
android:drawable="#android:color/white"
/>
Note that if you are supporting pre-lollipop devices, you need to have a dummy file with the same name in your drawable/ folder. An empty selector is sufficient for that file. And remember that those older devices will not ripple.
answer above dont work for me,
here is my solution:
ripple.xml:
<?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">
<shape android:shape="rectangle">
<solid android:color="?android:colorAccent" />
</shape>
</item>
</ripple>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
>
...put het your design
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ripple" />
</FrameLayout>

How to create ripple effect in simple layout

How can I have a ripple effect in a simple linear/relative layout when touching the layout?
I've tried setting the background of a layout to something like:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight" >
<item android:drawable="#android:color/white"/>
</ripple>
However I'm only seeing a plain white background and no ripple effect when touching the layout. What am I doing wrong?
Edit:
For reference, here is my layout xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="250dp"
android:layout_height="250dp"
android:background="#drawable/test"
android:clickable="true">
</RelativeLayout>
Set these properties on your layout (if your layout has the default white/light background):
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
You don't need a custom drawable in this case.
However, if your layout has a black/dark background, you'd have to create your own ripple drawable like this one:
<?xml version="1.0" encoding="utf-8"?>
<!-- An white rectangle ripple. -->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#android:color/white">
<item
android:id="#android:id/mask"
android:gravity="center">
<shape android:shape="rectangle">
<solid android:color="#android:color/white"/>
</shape>
</item>
</ripple>
And then set this ripple drawable as your android:background property.
You can see many examples of these types of ripples in AOSP: in here
I'm adding this in addition to Igor Ganapolsky's answer in case anyone wants to have a layout with different color and also have a ripple effect. You simply create a ripple effect in drawables like this:
ripple.xml
<?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/rippleColor"
tools:targetApi="lollipop"> <!-- ripple effect color -->
<item android:id="#android:id/background">
<shape android:shape="rectangle">
<solid android:color="#color/backgroundColor" /> <!-- background color -->
</shape>
</item>
</ripple>
then give it as your layout's background or any button as Igor Ganapolsky mentioned in his answer.
android:clickable="true"
android:background="#drawable/ripple"
You have to alter following code snippet in any widget(TextView/Button) and you can implement Ripple Effect:
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
And you are good to go.
It turns out this is working as intended. The standard Material theme colorControlHighlight value is #40ffffff. So on a white background, this will not show up. Changing the highlight color to something else works, and/or changing the background color of the object.
Thanks all (especially Alanv for pointing me in the right direction).
This is an example for clicking on a sample layout with ripple effect :
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical">
Set android:clickable="true" on your layout.
<?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="#cfd8dc"
tools:targetApi="lollipop">
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<solid android:color="#d1c4e9" />
</shape>
</item>
</ripple>
Use this ripple.xml in Drawable folder, then assign it as View's
android:background="#drawable/ripple"
android:clickable="true"
Simply add default ripple effect with android:background="?selectableItemBackground"
Like:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="?selectableItemBackground"
android:clickable="true"
>
...
</LinearLayout>
Put this into your layout:
android:clickable="true"
android:background="?attr/selectableItemBackground"
Note: There is a bug in API documentation. It will work only on API >= 23
For all API levels use this Solution
I tries all those answers and nothing worked to me, so, I solved creating the following style:
<style name="Button.Borderless" parent="Base.Widget.AppCompat.Button.Borderless">
<item name="android:minHeight">0dp</item>
<item name="android:minWidth">0dp</item>
<item name="android:layout_marginLeft">-6dp</item>
<item name="android:layout_marginRight">-6dp</item>
<item name="android:paddingTop">0dp</item>
<item name="android:paddingBottom">0dp</item>
<item name="android:paddingLeft">6dp</item>
<item name="android:paddingRight">6dp</item>
</style>
And then, I applied to my linear layout:
<LinearLayout
android:id="#+id/ll_my_ripple"
style="#style/Button.Borderless">
</LinearLayout>
To make a circlar effect use the below code:
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"

Selector in Custom View With 9 Patch Image Background

I have problem with implementing selector in custom view. I have searched it for the last 1 hour but I could not find the solution. I have custom view with imageview, some textviews and nine patch background but the problem is when it's on pressed state, the color of selector doesn't cover the image view.
For the implementation, I'm using background selector for the layout and I just change the 9 patch image on pressed and un-pressed state, then disable default selector in listview.
Do you have any better solution? I see in google play, the blue selector covers all of the views and that is what I want.
For the xml,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/touch_selector"
android:orientation="horizontal"
android:padding="16dp" >
<ImageView />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView />
<TextView />
</LinearLayout>
</LinearLayout>
And for the touch selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/card_background_pressed" android:state_focused="true"/>
<item android:drawable="#drawable/card_background_pressed" android:state_pressed="true"/>
<item android:drawable="#drawable/card_background_white" android:state_focused="false" android:state_pressed="false"/>
</selector>
where card_background is 9 patch image.
EDIT :
This is the example from google play,
The accepted is not correct.
It's possible to use a nine-path in the selector, you just need to use the <nine-patch>. Something like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<nine-patch
android:src="#drawable/marker"/>
</item>
<item>
<shape>
<solid android:color="#000000" />
</shape>
</item>
</selector>

Categories

Resources