18

I'm building an app that has plenty of screens. most of the screens have a View at the top with a background color.

I often change that color using view.setBackgroundColor(color)

Here comes the weird thing: sometimes after setting the color of one view, say to #f14fb7 , when navigating in the app, other views' backgrounds are set to that color without me wanting them to. It sometimes even happens to views I had not set an id for, so there is no way setBackgroundColor was called on those views.

This happens rarely and is not consistant to any flow I tried.

When I kill the app and restart it, everything works as it should.

My only guess is some king of memory leak, but I hope there is a simpler explanation.

Can anyone think of some reason for this to happen?

*It happens on my Galaxy S3.

dors
  • 5,802
  • 8
  • 45
  • 71
  • 2
    We need to see some code to understand how exactly you are laying out your screens and the views on top. – Szymon Rozga Feb 14 '13 at 12:37
  • Well may be that you have the outer most background with a color and then you have other layouts on it with background transparent or something? – Nezam Feb 14 '13 at 12:41
  • I don't have code to post, since this happens throughout the app. Nezam, it's nothing like that... 95% of the time this problem does not happen. but the code for setting the background is view.setBackgroundColor(color)... nothing special. the weird thing is that it affects other screens that have nothing to do with that particular screen... that's why I suspect it is a memory leak – dors Feb 14 '13 at 12:51
  • If it's any consolation, I've noticed this issue on my Galaxy S3 as well. Random views throughout the app, with no relation to one another, randomly turn one color. Cannot figure out why. – jmhend Mar 27 '14 at 23:45

3 Answers3

15

Without the code it's not easy... but I guess you are reusing the same ColorDrawable on multiple views and if you take a look at View.setBackgroundColor() source code :

public void setBackgroundColor(int color) {
    if (mBGDrawable instanceof ColorDrawable) {
        ((ColorDrawable) mBGDrawable).setColor(color);
    } else {
        setBackgroundDrawable(new ColorDrawable(color));
    }
}

You can see that it change the color of the ColorDrawable and don't create a new one each time. I'm pretty sure this is why you have this strange behavior.

EDIT

When you set the initial background color in xml with android:background you are doing this (according android doc):

Set the background to a given resource. The resource should refer to a Drawable object

According my understanding it will set the field View.mBGDrawable during the inflate. I suggest you to use View.setBackgoundDrawable(new ColorDrawable(the_color_int_code))) instead of setBackgroung(the_color_int_code). It should solve your issue.

ben75
  • 29,217
  • 10
  • 88
  • 134
  • Why do you think I use the same ColorDrawable on multiple views? As you can see in the source code, all I do is pass a color (int), and the View class created a new ColorDrawable instance if needed. Further more, when this strage behaviour occurs, if I debug the views color it seems that it has the correct color set, when in fact a different color is shown. – dors Feb 14 '13 at 17:16
  • to validate or not my suggestion : when debugging note the reference of 'mBGDrawable' of 2 views displaying wrongly the same background. If this is the same reference, my suggestion is right. – ben75 Feb 14 '13 at 20:18
  • And if you are right, how can I fix this? I am not the creating a new ColorDrawable instance, the View class does that – dors Feb 15 '13 at 06:26
  • Your views are declared in xml ? or do you create them programmatically ? If you declare them in xml: how do you specify the initial background color ? – ben75 Feb 15 '13 at 08:47
  • I declare them in xml. the initial color is set via the android:background xml attribute – dors Feb 16 '13 at 18:41
  • I have this exact same issue (but at least I'm glad to know I'm not the only one facing it) in multiple elements of my app. I figured that 2 elements (in particular) located in different screens where changing to the same color but resource names are different, values on the R.java file are different...I'm out of ideas :( I'm using Google APIS and testing on ICS (4.0.3) – Paulina D. Feb 21 '13 at 22:21
  • @Paulina D did you try to change the color with something like `setBackgoundDrawable(new ColorDrawable(the_color_int_code)))` – ben75 Feb 22 '13 at 07:41
  • I'm actually trying with drawables alone (because I'm using normal rectangular buttons, I created solid pngs), this seems so stop the rainbow that was being displayed :P. We discussed the issue a little bit in the Android chat and the latest explanation is that there must be some bug in the layout managers on Android. – Paulina D. Feb 22 '13 at 18:48
  • @ben75 I replaced all my calls of `setBackgroundColor(COLOR)` for `setBackground(new ColorDrawable(COLOR))`, not sure if it worked as I cannot reproduce the error consistently. If the color change doesn't bug in a week I will let you know. By the way, the client's phone is a Galaxy S III mini (GT-I8190L) – SparK Sep 10 '13 at 13:40
  • @ben75 I can confirm your solution works for my case which was 100% reproducible – Catalin Iacob Sep 26 '13 at 12:04
  • i set the background from my xml so what should i do? – Karthika PB May 06 '15 at 07:28
  • 1
    @KarthikaPB this problem will only manifest when you change one of the backgrounds at runtime. At that point, you can create a new `ColorDrawable(int color)` and set that, or if you are applying a filter to the background, remember to call `.mutate()` on the background before applying the filter. – ataulm Oct 21 '15 at 07:10
6

This usually happens if you have a view whose color is set in xml ex:

android:background="@color/cyan" 

Now this internally creates a new ColorDrawable - lets call it conceptual_drawable_cyan inside that view's class. Now when same view is assigned a different color programmatically using:

view.setBackgroundColor(newColor);

Internally this view instead of creating a different drawable it sets this newColor to drawable_cyan. Hence from this point onwards if you anywhere use

android:background="@color/cyan" 

the view would actually use conceptual_drawable_cyan with newColor.

Solution:

Instead of using setBackgroundColor to set color programmatically, use:

    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        view.setBackground(new ColorDrawable(newColor));
    } else {
        view.setBackgroundDrawable(new ColorDrawable(newColor));
    }
Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
Aalap
  • 2,847
  • 2
  • 26
  • 24
1

Create the "colors.xml" file under the "values" folder. Example:

<?xml version="1.0" encoding="utf-8"?>
<resources><color name="pink">#f14fb7</color></resources>

Use view.setBackgroundResource(R.color.pink);

Andrea Motto
  • 1,540
  • 21
  • 38