In Android development, tinting drawables is a common task used to dynamically change the color of image assets. Tinting can be applied programmatically using Kotlin or declaratively via XML. Understanding how to tint drawables in both Kotlin and XML (using android:tint) is essential for creating flexible and visually appealing UIs. This guide delves into how to achieve this using both methods with practical examples.
Why Tint Drawables?
- Dynamic Theming: Allows you to adapt the color scheme based on the application’s theme or user preferences.
- Visual Feedback: Provides immediate visual feedback on user interactions, such as button presses.
- Reduced Asset Size: Instead of creating multiple image assets for different colors, you can reuse a single asset and apply different tints.
Tinting Drawables Programmatically in Kotlin
Step 1: Prepare Your Drawable
First, make sure you have a drawable asset in your res/drawable directory. For example, ic_vector_asset.xml:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
</vector>
Step 2: Tint the Drawable Programmatically
You can tint this drawable programmatically using ContextCompat and DrawableCompat:
import android.graphics.drawable.Drawable
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import android.widget.ImageView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView: ImageView = findViewById(R.id.myImageView)
val drawable: Drawable? = ContextCompat.getDrawable(this, R.drawable.ic_vector_asset)
drawable?.let {
val wrappedDrawable = DrawableCompat.wrap(it)
DrawableCompat.setTint(wrappedDrawable, ContextCompat.getColor(this, R.color.my_tint_color))
imageView.setImageDrawable(wrappedDrawable)
}
}
}
In this example:
- We first get a reference to the
ImageViewfrom the layout. - We load the drawable using
ContextCompat.getDrawable. - Then, we use
DrawableCompat.wrapto ensure the drawable can be tinted across different Android versions. - We set the tint using
DrawableCompat.setTint, providing a color resource obtained withContextCompat.getColor. - Finally, we set the tinted drawable to the
ImageView.
Define the color in your res/color/my_tint_color.xml:
<color xmlns:android="http://schemas.android.com/apk/res/android">
#FF0000
</color>
Alternative Approach Using ColorFilter
Another way to tint drawables programmatically is by applying a ColorFilter:
import android.graphics.PorterDuff
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import android.widget.ImageView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView: ImageView = findViewById(R.id.myImageView)
val drawable = ContextCompat.getDrawable(this, R.drawable.ic_vector_asset)
drawable?.let {
it.setColorFilter(ContextCompat.getColor(this, R.color.my_tint_color), PorterDuff.Mode.SRC_IN)
imageView.setImageDrawable(it)
}
}
}
In this example:
- We load the drawable as before.
- We apply a
ColorFilterto the drawable usingsetColorFilter.PorterDuff.Mode.SRC_INspecifies that the tint color should be applied only to the opaque parts of the drawable. - Finally, we set the modified drawable to the
ImageView.
Tinting Drawables via XML (android:tint)
You can also tint drawables directly in XML using the android:tint attribute. This method is simpler and more declarative, but it’s less flexible than doing it programmatically.
Step 1: Set Up Your ImageView
In your layout file (e.g., activity_main.xml), add an ImageView and use the android:tint attribute:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/myImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_vector_asset"
android:tint="@color/my_tint_color"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Here, the android:tint attribute is set to the color resource @color/my_tint_color. When the layout is rendered, the drawable will be tinted with the specified color.
Compatibility Notes
The android:tint attribute is available starting from API level 21 (Android 5.0 Lollipop). For backward compatibility, use the androidx.appcompat.widget.AppCompatImageView instead of the standard ImageView, along with the app:tint attribute from the app namespace:
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/myImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_vector_asset"
app:tint="@color/my_tint_color"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
Ensure that you have added the app namespace to your root layout element:
xmlns:app="http://schemas.android.com/apk/res-auto"
Example: Button Tinting
Tinting can also be applied to buttons. In your XML layout, define a button and apply a tint:
<Button
android:id="@+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tinted Button"
android:drawableLeft="@drawable/ic_vector_asset"
android:tint="@color/my_tint_color"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
Alternatively, use android:drawableTint for more control over individual drawables of the button:
<Button
android:id="@+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tinted Button"
android:drawableLeft="@drawable/ic_vector_asset"
android:drawableTint="@color/my_tint_color"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
Conclusion
Tinting drawables programmatically using Kotlin and declaratively using XML (android:tint) are powerful techniques for enhancing the visual elements of your Android application. Programmatic tinting provides greater flexibility for dynamic changes, while XML tinting offers simplicity and ease of use for static configurations. Understanding both approaches ensures you can efficiently manage and customize the appearance of your drawables to create a polished user interface. Using AppCompatImageView helps provide backward compatibility for android:tint.