Search code examples
javaandroidandroid-drawable

android Drawable - getConstantState.newDrawable() vs mutate()


In android I have read a few articles on how drawables share a constant state. So if you make a change to a drawable it affects all the same bitmaps. For example lets say you had a list of star drawables. changing the alpha on one will change all the star drawables alpha. but you can use mutate to get your own copy of a drawable with no shared state.
The article I was reading is here

Now onto my question:

What is the difference between the following two calls in android:

Drawable clone = drawable.getConstantState().newDrawable();

// vs

Drawable clone = (Drawable) drawable.getDrawable().mutate();

For me they are both cloning a drawable as they both return a drawable that has no shared state. Am I missing something ?


Solution

  • As @4castle pointed in comments mutate() method returns same instance of the drawable with copied constant drawable state. Docs says that

    A mutable drawable is guaranteed to not share its state with any other drawable

    So it is safe to change a drawable without affecting drawables with the same state

    Lets play with this drawable - a black shape

     <!-- shape.xml -->
    <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
        <solid android:color="@android:color/black" />
    </shape>
    


    view1.setBackgroundResource(R.drawable.shape); // set black shape as a background
    view1.getBackground().mutate().setTint(Color.CYAN); // change black to cyan
    view2.setBackgroundResource(R.drawable.shape); // set black shape background to second view
    


    The opposite method is newDrawable(). It creates a new drawable but with the same constant state. E.g. look at BitmapDrawable.BitmapState:

        @Override
        public Drawable newDrawable() {
            return new BitmapDrawable(this, null);
        }
    

    Changes to new drawable will not affect current drawable, but will change a state:

    view1.setBackgroundResource(R.drawable.shape); // set black shape as background
    Drawable drawable = view1.getBackground().getConstantState().newDrawable();
    drawable.setTint(Color.CYAN); // view still black
    view1.setBackground(drawable); // now view is cyan
    view2.setBackgroundResource(R.drawable.shape); // second view is cyan also