I have several shape drawable resourses that I want use as backround for buttons and they are similar except gradient start and end color and stroke color. The problem is that I don't want to create several similar XML files for this drawables.
I throught that I can inherit shape drawable like a styles using tag, but this is impossible.
I found this Defining XML Parent Style of Gradient / Shape / Corners but I don't understand how it's working. How he changing attributes values?
There Using parents for drawable resources I found that I can change appearance of background by layer-list that will draw layers of drawables one on top of another. But how manipulate with gradient and stroke color using layer-list?
I found that I can change appearance of buttons background in runtime, but probably there is a simpler way to do this?
the initial suggestion I made is not feasible. I thought the <shape>
will return a ShapeDrawable
but I was wrong, here is how I would modify the gradient.
Before the button is clicked
After the button is clicked, its background changes.
This is the Main Activity
public class MainActivity extends Activity {
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v)
{
try
{
// you can use the GradientDrawable directly, check the docs for details,
// I extended GradientDrawable just to make myself look like a badass :)
MyReflection.invokeMethod(button, new Gradient(), "setBackground");
}
catch (NoSuchMethodException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
});
}
}
Utility method to get a new method with the an object to invoke the method on, the Drawable
to use as the new background, and the name of the method without the ()
such as setBackground
to run on older versions. Reflection is SLOW. Use it sparingly.
/*
* We want to use reflection because the View.setBackground() method
* is only available on API level 16 and up.
* If you don't understand this, you can follow the tutorial provided by
* Oracle, just search for it on Google with the keyword "reflection tutorial"
*/
public class MyReflection {
public static void invokeMethod(Object receiverObject, Drawable background, String methodName)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
{
Class<?> clazz = View.class;
Method method = clazz.getMethod(methodName, Drawable.class);
// Allowing non-public access call to this method.
method.setAccessible(true);
method.invoke(receiverObject, background);
}
}
My custom gradient
/**
* I am extending the GradientDrawable just for fun, and also
* because I want to set the shader property for this drawable,
* you could, however, use the GradientDrawable directly.
*/
public class Gradient extends GradientDrawable {
private Paint paint;
private LinearGradient gradient;
public Gradient()
{
super();
// check the docs for LinearGradient for these parameters
gradient = new LinearGradient(0,0, 100, 100, Color.BLUE, Color.GREEN, Shader.TileMode.REPEAT);
paint = new Paint();
paint.setShader(gradient);
}
public Gradient(GradientDrawable.Orientation orientation, int[] colors)
{
super(orientation, colors);
gradient = new LinearGradient(0,0, 100, 100, Color.BLUE, Color.GREEN, Shader.TileMode.REPEAT);
paint = new Paint();
paint.setShader(gradient);
}
@Override
public void draw(Canvas canvas)
{
canvas.drawPaint(paint);
}
}
The initial background for the button
<?xml version="1.0" encoding="utf-8"?>
<!-- The initial shape with just a solid yellow color -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#ff0"/>
</shape>