• Understanding Android’s Vector Image format: VectorDrawable
  • By Nick Butcher
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: HarderChen
  • Proofreader: GS666, Qiuk17

Since Android devices typically come in different sizes, shapes, and screen pixel densities, I prefer vector assets that are resolution-independent. But what exactly are they? What are the benefits? What is the cost? When should you use them? How do you create and use them? In this series of articles, I’ll explore these issues and explain why you should use vector assets a lot in your applications and how to get the most out of them.

Bitmap vs vector map

Most image formats (PNG, JPEG, BMP, GIF and WebP, to name a few) are bitmap formats, which means they draw an image as a fixed grid of pixels. Thus, with bitmaps of fixed resolution, we only know the color of each pixel, but not what it contains. Vector images, however, depict images by defining a series of shapes on a canvas of abstract size.

Why vector graphics?

Vector resources have three major benefits, which are:

  • Be useful
  • Less resource usage
  • dynamic

Be useful

Vector graphics can be gracefully resized; That’s because they draw images on abstract-sized canvases, which you can zoom in or out on and then redraw to the corresponding size. However, bitmap resources can be very bad after resizing. Zooming out raster resources is OK (meaning some information is lost), but zooming in on them can result in blurring or ribbon-like distortion because they have to insert missing pixels.

Enlarged bitmap (left) and enlarged vector map (right)

This is why on Android we need to provide multiple versions of bitmap resources for screens of different densities:

  • res/drawable-mdpi/foo.png
  • res/drawable-hdpi/foo.png
  • res/drawable-xhdpi/foo.png

When needed, Android will pick the nearest larger density and scale it down. As devices have higher and higher screen density, application developers must keep creating, including, and converting more and more versions of the same resource. Note that the screen density of many modern devices is not exact (for example, Piexl 3 XL is 552 Dpi, between XXHDPI and XXXHDpi), so resources are usually scaled.

Because vector resources can be gracefully resized, you only need to include a single resource and it can be rendered on any screen density device.

Less resource usage

Vector resources are usually less resource-intensive than bitmap resources because you only need to provide one version and vector resources are easily compressed.

For example, Google I/O App saved 482 KB in this submission by converting some PNG ICONS from bitmaps to vector images. It doesn’t sound like a lot, but that’s for small images; Larger images (such as illustrations) will save even more.

This illustration is from last year’s Google I/O sample APP flow:

For illustrations, vectors are a good choice

We couldn’t replace it with VectorDrawable because gradients weren’t widely supported at the time (they are now), so we had to release a bitmap version 😔. If we could use vectors, this would only be 30% of its size and would be much better:

  • Raster: Download Size = 53.9KB (Raw file size = 54.8KB)
  • Vector: Download Size = 3.7KB (Raw file size = 15.8KB)

Note that while Android App Bundles provide the same benefits by providing the density resources they need to different devices, VectorDrawable is typically smaller and does not require the creation of larger bitmap resources.

dynamic

Since vector images describe their content rather than “flattening” themselves into pixels, this opens the door to interesting new possibilities for animation, interaction, or dynamic themes. More on this in the future.

Vectors keep the image structure, so the attributes of individual elements can be changed to make themes or animations.

Weigh the

Vectors do have some disadvantages to consider:

decoding

As explained earlier, vector images describe what they contain, so you need to inflate and draw them before using them.

The steps involved in decoding the vector prior to rendering

There are two steps:

  1. Inflation. Your vector file must be read and parsed into[VectorDrawable](https://developer.android.com/reference/android/graphics/drawable/VectorDrawable)To youpaths 和 groupsModeling.
  2. Drawing. Then you have to executeCanvasDraw command to draw these model objects.

The execution time of these two steps is proportional to the complexity of the vector and the type of operation you perform. If you use very complicated shape, it will take a longer time will parse [Path] (https://developer.android.com/reference/android/graphics/Path). Similarly, more drawing operations will take longer to perform (and some more time consuming, such as clipping operations).

For static vectors, the drawing phase is performed only once and can then be cached as a Bitmap. This optimization cannot be done with animation vectors because their properties will necessarily change and need to be redrawn.

Compare this to bitmap resources like PNG that only need to decode the contents of a file, which have been highly optimized over time.

This is the basic tradeoff between bitmaps and vector maps. Vector graphics provide these benefits, but at the cost of being more expensive to render. In the early days of Android, device performance was poor and screen density didn’t vary much. Now, Android devices are getting better and better, but screen densities vary. So I think all apps should use vector resources.

adaptive

Because of the nature of the format, vectors are very useful in describing some vector resources, such as simple ICONS. They are terrible for encoding photogram-type images, which are difficult to describe as a series of shapes. Bitmap formats (such as WebP) are more efficient at this point. This is of course a range, depending on the complexity of your resources.

shift

As far as I know, no design tool can create VectorDrawables directly, which means there is a conversion step from another format. This complicates the workflow between designers and developers. We will delve into this topic in a future article.

Why not use SVG?

If you’ve ever used vector image formats, you’ve probably come across the industry standard SVG format (Scalable vector graphics) on the web. It’s a powerful, mature modeling tool, and it’s also a powerful standard. It includes many complex features such as executing arbitrary javascript, blurring and filter effects or embedding other images, and even GIF animations. Android runs on restricted mobile devices, so supporting the entire SVG specification is not a realistic goal.

However, SVG includes a path specification that defines how shapes are described and drawn. Using this API, you can express most vector shapes. This is basically the same SVG path specification that Android supports, with a few additions.

In addition, VectorDrawable can be integrated with Android platform features by defining its own format. For example, use the Android resource system to reference @colors, @Dimens, or @strings, and use standard Animators to handle theme attributes or AnimatedVectorDrawable.

VectorDrawableThe function of the

As mentioned above, VectorDrawable supports the SVG path specification, allowing you to specify one or more shapes to draw. It is implemented through an XML file, as follows:

<! Copyright 2018 Google LLC.spdx-license-Identifier: apache-2.0 --> <vector XMLNS: Android = Copyright 2018 Google LLC.spdx-license-Identifier: apache-2.0 --> <vector XMLNS: Android ="http://schemas.android.com/apk/res/android"
  android:width="24dp"
  android:height="24dp"
  android:viewportWidth="24"
  android:viewportHeight="24">

    <path
      android:name="cross"
      android:pathData="M6.4, L17.6 6.4, 17.6 M6.4, 17.6 L17.6, 6.4"
      android:strokeWidth="2"
      android:strokeLineCap="square"
      android:strokeColor="# 999" />

</vector>
Copy the code

Note that you need to specify the inherent size of the resource, which is set through the WrAP_content of the ImageView. The second viewport size defines the virtual canvas, or the spatial coordinates of all subsequent draw commands. The native and viewport sizes can be different (but should be in the same proportion) – you can define vectors in a 1*1 canvas if you want.

The

element contains one or more elements. They can be named (for later reference, such as animation), but it is crucial that you specify the pathData element that describes the shape. This mysterious string can be thought of as a series of commands to control the pen on the virtual canvas:

Visual path operation

The command above moves the virtual pen, then draws a line to another point, lifts and moves the pen, then draws another line. We can describe almost any shape with just the four most commonly used commands (see specification for more) :

  • M move to
  • L line to
  • C (cubic bezier) curve to
  • Z close (line to first point)

(Uppercase commands use absolute paths & lowercase commands use relative paths)

You might be wondering if you need to pay attention to these details – could you possibly get them directly from an SVG file? You don’t need to read the path to know what it will draw, but having a general idea of what a VectorDrawable is doing is useful and necessary to understand some of the advanced features we’ll learn about later.

Paths themselves don’t draw anything, they need to be stroked or filled.

<! Copyright 2018 Google LLC.spdx-license-Identifier: Apache-2.0 --> <vector... > <path android:pathData="..."
      android:fillColor="#ff00ff"
      android:strokeColor="# 999"
      android:strokeWidth="2"
      android:strokeLineCap="square" />

</vector>
Copy the code

Part 2 of this series details the different ways to fill and stroke paths.

You can also define path groups. This allows you to define transformation operations that apply to all paths within a group.

<! Copyright 2018 Google LLC.spdx-license-Identifier: Apache-2.0 --> <vector... > <path ... /> <group android:name="foo"
        android:pivotX="12"
        android:pivotY="0"
        android:rotation="45"
        android:scaleX="1.2"
        android:translateY="To 4">

        <path ... />

    </group>

</vector>
Copy the code

Note that you cannot rotate, scale, or convert individual paths. If you want this kind of behavior, you need to put them together in a group. These transformations make no sense for static images, which can “bake” them directly into their path — but they are very useful for animation.

You can also define clip-path, which masks areas that can be drawn by other paths in the same group. They are defined exactly the same as path.

<! Copyright 2018 Google LLC.spdx-license-Identifier: Apache-2.0 --> <vector... > <clip-path android:name="mask"
    android:pathData="..."/> <path ... /> </vector>Copy the code

One limitation worth noting is that clip-path does not eliminate aliasing.

Declare a non-anti-aliasing clip path

This example (I have to zoom in to see the effect) shows two ways to draw the camera shutter icon. The first draws a path, and the second draws a solid block that shields the shutter shape. Masks can help create interesting effects (especially when animating), but they are relatively expensive, so you need to draw shapes differently to avoid it.

Paths can be trimmed; This is just a subset of plotting the entire path. You can prune filled paths, but the results can be surprising! Trimming stroke paths is more common.

<! Copyright 2018 Google LLC.spdx-license-Identifier: Apache-2.0 --> <vector... > <path android:pathData="..."
    android:trimPathStart="0.1"
    android:trimPathEnd="0.9" />

</vector>
Copy the code

Clip path

You can trim from the beginning or end of a path, or you can use offsets for any trim. They are defined as part of the path [0,1]. Learn how to set different trim values to change the part of the drawn line. Also note that offsets can “wrap” the trim values. One more, this property doesn’t make much sense for still images, but it’s handy for animations.

The root vector element supports the alpha attribute [0, 1]. Group has no alpha property, but each path supports fillAlpha and strokeAlpha.

<! Copyright 2018 Google LLC.spdx-license-Identifier: Apache-2.0 --> <vector... android:alpha="0.7">

  <path
    android:pathData="..."
    android:fillColor="#fff"
    android:fillAlpha="0.5" />

</vector>
Copy the code

The follow-up work

So hopefully this article has given you an understanding of what vector resources are, the benefits of using them, and the trade-offs involved. Android’s vector format is already widely supported. Given the wide variety of devices on the market, you should use vector resources as the default choice and use bitmap resources only in special cases. Read our next article to find out more:

Coming soon: Draw paths

Coming soon: Create an Android vector resource

Coming soon: Using Vector Assets in Android applications

Coming soon: Analyzing AndroidVectorDrawable

Thanks to Ben Weiss, Jose Alcerreca and Chris Banes.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.