Understanding Android's VectorDrawable properties - android

I need help understanding some of the properties of VectorDrawable that is defined by XML.
When i import a new vector asset using Android Studio, It generates something like:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#000000"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
</vector>
By default the width, height, viewportHeight and viewportWidth are being set to some default Material design value.
Since we are dealing with vectors and not pixel images. My questions are:
why is it required to set a size (width, height, etc)?
If i need to use the same vector xml for multiple sizes, how to override this 24dp value without creating multiple vector xml's which of course defeats the whole purpose!

Adding #dimen in xml vector gives error
Error:Error: Width (0) and height (0) cannot be <= 0

VectorDrawables can be used in places where you would previously have used a png. If the VectorDrawable didn't have a width and height you would be creating a whole new problem, if you didn't define the size of the VectorDrawable as part of the original xml, you would have to make sure it was defined every time you wanted to use it.
This would make using a VectorDrawable a lot different to using other drawables, for example using "wrap_content" would not work, because you are relying on the 'content' to define what size things should be. That means you wouldn't be able to just replace a png with a VectorDrawable like you can now, it would cause parts of your app to break.
If you want to make your VectorDrawable a different size for different device configurations you can define it by using a reference to resources.
<vector ...
android:width="#dimen/vector_size"
android:height="#dimen/vector_size"
.../>
Then defining this in res/values:
<resources>
<dimen name="vector_size">24dp</dimen>
</resources>
You can then specify vector_size as a different value for different device configurations in the usual way.
If you want to use the same VectorDrawable with multiple sizes on the same device, that's a bit more difficult at the moment and you may be best to create multiple versions. If doing this you may consider moving the pathData to a string resource:
<path ...
android:pathData="#string/vector_path_name"/>
This way, even though you have multiple VectorDrawables, they all refer to the same pathData that defines their shape and if you change that string, you'll be updating all of the versions.

The size defined in XML is only the "intrinsic" size for the drawable. You can draw it at any size you want. If you intend to draw the same vector at multiple sizes, you should inflate a separate instance of VectorDrawable for each size, and call setBounds(0,0,desiredWidth,desiredHeight) on each. You may already be used to having to call setBounds for many usages.

viewportHeight and viewportWidth define the resolution of image and height and width defines how much space it will take on screen when rendered.
All you need to know is:
if you want to display an high quality image on small area like 54x54 then you should get a high quality image like 380x380 or 512x512 then set its width and height to 54x54, otherwise your image wont have smooth corners and it wont look good

Related

How to let ImageView dictate size of custom Drawable?

Im working on a initials drawable, sort of like gmail has.
I want this drawable to scale automatically to image view size (which will be hardcoded in xml, but multiple variants), therefore I dont want the initials drawable to have hardcoded size as well, which is to me seems is what getIntrinsicWidth() does.
Is there a way to do this? Is there way then for drawable to get the image view size, to do its calculations for rendering?
Thanks
Vector drawables can scale automatically by default. Setting android:width="24dp" for example in your drawable xml doesn't force it to be this size, it's just a default value.
Set the ImageView to the size you want, and set your drawable as the image resource. It will fill the available space.
In layout xml:
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="#drawable/your_drawable"/>
Or in java:
ImageView iv = findViewById(R.id.your_image_view);
iv.setImageResource(R.drawable.your_drawable);
Mess with android:scaleType if you want different crop behavior.

How to format/size VectorDrawable for adaptive icon in android

I am trying to create an adaptive icon for an app using two VectorDrawables for background and foreground. However the foreground vector, which was created from an svg made in Illustrator, cannot be sized or placed properly in the icon.
Foreground vector for future reference
I sized the vector viewportheight and viewportwidth to 108dp x 108dp according to the specification in the Adaptive icon guidelines, however this has only caused the foreground to be offset.
<vector android:height="108dp" android:viewportHeight="108"
android:viewportWidth="108" android:width="108dp"
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
...
</vector>
However when the viewportheight and viewportwidth are set to smaller values (eg. 50) the vector appears in the correct position but is too large.
<vector android:height="108dp" android:viewportHeight="50"
android:viewportWidth="50" android:width="108dp"
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
...
</vector>
I am new to working with android and so I am not sure what other factors may be causing this. There was a another similar question however the cause for the offsetting effect was not addressed.
Wanting to minimize my app size I attempted using the same vector for a notification as well as the icon and hit exactly the same problem.
I tried the "wizard":
Right-click the res folder and click New > Image Asset
This then lets me scale my vector on top of the background vector, but centered.
Looking at the generated vector files for the icon it nests my vector path inside a group tag with translateX and translateY attributes, like this:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="52.173912"
android:viewportHeight="52.173912">
<group android:translateX="14.086957"
android:translateY="14.086957">
<path.../>
</group>
</vector>
Try that directly in your xml if you want to avoid generating files, and deleting the generated png files afterwards etc.
That may work for you - in my case I can't use the new icon foreground vector as a notification as it is too small. So I appear to need copies of my scalable vector graphic with different sizes!
It would be ideal if we could scale the foreground/background parts of an adaptive icon in the adaptive icon file itself - perhaps somebody knows how to do that already?

How do items of a LayerList drawable scale to fit the container View?

From this developer guide about Layer-list drawables,
All drawable items are scaled to fit the size of the containing View,
by default. Thus, placing your images in a layer list at different
positions might increase the size of the View and some images scale as
appropriate.
In the first sentence, they say that the items are scaled to fit the
container view (and Not that the view is scaled according to the
size of the items contained in it). Then they say that the size of
the container view might increase (which means that the View is
being scaled, right?). So doesn't the second sentence contradict
the first one? Can somebody explain what is meant there?
android:drawable
Drawable resource. Required. Reference to a drawable resource.
...
To avoid scaling items in the list, use a
element inside the element to specify the drawable...
...
For example, the following defines an item that scales to
fit its container View:
<item android:drawable="#drawable/image" />
To avoid scaling, the following example uses a element with centered gravity:
<item>
<bitmap android:src="#drawable/image"
android:gravity="center" />
</item>
Again, they say that android:drawable is a required attribute, and then they give an example which does not use this attribute. What is correct?
To avoid scaling items in the list, use a <bitmap> element inside the
<item> element to specify the drawable and define the gravity to
something that does not scale, such as "center"
How is gravity scalable and how is center as its value make it unscalable?
It seems that layer-list items are effectively stretched in both X & Y dimensions to fit the container.
You need a drawable for each item. The bitmap child element effectively becomes the drawable for that item.
If you have an empty item (with no drawable resource), it will cause an error when you try to load the layer-list.
Gravity has several options (like FILL, which is probably the item's default gravity, or FILL_HORIZONTAL that stretch the item to fill its container).
Additionally, you can set android:gravity="center" on an item tag itself (without a <bitmap> child) and it seems to have the same effect (in API 23, at least).
In addition to what #Cristopher_Boyd said. The screen density is also important, so different bitmaps should be created for different screen densities. If there is a single image inside the drawable folder directly, it may not scale properly. Hence, generate different images, or place a single image in drawables-xxhdpi folder, for example (but not on drawables). See this answer.
Hope this helps someone,
Xavi

How to change color of vector drawable path on button click

With the new android support update, vector drawables get backward compatibility. I have a vector image with various paths. I want the color of the paths to change on click of a button or programmatically based on an input value. Is it possible to access the name parameter of the vector path? And then change the color.
The color of the whole vector can be changed using setTint.
You have to set up your ImageView in your layout file as this:
<ImageView
android:id="#+id/myImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tint="#color/my_nice_color"
android:src="#drawable/ic_my_drawable"
android:scaleType="fitCenter" />
Then to change the color of your image:
DrawableCompat.setTint(myImageView.getDrawable(), ContextCompat.getColor(context, R.color.another_nice_color));
Note: myImageView.getDrawable() gives nullpointerexception if the vector drawable is set to the imageView as background.
Use this to change a path color in your vector drawable
VectorChildFinder vector = new VectorChildFinder(this, R.drawable.my_vector, imageView);
VectorDrawableCompat.VFullPath path1 = vector.findPathByName("path1");
path1.setFillColor(Color.RED);
Library is here: https://github.com/devsideal/VectorChildFinder
There are several ways of doing the same stuff, but this works for both Vector Drawables as well as SVG (Local/Network).
imageView.setColorFilter(ContextCompat.getColor(context,
R.color.headPink), android.graphics.PorterDuff.Mode.SRC_IN);
(change R.color.headPink with color of your choice)
You can change the color of individual path at runtime, without using reflection.
VectorMaster introduces dynamic control over vector drawables. Each and every aspect of a vector drawable can be controlled dynamically (via Java instances), using this library.
Just add the following dependency in your app's build.gradle
dependencies {
compile 'com.sdsmdg.harjot:vectormaster:1.0.9'
}
In your case you need a simple color change:
Vector example: your_vector.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:name="outline"
android:pathData="M20.84,4..."
android:strokeColor="#5D5D5D"
android:fillColor="#00000000"
android:strokeWidth="2"/>
XML:
<com.sdsmdg.harjot.vectormaster.VectorMasterView
android:id="#+id/your_vector"
android:layout_width="150dp"
android:layout_height="150dp"
app:vector_src="#drawable/your_drawable" />
Java:
VectorMasterView heartVector = (VectorMasterView)
findViewById(R.id.your_drawable);
// find the correct path using name
PathModel outline = heartVector.getPathModelByName("outline");
// set the stroke color
outline.setStrokeColor(Color.parseColor("#ED4337"));
// set the fill color (if fill color is not set or is TRANSPARENT, then no fill is drawn)
outline.setFillColor(Color.parseColor("#ED4337"));
From: https://github.com/harjot-oberai/VectorMaster, licensed under MIT.
You have now full control over vector drawables.
You can use this method to change color in lower API to change vector color in fragment
int myVectorColor = ContextCompat.getColor(getActivity(), R.color.colorBlack);
myButton.getIcon().setColorFilter(myVectorColor, PorterDuff.Mode.SRC_IN);
in place of getActivity you should use MainActivity.this for changing vector color in activity
Check my answer on this other question: https://stackoverflow.com/a/38418049/1335438.
It is a great idea on how to manage this by using Themes and parameterizing the paths in order to be able to set them dynamically.
As stated by #Eyal in this post
https://stackoverflow.com/a/32007436/4969047
You cannot change the color of individual path at runtime.
Looking at the source code of VectorDrawableCompat, the only method to expose the inner element by name is getTargetByName which is present in inner private state class VectorDrawableCompatState of VectorDrawableCompat.
Since it is a package private (default) - you can't use it (unless you use reflection).
DrawableCompat.setTint(imageView.getDrawable(),ContextCompat.getColor(getApplicationContext(), R.color.colorAccent));

what does android getIntrinsicHeight and getIntrinsicWidth mean?

Hi I am confused by the two methods from Android Drawable class
getIntrinsicHeight()
getIntrinsicWidth()
api definition says
http://developer.android.com/reference/android/graphics/drawable/Drawable.html#getIntrinsicHeight()
what does the word intrinsic height/width mean?
i mean is it a width of the actual image?
If you want to know the meaning of intrinsic, it is nothing but the actual property possessed by an object. In our case getIntrinsicWidth/Height simply means to provide you with the default width/height of that drawable.
This returns the exact size of the drawable which you have put in the resource folder without any modification.
Now you have to know that getWidth or getHeight will return a value which might vary according to the width and height you specify for your ImageView in your XML layout.
Let's say that you have provided the width and height of your ImageView as 100*100 in the XML layout and the drawable you used as the background is of size 200*200.
Now getIntrinsicWidth must return 200 whereas getWidth must return 100.
related question here on stackoverflow.
IF your image is downloaded from the internet, .getIntrinsicWidth() and .getIntrinsicHeight() indeed give you the "real" width and height, respectively of the image.
It's called intrinsic, because it depends ONLY on the image and on nothing else (such as your phone).
Alas, what you get is NOT intrinsic in all circumstances - it DOES depend things other than the image, unfortunately.
Here is where you get a wrong (namely, non-intrinsic) result. Let's say you are using the default launcher icon, then
Log.i("", "ic_launcher intrinsic width " + getResources().getDrawable(R.drawable.ic_launcher).getIntrinsicWidth());
will tell you the width (in pixels) of the launcher icon. But of which one? - you have several of them, one in drawable-xhdpi folder, one in drawable-hdpi folder, etc. Well, if your device is, say, xhdpi, it gives you 96, which is indeed the pixel-width of the version of the launcher icon residing in the drawable-xhdpi folder. Now, delete the icon in the drawable-xhdpi folder, and run again (still using an xhdpi device (real or emulated)). The image that will be used will be from drawable-hdpi folder, because that's "closest" to the xhdpi version. That icon has a pixel width of 72. But above code WILL STILL GIVE YOU 96!!!
That is clearly NOT "intrinsic" (in the proper sense of the word), as it does not depend only on the image used.
So if you are as lazy as I am, and are therefore not generating 4 versions of each resource icon/image (but instead using only 1 or 2, and scaling them by hand), you have to beware the mentioned androidal misnomer.
In android a drawable can be of many types such as color, bitmap, shape etc.
Some of these drawables have an intrinsic height such as a BitmapDrawable which is the dimension of the image.
Drawables such as ColorDrawable (used to draw just solid colors) don't have an intrinsic height. In this case the value of getIntrinsicHeight/Width returns -1.
Even if a drawable doesn't have intrinsic height/width, every drawable needs to have their bounds set before they can render itself (i.e before you call mydrawable.draw(canvas))
If you using a drawable as a background for a view, the view internally sets the bounds for you. But if you are using drawables in your own onDraw, then you need to explicitly set the bounds via setBounds.

Categories

Resources