0

In my application I have a lot of drawables defined with xml files. For example I have a button defined like that:

button.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Bottom 3dp Shadow -->
<item android:top="3dp">
    <shape android:shape="rectangle">
          <corners android:radius="3dp" />
          <solid android:color="@color/black_30" />   
    </shape>
</item>
<!-- green top color -->
<item android:top="3dp" android:bottom="3dp" android:id="@+id/background">
    <shape android:shape="rectangle">
        <corners android:radius="3dp" />
        <solid android:color="@color/green1" />           
    </shape>
 </item>
</layer-list>

and then I display a button like that:

layout.xml
<Button
        android:id="@+id/button"
        android:layout_gravity="center"
        android:layout_height="60dp"
        android:layout_width="fill_parent"
        android:textSize="17sp"
        android:gravity="center"
        android:background="@drawable/button" />

When I navigate in the app, I want to "theme" some views (some colors are changing in function of the context) and to do that I would like to be able to change dynamically the color of the button (green1) at runtime.

1) One first nice approach would be to change the color definition in button.xml with an ?attr/my_color. And then define the different color values I need in the theme file style.xml. Then at runtime, I can switch to the desired theme and that will work. The complete steps are here:

How to reference colour attribute in drawable?

The issue is that it works on Android 5 but not on Android 4 ( and I need to support this version) (we get an android.view.InflateException: Binary XML file line #2: Error inflating class <unknown>)

2) The second approach is to load the drawable in the code and then use setColorto change the color of the drawable: (written in Xamarin.Android but I am sure that you will understand the corresponding Java version)

LayerDrawable button = (LayerDrawable)Resources.GetDrawable(Resource.Drawable.normal_question_button);
GradientDrawable background = (GradientDrawable)button.FindDrawableByLayerId(Resource.Id.background);
background.SetColor(Android.Graphics.Color.Red.ToArgb());

The good things is that it's works... but randomly... Sometimes when I am displaying the button again, it's still the original green that is displayed. Sometimes, it's the new color... And once I have one of the both behaviour, the same color can stay many time and suddenly it changes again to the correct one.

Someone could explain this? Is there some caching on drawables that can gives that kind of issue?

3) I was thinking about a third solution: dynamically change the color defined in colors.xml (where green1 is defined) but it doesn't seem possible

Community
  • 1
  • 1
nicolas
  • 489
  • 1
  • 3
  • 22
  • Hello, in my navigation drawer icons (drawable inside imageViews), I use setColorFilter method. For example, if I have a black icon 100% opacity, using this method I can get an icon with some color at 100% opacity. Combining it with setImageAlpha or setAlpha I can get all icons in all colors in all opacities. If this works for you, tell me to add to an answer. – JavierSegoviaCordoba Apr 01 '15 at 14:37
  • did you try to call invalidateSelf() on outer LayerDrawable? – pskink Apr 01 '15 at 14:40
  • @pskink: i tried to call invalidateSelf() on both drawables and there is no changes – nicolas Apr 01 '15 at 14:48
  • @Dahnark: if I use setColorFilter instead of setColor, my button has no color anymore. `background.SetColorFilter(new PorterDuffColorFilter(Android.Graphics.Color.Red, PorterDuff.Mode.Multiply));` – nicolas Apr 01 '15 at 14:54
  • No no, setColorFilter works over the first color. My icons are black (they are .png), then setColorFilter change this flat black to other flat color. – JavierSegoviaCordoba Apr 01 '15 at 14:57
  • I see :-) Thank you very much. It works. Then how I can preserve the black shadow (black 30%). The color filter should not modify this color. – nicolas Apr 01 '15 at 16:03
  • you can use for example ColorMatrixColorFilter – pskink Apr 01 '15 at 16:15

1 Answers1

-1

In fact for 2) one really simple solution is:

instead of trying to customize the drawable coming from the xml file:

 LayerDrawable button = (LayerDrawable)Resources.GetDrawable(Resource.Drawable.normal_question_button);
GradientDrawable background = (GradientDrawable)button.FindDrawableByLayerId(Resource.Id.background);
background.SetColor(Android.Graphics.Color.Red.ToArgb());

We can change directly the color on each button once we have an instance for them:

LayerDrawable buttonDrawable = _button.Background;
GradientDrawable background = (GradientDrawable)buttonDrawable.FindDrawableByLayerId(Resource.Id.background);
background.SetColor(Android.Graphics.Color.Red.ToArgb());
nicolas
  • 489
  • 1
  • 3
  • 22