Tint icon with gradient programatically in Android

Viewed 498

I have an icon which must be tinted programatically. I receive from back-end two colors as string (for example "#FFFFFF" and "#AAAAAA"), one of them will be used as startGradient value, the another one as endGradient value.

The next lines create the gradient programatically

val startGradientColor = Color.parseColor(it.gradientStart)
val endGradientColor = Color.parseColor(it.gradientEnd)
val grad = GradientDrawable(TOP_BOTTOM, intArrayOf(startGradientColor, endGradientColor))
grad.cornerRadius = 0f

Once the gradient created, I only need to tint the icon. I tried setting gradient as background, but such method colors the background but not the icon. Below, the code I tried.

binding.leagueBadge.background = grad

I hope you guys have the solution. Thanks in advance

1 Answers

Well guys, I have found a tricky solution for this. However, I hope Android implements soon a clean and direct way to do this.

Lets see the solutions:

For the case of setting gradient programatically to an image, I redrew the image into imageview. The method below casts imageview to bitmap, setting height and width manually (these values must keep the aspect ratio of the original asset in order to not modify the original asset) and passing gradient colors. Colors is an object I created which contains to strings (gradientStart and gradientEnd)

fun setImageWithGradient(imageview: ImageView, width : Int, height : Int, colors: Colors) {
        val myBitmap = (imageview.drawable).toBitmap(width, height)
        val newBitmap = addGradient(myBitmap, colors)
        imageview.setImageDrawable(BitmapDrawable(resources, newBitmap))
    }

The method above redraws the bitmap received, applies the gradient (colors object I mentioned above) and return the bitmap (asset with the gradient applied) Then you may set this bitmap returned into the imageview desired.

fun addGradient(originalBitmap: Bitmap, colors: Colors): Bitmap? {
        val width = originalBitmap.width
        val height = originalBitmap.height
        val updatedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(updatedBitmap)
        canvas.drawBitmap(originalBitmap, 0.toFloat(), 0.toFloat(), null)
        val paint = Paint()
        val shader = LinearGradient(0.toFloat(), 0.toFloat(), 0.toFloat(), height.toFloat(), Color.parseColor(colors.gradientStart), Color.parseColor(colors.gradientEnd), Shader.TileMode.CLAMP)
        paint.shader = shader
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
        canvas.drawRect(0.toFloat(), 0.toFloat(), width.toFloat(), height.toFloat(), paint)
        return updatedBitmap
    }

For the case of applying gradient to CardView background, I didnt find different way but adding a RelativeLayout inside the CardView, with size matching parent's one. Then, applying the gradient not to CardView's background, but to RelativeLayout's background.

The method below is the one doing this:

private fun setMedalRankingColor(gradientColors: Colors) {
        val color = intArrayOf(Color.parseColor(gradientColors.gradientStart), Color.parseColor(gradientColors.gradientEnd))
        val gradient = GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, color)
        gradient.cornerRadius = 0f
        binding.medal.background = gradient
    }

I hope helps someone, and feel fre to share your solution if better than this.

CHEERS!

Related