I have been trying to trace this issue for a while now. I think I did find an explanation some time ago. Unfortunately, its lost in code-comments somewhere.
I am trying to create a Material Borderless-Button
in Java. To start with, here's what the button looks like in the framework:
Button bg (button_borderless_material.xml):
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight">
<item android:id="@id/mask"
android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>
The drawable
being used as mask (btn_default_mtrl_shape.xml):
<?xml version="1.0" encoding="utf-8"?>
<!-- Used as the canonical button shape. -->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/button_inset_horizontal_material"
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle"
android:tint="?attr/colorButtonNormal">
<corners android:radius="@dimen/control_corner_material" />
<solid android:color="@color/white" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
android:right="@dimen/button_padding_horizontal_material"
android:bottom="@dimen/button_padding_vertical_material" />
</shape>
</inset>
Java equivalent of the inset-drawable
(btn_default_mtrl_shape.xml):
Drawable createButtonShape(Context context, int color) {
Resource res = context.getResources();
int radius = res
.getDimensionPixelSize(R.dimen.control_corner_material);
int paddingH = res
.getDimensionPixelSize(R.dimen.button_padding_horizontal_material);
int paddingV = res
.getDimensionPixelSize(R.dimen.button_padding_vertical_material);
int insetH = context.getResources()
.getDimensionPixelSize(R.dimen.button_inset_horizontal_material);
int insetV = res
.getDimensionPixelSize(R.dimen.button_inset_vertical_material);
float[] outerRadii = new float[8];
Arrays.fill(outerRadii, radius);
RoundRectShape r = new RoundRectShape(outerRadii, null, null);
ShapeDrawable shapeDrawable = new ShapeDrawable(r);
shapeDrawable.getPaint().setColor(color);
shapeDrawable.setPadding(paddingH, paddingV, paddingH, paddingV);
return new InsetDrawable(shapeDrawable,
insetH, insetV, insetH, insetV);
}
The color
argument is obtained from theme's attr/colorButtonNormal
- the same color used with android:tint
in xml definition.
The RippleDrawable
created programmatically:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
Drawable createButtonRippleBg(Context context,
int colorButtonNormal,
int colorControlHighlight) {
return new RippleDrawable(ColorStateList.valueOf(colorControlHighlight),
null, createButtonShape(context, colorButtonNormal));
}
I am aware that even though the mask-color/shape is not rendered visually, its alpha does affect the RippleDrawable
. This is not an issue here - all colors being used for the mask have full-blown alpha.
I have also confirmed that the colors being read from attributes - colorControlHighlight & colorButtonNormal
- are correct for the theme at play.
Yet, the result is:
Xml rendition:
In Java:
Interesting bit is that this happens on API 21. On API 22, both approaches produce identical results.
The question:
I am certain that this is a bug on API 21. If someone can track this down, we can probably find a multiplier for the alpha/color value to offset this visual difference.
In addition to general good-will, I also promise a bounty as I have already spent quite some time on this.
android:tint="?attr/colorButtonNormal"
and via code you set it as colorshapeDrawable.getPaint().setColor(color);
– Potassiumcolor
argument is actually obtained from theme'scolorButtonNormal
. I mentioned this above: Thecolor
argument is obtained from theme'sattr/colorButtonNormal
- the same color used withandroid:tint
in xml definition. – BrachyuranshapeDrawable.getPaint().setColor(color)
in the xml astint
. why not set it as tint too via code?shapeDrawable.setTint();
– PotassiumColorFilter
usingPorterDuffColorFilter
withMode.MULTIPLY
. The output was not as expected. Moreover, the problem is not present on API 22 which leads me to believe that the xml rendition on API 21 has a bug. – Brachyuran