Set a Stateful Tint for an ImageView Programatically

2.8k Views Asked by At

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.stack.imgur.com/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>
2

There are 2 best solutions below

1
snachmsm On BEST ANSWER

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

0
Shailendra Madda On

I am trying to update the tint of an ImageView programmatically, and the colour I'm using is a selector that has different colours for when the view is enabled or disabled

Option1:

As I see you already set android:tint for each view in your XML file.

so to do enable disable programmatically you need to update the views after enable or disable.

Set isEnabled to true or false first:

imgDot2.isEnabled = false
imgDot4.isEnabled = false

Invalidate views:

imgDot2.invalidate()
imgDot4.invalidate()

Then it will update the views based on your input.

No need to set setImageTintList or setColorFilter again in Activity.

Option2:

Want to set color programmatically then

ImageViewCompat.setImageTintList(imgDot2, ContextCompat.getColorStateList(this, R.color.color_green_stateful))
ImageViewCompat.setImageTintList(imgDot4, ContextCompat.getColorStateList(this, R.color.color_green_stateful))

Invalidate views:

imgDot2.invalidate()
imgDot4.invalidate()

Note: you must use app:tint instead of android:tint in your XML.