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(); } public void setForegroundResource(int drawableResId) { setForeground(getContext().getResources().getDrawable(drawableResId)); } 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.