23.2.0 set vector drawable as background in 4.X - android

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?

Related

How to apply vector drawable to an image programmatically?

I have a bunch of SVG images(5) that I've converted to vector drawables in Android Studio.
I need to hardcode them in my adapter but I can't figure out how to apply.
Using a regular image(a .png or jpg) this is how I would put them in an array.
int[] images = {R.drawable.image_one.png,R.drawable.image_two.png};
and apply to the image like below:
imageView.setImageResource(images[position]);
how can I achieve the same with vector drawables(the extension file ends with .xml)
Using getResources() method as follows
imageView.setImageResource(getResources().getDrawable(R.drawable.image_one));
Both png drawables and vector drawables can be set like this:
Say, on file system your drawable is hello.xml or hello.png
You set it like imageView.setImageResource(R.drawable.hello) in both cases (you don't have to specify extensions)
I would recommend using #DrawableRes annotation next to your type definition, for better compiler checks (#DrawableRes int [] images = ...)

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"/>

Programmatically tint a Support Vector

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)

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

Categories

Resources