Programmatically tint a Support Vector - android

Android Studio version 2.1, gradle version 2.1.0, please correct me if you spot any misinterpretations :)
I am confused about support vectors in the support library 23.3.0. Specifically what I would like to do is tint an image button programmatically, whose src is defined is a vector drawable. From what I can tell this is not possible on pre-lollipop now.
I have read several related posts about the changes:
23.2.0 announcement and changes:
As of Android Support Library 23.3.0, support vector drawables can only be loaded via app:srcCompat or setImageResource().
Does the above mean that vector xmls can only be used pre-Lollipop via srcCompat or setImageResource(), and therefore cannot be dynamically tinted?
Here is my basic image button:
<ImageButton
android:id="#+id/nav_header_exit_community_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:background="#null"/>
Works on Lollipop and above only:
Drawable bg = ContextCompat.getDrawable(a, R.drawable.ic_exit_to_app_24dp);
DrawableCompat.setTint(bg, headerTitleColor);
exitButton.setImageDrawable(bg);
Attempting this pre-lollipop throws:
android.content.res.Resources$NotFoundException: File res/drawable/ic_exit_to_app_24dp.xml from drawable resource ID #0x7f0200bf
Also works on Lollipop and above only
Drawable bg = ContextCompat.getDrawable(a, R.drawable.ic_exit_to_app_24dp);
DrawableCompat.setTint(bg, headerTitleColor);
exitButton.setImageResource(R.drawable.ic_exit_to_app_24dp);
This throws the same error on pre-Lollipop.
However if I remove vectorDrawables.useSupportLibrary = true as pointed out by Ian Lake here, with the intent of having the build tools auto-generate pngs for pre-Lollipop devices, the pngs do not tint on pre-lollipop, so I'm back to square one.
I have also tried specifying the vector via srcCompat and retrieving it programmatically but I don't think I've been able to achieve that, even though it works on post-Lollipop if the vector is specified using src instead.
So the situation for 23.3.0 seems to be:
Post-Lollipop: src and srcCompat accept vectors, only src can be
retrieved from the view as a drawable for tinting programmatically.
Referencing vectors in code is possible using getDrawable, and they
can be tinted.
Pre-Lollipop: srcCompat only can accept vectors, cannot be retrieved
programmatically from the view for tinting. setImageResource can
accept vectors, but only if vectorDrawables.useSupportLibrary = false, and tinting does not work. Similarly referencing vectors in code is not
possible unless vectorDrawables.useSupportLibrary = false and tinting
does not work.
Working on all versions using pngs:
Drawable bg = ContextCompat.getDrawable(a, R.drawable.ic_nav_exit_community);
DrawableCompat.setTint(bg, headerTitleColor);
exitButton.setImageDrawable(bg);
Addendum:
This technique also works on post-Lollipop, but like the others on pre-Lollipop I get the drawable, but no tinting:
Drawable bg = VectorDrawableCompat.create(a.getResources(), R.drawable.ic_exit_to_app_24dp, null);
DrawableCompat.setTint(bg, headerTitleColor);
exitButton.setImageDrawable(bg);
KIND OF SOLUTION:
Thanks to John's answer so far the only fool-proof way I can come up with to tint a support vector is to set a color filter on it - this means the DrawableCompat.setTint() function is seemingly not functional for me if the drawable in question is a support vector. I'm not sure if this is a legit bug, expected behavior or if I'm just doing something wrong!
Here is the solution I'm going with for the moment:
Drawable bg;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
bg = VectorDrawableCompat.create(a.getResources(), R.drawable.ic_exit_to_app_24dp, null);
exitButton.setColorFilter(headerTitleColor, PorterDuff.Mode.MULTIPLY);
}
else {
bg = ContextCompat.getDrawable(a, R.drawable.ic_exit_to_app_24dp);
DrawableCompat.setTint(bg, headerTitleColor);
}
exitButton.setImageDrawable(bg);

first of all you should use VectorDrawableCompat#create, once you have your Drawable you have to call DrawableCompat#wrap:
Potentially wrap drawable so that it may be used for tinting across
the different API levels, via the tinting methods in this class.
so your code would look like this:
ImageView iv = ....
Drawable d = VectorDrawableCompat.create(getResources(), R.drawable.ic_exit_to_app_24dp, null);
d = DrawableCompat.wrap(d);
DrawableCompat.setTint(d, headerTitleColor);
iv.setImageDrawable(d);

You can use setColorFilter method of ImageView:
imageView.setColorFilter(headerTitleColor, android.graphics.PorterDuff.Mode.MULTIPLY);

Another handy solution with Kotlin:
fun Context.drawableWithColor(#DrawableRes drawableRes: Int, #ColorInt color: Int): Drawable? {
val pic = ContextCompat.getDrawable(this, drawableRes)
pic?.setColorFilter(color, PorterDuff.Mode.SRC_IN)
return pic
}
Use is as simple as:
val drawable = context.drawableWithColor(R.drawable.your_awesome_drawable, Color.BLUE)

Related

Set vector drawable programmatically pre Lollipop

Very common problem with a twist to which I couldn't find a solution for.
I am setting my vector programmatically. I want to be able to change the tint color programmatically too.
Found some solutions such as
Programmatically tint a Support Vector
ImageView iv = ....
Drawable d = VectorDrawableCompat.create(getResources(), R.drawable.ic_exit_to_app_24dp, null);
d = DrawableCompat.wrap(d);
DrawableCompat.setTint(d, headerTitleColor);
iv.setImageDrawable(d);
The main problem comes with
iv.setImageDrawable(d);
I found that prelolipop only accepts setting view's drawable with
iv.setImageResource(int resource)
I couldn't find any solutions for setting it with a drawable file.
Use AppCompatImageView which has setImageDrawable() method.

Can provided Android SDK icons be set with color?

Android SDK provides the following icons.
Is there a way to set a color to those .. and if possible, how to do so?
<ImageView
android:id="#+id/share_button"
style="#style/IconNav"
android:src="#android:drawable/ic_menu_share"/>
<ImageView
android:id="#+id/bookmark_button"
style="#style/IconNav"
android:src="#android:drawable/ic_input_get"/>
UPDATE
After doing a complete refresh of the project, it turns out the tint attribute in Xml did the trick.
For the short answer
.. this is the solution that worked for me - adding the property to the ImageView xml:
android:tint="#color/grass_dark"
The answer from #goldenb is a thorough run through of the different ways to solve for this, so am marking that one as the answer.
You can indeed use a tint as a way of changing an ImageView's colour, BUT you should be reminded that the android:tint will always be applied on top of the original colour.
as stated by blogger danlew
ImageView's tint mixes the tint color with the original asset. What you want is for the tint color to take over entirely; instead it
applies the tint on top of the existing color. So, for example, if the
source asset is black, and you want it to be #77FFFFFF (a translucent
shade of white), you'll actually end up getting that shade of white
with a black background beneath it.
android:tint is limited to ImageView. You want to be able to tint any Drawable in any View.
One possible alternative would be for you to use android ColorFilter
According to the official documentation:
A color filter can be used with a Paint to modify the color of each pixel drawn with that paint. This is an abstract class that should never be used directly.
There are lots of more or less complex things you can do with ColorFilter but how can you apply this then?
One simple example from another so question is:
//White tint
imageView.setColorFilter(Color.argb(255, 255, 255, 255));
or
imageView.setColorFilter(ContextCompat.getColor(context,R.color.COLOR_YOUR_COLOR))
Or a more complete answer here in SO from here
ImageView redCircle = (ImageView) findViewById(R.id.circle_red_imageview);
ImageView greenCircle = (ImageView) findViewById(R.id.circle_green_imageview);
ImageView blueCircle = (ImageView) findViewById(R.id.circle_blue_imageview);
// we can create the color values in different ways:
redCircle.getDrawable().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY );
greenCircle.getDrawable().setColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY );
blueCircle.getDrawable().setColorFilter(getResources().getColor(R.color.blue), PorterDuff.Mode.MULTIPLY );
You should check these links if you want to learn more
SO - What is the difference between background, backgroundTint, backgroundTintMode attributes in android layout xml?
setColorFilter()
Fast Android asset theming with ColorFilter
SO-Modifying the color of an android drawable
You can use a bitmap with a tint. Add this to your drawables folder.
ic_input_get_colored.xml :
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#android:drawable/ic_input_get"
android:tint="#color/yourDesiredColor"/>

23.2.0 set vector drawable as background in 4.X

I am super excited about the new possibility to set vector drawables to layouts using the app:srcCompat="#drawable/icon" attribute introduced in support-library 23.2.0.
But I wonder how I can fetch one of this drawables programmatically or set is as background.
I thought about something like:
ContextCompat.getDrawable(context, R.drawable.icon)
Is this even possible?
Okay, I managed it myself. Thanks for #Budius for pointing me in the right direction.
The Answers lays in VectorDrawable#create()
Resources resources = context.getResources(Resources, int, Theme);
Theme theme = context.getTheme();
Drawable drawable = VectorDrawableCompat.create(resources, R.drawable.drawable, theme);
More input:
How to use vector drawables in Android API lower 21?

Android CardView setBackground as BitmapDrawable when API <21

I have a problem when I am trying to setBackground() of a CardView programatically in KitKat.When the application is running on Android L there is no problem in my code :
Drawable drawable = new BitmapDrawable(appContext.getResources(), bitmap);
if (cardView != null) {
cardView.setBackground(drawable);
}
But when it runs in API older than Android L I get this error: android.graphics.drawable.BitmapDrawable cannot be cast to android.support.v7.widget.RoundRectDrawableWithShadow.
I found out that this is known issue and there is work around where you want to set a colour as a background of the card, but is there any solution with which I could set the bitmap for background programatically? Do you have any idea how could I create RoundRectDrawableWithShadow from my BitmapDrawable?
Do you have any idea how could I create RoundRectDrawableWithShadow from my BitmapDrawable?
Don't do it unless you wish to rewrite your own CardView implementation.
RoundRectDrawableWithShadow is a package private class used to take the color you supplied by app:cardBackgroundColor or setCardBackgroundColro(int) and wrap it in a rounded rectangle with shadow to produce a drawable.
Since elevation was introduced in API 21 the shadow is painted as part of CardView background drawable on prior platforms so you can't set your own background via setBackground(*).
is there any solution with which I could set the bitmap for background programatically?
You could put a View, *Layout, etc. in the card and set a rounded drawable background on it. It will not be effective performance-wise though.
https://developer.android.com/reference/android/support/v4/graphics/drawable/RoundedBitmapDrawable.html
So yeah, if you absolutely need something else than solid color, go with a View.setBackground

Dynamically change SVG image color in android

I know that using third party library, it is possible to use SVG image in Android.
Library like: svg-android
The code to load SVG image is like below:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a new ImageView
ImageView imageView = new ImageView(this);
// Set the background color to white
imageView.setBackgroundColor(Color.WHITE);
// Parse the SVG file from the resource
SVG svg = SVGParser.getSVGFromResource(getResources(), R.raw.android);
// Get a drawable from the parsed SVG and set it as the drawable for the ImageView
imageView.setImageDrawable(svg.createPictureDrawable());
// Set the ImageView as the content view for the Activity
setContentView(imageView);
}
It's working fine. I'm able to see the image. But now I want to change the color for the svg image at runtime.
For that I tried the code below as mentioned in the same project description.
// 0xFF9FBF3B is the hex code for the existing Android green, 0xFF1756c9 is the new blue color
SVG svg = SVGParser.getSVGFromResource(getResources(), R.raw.android, 0xFF9FBF3B, 0xFF1756c9);
But with that I am not able to see the change in the color. So I would like to know how it is possible to change the color dynamically in Java file.
I know it's kind of late but I also had this issue and was able to fix this issue using the setColorFilter(int color, PorterDuff.Mode mode) method.
Example:
imageView.setColorFilter(getResources().getColor(android.R.color.black), PorterDuff.Mode.SRC_IN);
I got where is the problem.
The issue is with the color code i am using in svg file.
Its not exactly 0xFF9FBF3B but #9FBF3B
But during java code you have to write it with ARGB value (e.g. 0xFF9FBF3B).
I have updated it and its work fine now. I can able to change the color of svg file with same code.
Hope this will also help others to identify the actual case while changing the color of the SVG image at runtime.
Using the answer of Antlip Dev in Kotlin.
package com.example.... // Your package.
import android.graphics.PorterDuff
import android.widget.ImageView
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
fun ImageView.setSvgColor(#ColorRes color: Int) =
setColorFilter(ContextCompat.getColor(context, color), PorterDuff.Mode.SRC_IN)
Usage:
view.your_image.setSvgColor(R.color.gray)
what #Antlip Dev said is correct, but that method is deprecated now.
This is the updated version:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
drawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.SRC_ATOP));
} else {
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
You can use: drawableTint to change the color
This worked for me DrawableCompat.setTint(imageView.getDrawable(),ContextCompat.getColor(getApplicationContext(), R.color.colorAccent))

Categories

Resources