Search code examples
androidimageviewtint

Set a Stateful Tint for an ImageView Programatically


I am trying to update the tint of an ImageView programatically, and the colour I'm using is a selector that has different colours for when the view is enabled or disabled. When I try and use the suggested methods from other StackOverflow posts it set the enabled colour but does not update for views that are disabled

For example, I have the following four dots that are coloured red in XML, and I'm setting the bottom two to be green in code. The right two dots are disabled so I'd expect both to be the same disabled grey but as you can see the programatically set colour is always green

https://i.sstatic.net/tFpgp.png

How can I programatically update the tint of an ImageView so that it correctly follows the state in the selector?

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <!-- Also tried making these AppCompatImageViews -->
    <ImageView
        android:id="@+id/imgDot1"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:tint="@color/color_red_stateful"
        android:src="@drawable/ic_dot" />

    <ImageView
        android:id="@+id/imgDot2"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:tint="@color/color_red_stateful"
        android:src="@drawable/ic_dot" />
</LinearLayout>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/imgDot3"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:tint="@color/color_red_stateful"
        android:src="@drawable/ic_dot" />

    <ImageView
        android:id="@+id/imgDot4"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:tint="@color/color_red_stateful"
        android:enabled="false"
        android:src="@drawable/ic_dot" />
</LinearLayout>
imgDot2.isEnabled = false
imgDot4.isEnabled = false

// Tried each of these in turn
ImageViewCompat.setImageTintList(imgDot3, ColorStateList.valueOf(resources.getColor(R.color.color_green_stateful)))
ImageViewCompat.setImageTintList(imgDot4, ColorStateList.valueOf(resources.getColor(R.color.color_green_stateful)))

imgDot3.setColorFilter(ContextCompat.getColor(this, R.color.color_green_stateful))
imgDot4.setColorFilter(ContextCompat.getColor(this, R.color.color_green_stateful))

imgDot3.imageTintList = ColorStateList.valueOf(resources.getColor(R.color.color_green_stateful))
imgDot4.imageTintList = ColorStateList.valueOf(resources.getColor(R.color.color_green_stateful))

imgDot3.imageTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_green_stateful))
imgDot4.imageTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_green_stateful))

color_red_stateful.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false" android:color="#999"/>
    <item android:color="#F00"/>
</selector>

color_green_stateful.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false" android:color="#999"/>
    <item android:color="#0F0"/>
</selector>

ic_dot.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#000"
        android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
</vector>

Solution

  • You are using getColor method in every tried line, but this isn't proper, that's not color, that's selector (color_red_stateful and color_green_stateful files). use Resources or preferably ContextCompat class and its useful method getColorStateList

     ColorStateList colorStateList = ContextCompat.getColorStateList(this, R.color.your_color_selector);
    

    this colorStateList should be set as imageTintList for desired ImageViews

    btw. after setting above you may use view.invalidate() method for forcing immediate apply of changes (force redraw), but in most cases, also probably yours, this won't be needed