RippleDrawable DO NOT draw over views

I have a layout with KenBurnsView and ImageView above it (just a toggle button). When I click on the button, a Ripple is created, but KenBurnsView is drawn below.

Previously, when I had the Image view instead of KenBurnsView, Ripple was drawn above the ImageView.

Here is my layout:

<LinearLayout 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/drawer" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/background" android:clickable="true" android:orientation="vertical"> <RelativeLayout android:id="@+id/header" android:layout_width="match_parent" android:layout_height="@dimen/nav_drawer_header_height"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.flaviofaria.kenburnsview.KenBurnsView android:id="@+id/header_cover" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/cover_1" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/header_toggle" android:layout_width="50dp" android:layout_height="30dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="10dp" android:layout_marginRight="10dp" android:padding="10dp" android:src="@drawable/toggle_down" /> </RelativeLayout> </FrameLayout> </RelativeLayout> <RelativeLayout android:id="@+id/nav_toggle_container" android:layout_width="wrap_content" android:layout_height="wrap_content"></RelativeLayout> </LinearLayout> 

This is my flexible XML code:

 <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@android:color/white" android:drawSelectorOnTop="true"> <!-- ripple color --> </ripple> 

This is how I add ripple:

 toggle.setBackground(getResources().getDrawable(R.drawable.ripple)); 

What is the problem that makes Ripple get drwan below KenBurnsView? It worked fine when instead of KenBurnsView was ImageView?

+1
source share
3 answers

as you can see from your own code ('setBackground'), you set the ripples as BACKGROUNDS to draw on the background.

ImageView in android API 21 added this "hack" to ripple android:drawSelectorOnTop="true" . But the library you use did not add the same hack to it.

There is nothing wrong with the code. But this type of behavior cannot be guaranteed by the Android team for third-party libraries.

You have several options here that will vary in purity, effort, and performance:

  • check the source code of ImageView, clone the library, add the same hack as the image used for ripple for it. After it works fine, be sure to return the request back to the library.
  • wrap KenBurnsView with FrameLayout and set the ripple using setForeground in FrameLayout.
  • clone the library, add an option to draw the foreground (similar to How to set the foreground attribute for a different view than FrameLayout ). Also remember to return this meaningful code back to the library.
0
source

A limitless ripple is projected onto the first accessible background of the ancestor. Track the hierarchy of views from ImageView , on which the wave is placed, and you will find that the first available background is in LinearLayout with the identifier drawer .

If you set a transparent background to a RelativeLayout containing your ImageView , the first available background will be the one that appears above the sibling view and you will get the desired effect.

Note that RelativeLayout should be replaced with FrameLayout , which will provide the same effect with a less expensive layout.

+3
source

I took inspiration and helped from @Budius answer and created a much better solution.

ImageView uses a hack that allows RippleDrawable to draw on top of it, even when you set it as a background (using setBackgroundDrawable ()).

I created a modified ImageView with the ability to draw a foreground,

 public class ForegroundImageView extends ImageView { private Drawable foreground; public ForegroundImageView(Context context) { this(context, null); } public ForegroundImageView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundImageView); Drawable foreground = a.getDrawable(R.styleable.ForegroundImageView_android_foreground); if (foreground != null) { setForeground(foreground); } a.recycle(); } /** * Supply a drawable resource that is to be rendered on top of all of the child * views in the frame layout. * * @param drawableResId The drawable resource to be drawn on top of the children. */ public void setForegroundResource(int drawableResId) { setForeground(getContext().getResources().getDrawable(drawableResId)); } /** * Supply a Drawable that is to be rendered on top of all of the child * views in the frame layout. * * @param drawable The Drawable to be drawn on top of the children. */ public void setForeground(Drawable drawable) { if (foreground == drawable) { return; } if (foreground != null) { foreground.setCallback(null); unscheduleDrawable(foreground); } foreground = drawable; if (drawable != null) { drawable.setCallback(this); if (drawable.isStateful()) { drawable.setState(getDrawableState()); } } requestLayout(); invalidate(); } @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == foreground; } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (foreground != null) foreground.jumpToCurrentState(); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (foreground != null && foreground.isStateful()) { foreground.setState(getDrawableState()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (foreground != null) { foreground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); invalidate(); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (foreground != null) { foreground.setBounds(0, 0, w, h); invalidate(); } } @Override public void draw(Canvas canvas) { super.draw(canvas); if (foreground != null) { foreground.draw(canvas); } } } 

Then I put my image in that image and apply the ripple programmatically,

 toggle.setForegroundResource(R.drawable.ripple); 

This will cause ripples to occur within the given species. To make ripples draw other species,

 <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false"> 

You need to add false for both clipChildren and clipToPadding.

Hope this helps someone get stuck in the problem.

+3
source

All Articles