In fact, you can use <layer-list> for drawing to combine the ripple effect with any other drawing. This universal solution is also for pre-lolipop: I tested it in many configurations.
The only problem is that pre-lolipop crashes when ?selectableItemBackground SelectableItemBackground SelectableItemBackground appears inside the <layer-list> , so we need to create a LayerDrawable programmatically.
A very quick simple solution looks like this:
Specify for your viewing
android:background="?selectableItemBackground"
Then, anywhere in the code, create mySpecialDrawable and do your job:
Drawable[] layers = {mySpecialDrawable, getBackground()}; setBackgroundDrawable(new LayerDrawable(layers).mutate());
Please note that .mutate() for LayeredDrawable is very important here!
A more complex solution may be useful when you already have a custom view, and you prefer to expand its functionality and compatibility rather than add an additional empty FrameLayout as a parent.
Inside attrs.xml put:
<resources> <declare-styleable name="MyView"> <attr name="selectableBackground" format="reference"/> <attr name="backgroundDrawable" format="reference"/> </declare-styleable> </resources>
then inside your descendant class:
private Drawable selectableBackground; private Drawable backgroundDrawable; public MyView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); try { TypedArray attributeArray; attributeArray = context.obtainStyledAttributes(attrs, R.styleable.MyView); int id = attributeArray.getResourceId(R.styleable.MyView_selectableBackground, -1); if (id != -1) { selectableBackground = ResourcesCompat.getDrawable(getResources(), id, context.getTheme()); } id = attributeArray.getResourceId(R.styleable.MyView_backgroundDrawable, -1); if (id != -1) { backgroundDrawable = ResourcesCompat.getDrawable(getResources(), id, context.getTheme()); } constructBackground(); attributeArray.recycle(); } catch (Exception e) { Log.e(this.toString(), "Attributes initialization error", e); throw e; } } void setSelectableBackground(Drawable drawable) { selectableBackground = drawable; constructBackground(); } void setDrawable(Drawable drawable) { backgroundDrawable = drawable; constructBackground(); } private void constructBackground() { if (selectableBackground != null) { if (backgroundDrawable != null) { Drawable[] layers = {backgroundDrawable, selectableBackground}; setBackgroundDrawable(new LayerDrawable(layers).mutate());
I prefer this approach because it does not have problems such as the android:foreground attribute which is 23+, or the additional overhead of including interactive views inside FrameLayout.