After adding color to an android button, it loses its ripple effect that makes the user feel like there is a responsive click. How do I fix this? I\'ve searched through many
Actually, you can use of drawables to combine ripple effect with any other drawable. This is a universal solution also for pre-lolipop: I've tested it in many configurations.
The only problem is that pre-lolipop crashes when ?selectableItemBackground appears inside , so we have to create LayerDrawable programmatically.
A very fast simple solution looks like:
Specify for your View
android:background="?selectableItemBackground"
Then anywhere in the code create mySpecialDrawable and do the trick:
Drawable[] layers = {mySpecialDrawable, getBackground()};
setBackgroundDrawable(new LayerDrawable(layers).mutate());
Please note that .mutate() for LayeredDrawable is essential here!
A more complex solution could be useful when you already have your custom View and prefer rather extend its functionality and compatibility than add extra empty FrameLayout as a parent.
Inside attrs.xml put:
then inside your View-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()); // Both, using layers
} else setBackgroundDrawable(selectableBackground); // Selectable only
} else setBackgroundDrawable(backgroundDrawable); // Background only or null
}
I prefer this approach because it has no issues like android:foreground attribute which is 23+ or extra overhead of enclosing clickable views inside FrameLayout.