Vectorial Victories - Android Makers 2018 - Paris

Vectorial Victories - Android Makers 2018 - Paris

Tired of having to duplicate all your assets in order to manage Android's densities? Vector Drawables are here to save the day!

Except... it is not that easy. There are some nasty traps you need to know in order to really take advantage of this format.

We will see how vectorial drawing works, how it has been adapted to Android and finally one of its biggest strengths: animations!

4d2070e800356da57315d427aa2343f4?s=128

François Blavoet

April 24, 2018
Tweet

Transcript

  1. @francoisblavoet Vectorial Victories

  2. Goal Understand when and how to use Vector drawables and

    how to use them to their fullest. Learn how to deal with common pain points.
  3. First, there were rasters Raster : bitmap / png /

    jpg / webp / … • An asset that describes it’s content as an array of pixels • Works great for images • Can be very heavy • Need to be duplicated for each density
  4. First, there were rasters

  5. First, there were rasters

  6. First, there were rasters

  7. First, there were rasters

  8. First, there were rasters

  9. First, there were rasters

  10. First, there were rasters

  11. First, there were rasters

  12. Then, came SVGs Scalable Vector Graphics • Describes curves that

    can be rendered in any size • Infinitely scalable • Very nimble and don’t have to be duplicated • Some bonus features you can’t achieve with rasters
  13. Then, came SVGs

  14. Then, came SVGs

  15. What’s in there ? <svg fill="#ffffff" height="24" viewBox="0 0 24

    24" width="24" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/> </svg>
  16. • viewBox : size of the canvas where you describe

    the curves of this vector • height/width : size in pixel of this svg when rendered, ≠ viewBox • fill : color fill for this vector/path • path : curve to draw on the screen <svg fill="#ffffff" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/> </svg>
  17. <svg fill=“#ffffff” height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> <path

    d="M0 0h24v24H0z" fill="none"/> <path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/> </svg>
  18. A path you said ?

  19. A path you said ? Let’s define a viewbox :

    viewBox="0 0 24 24"
  20. A path you said ? First instruction : Move <path

    d="M2 5”/> moves the painting cursor somewhere on the canvas
  21. A path you said ? Line <path d="M2 5 l8

    0”/> draws a line starting from the current end of the curve
  22. A path you said ? Line <path d="M2 5 l8

    0”/> draws a line starting from the current end of the curve
  23. A path you said ? Line <path d="M2 5 l8

    0 L 8 0”/> draws a line starting from the current end of the curve
  24. l vs L • Upper case : absolute coordinates •

    lower case : relative coordinates
  25. A path you said ? Line <path d="M2 5 l8

    0 L 8 0”/> draws a line starting from the current end of the curve
  26. A path you said ? closepath <path d="M2 5 l8

    0 L 8 0 z"/> closes the current curve with a straight line to its starting point
  27. A path you said ? <path d="M2 5 l8 0

    L 8 0 z"/>
  28. A path you said ? horizontal and vertical lines <path

    d="M2 5 l8 0 L 8 0 z
 M 2 10 h 2"/>
  29. A path you said ? horizontal and vertical lines <path

    d="M2 5 l8 0 L 8 0 z
 M 2 10 h 2 v -1.5"/>
  30. A path you said ? stroke <path d="M2 5 l8

    0 L 8 0z"
 fill="none" 
 stroke="red" 
 stroke-width="5"/> you can also control how your curves are drawn
  31. you get the idea many more commands are available :

    
 elliptical arc
 rectangle triangle gradient etc
  32. Up to powerful instructions such as Bezier curves <path d=“M93.069,146.642

    c0,0 298.599, -84.231 196.926,1.225”/>
 
 

  33. I thought this was about Android ? <svg fill="#000000" height="24"

    viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/> </svg>
  34. I thought this was about Android ? <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="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z" /> </vector> <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/> </svg>
  35. I thought this was about Android ? <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width=“@dimen/material_icon“

    android:height=“@dimen/material_icon" android:viewportHeight="24.0" android:viewportWidth="24.0"> <path android:fillColor="@color/accent" android:pathData=“@string/note_path” /> </vector> <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0h24v24H0z" fill="none"/> <path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z"/> </svg>
  36. Vector Drawable • That’s an Android resource !
 
 ->

    can be used just as any other drawable
 
 -> can use @color for fills, @string for paths, @dimen for dimensions • It’s NOT an SVG !!! 
 
 -> only a subset of the SVG norm is supported
  37. None
  38. How to convert ?

  39. Is it that easy ? Nope

  40. Most common issue : even-odd path

  41. Most common issue : even-odd path http://a-student.github.io/SvgToVectorDrawableConverter.Web/

  42. even-odd path http://a-student.github.io/SvgToVectorDrawableConverter.Web/

  43. Performances <?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportHeight="108" android:viewportWidth="108">

    <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffffff" android:pathData="M0.996 21l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 21l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffffff" android:pathData="M0.996 15l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 15l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffffff" android:pathData="M0.996 18l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 18l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ff0000" android:pathData="M0.996 21l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 21l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ff0000" android:pathData="M0.996 15l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 15l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ff0000" android:pathData="M0.996 18l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 18l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffed00" android:pathData="M9.996 21l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M9.996 21l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffed00" android:pathData="M9.996 15l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M9.996 15l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffed00" android:pathData="M9.996 18l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M9.996 18l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ff0092" android:pathData="M18.996 21l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M18.996 21l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ff0092" android:pathData="M18.996 15l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M18.996 15l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ff0092" android:pathData="M18.996 18l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M18.996 18l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#bed62f" android:pathData="M27.996 21l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M27.996 21l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#bed62f" android:pathData="M27.996 15l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M27.996 15l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#bed62f" android:pathData="M27.996 18l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M27.996 18l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#00c7f2" android:pathData="M36.996 21l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 21l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#00c7f2" android:pathData="M36.996 15l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 15l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#00c7f2" android:pathData="M36.996 18l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 18l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffffff" android:pathData="M0.996 12l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 12l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffffff" android:pathData="M0.996 9l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 9l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffaaaa" android:pathData="M0.996 12l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 12l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffaaaa" android:pathData="M0.996 9l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M0.996 9l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffc2e5" android:pathData="M18.996 12l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M18.996 12l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffc2e5" android:pathData="M18.996 9l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M18.996 9l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffc2e5" android:pathData="M18.996 6l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M18.996 6l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#ffc2e5" android:pathData="M18.996 3l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M18.996 3l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#dde89b" android:pathData="M27.996 12l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M27.996 12l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#dde89b" android:pathData="M27.996 9l8 0 0 2 -8 0 0 -2z" /> <path android:fillColor="#ffffff" android:pathData="M27.996 9l8 0 0 2 -8 0 0 -2z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#c1f1fc" android:pathData="M36.996 12l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 12l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#c1f1fc" android:pathData="M36.996 9l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 9l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#c1f1fc" android:pathData="M36.996 6l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 6l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#c1f1fc" android:pathData="M36.996 3l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 3l8 0 0 2 -8 0z" /> </group> <group android:translateX="31" android:translateY="36"> <path android:fillColor="#c1f1fc" android:pathData="M36.996 0l8 0 0 2 -8 0z" /> <path android:fillColor="#ffffff" android:pathData="M36.996 0l8 0 0 2 -8 0z" /> </group> <group android:translateX="32" android:translateY="62"> <path android:fillColor="#007feb" android:pathData="M1 0l42 0c0.554 0 1 0.446 1 1l0 8c0 0.554 -0.446 1 -1 1L1 10C0.446 10 0 9.554 0 9L0 1C0 0.446 0.446 0 1 0Z" /> <path android:fillColor="#ffffff" android:pathData="M17.289062 5.6640625q0 0.3164062 -0.128906 0.5654297Q17.03418 6.4785156 16.811523 6.6425781 16.553711 6.8359375 16.243164 6.9179687 15.935547 7 15.460938 7l-1.904297 0 0 -4.3623047 1.693359 0q0.527344 0 0.770508 0.035156 0.246094 0.035156 0.486328 0.1552734 0.249023 0.1259766 0.369141 0.3398438 0.123046 0.2109375 0.123046 0.4833984 0 0.3164062 -0.166992 0.5595703 -0.166992 0.2402344 -0.471679 0.375l0 0.023437q0.427734 0.084961 0.676757 0.3515625 0.251953 0.2666016 0.251953 0.703125zM15.847656 3.8710938q0 -0.1083985 -0.05566 -0.2167969Q15.739258 3.545898 1.7753906q0 -0.2080078 -0.08203 -0.319336 -0.0791 -0.1142578 -0.272461 -0.1699218 -0.131836 -0.038086 -0.363282 -0.041016 -0.231445 -0.00293 -0.483398 -0.00293l-0.246094 0 0 1.0869141 0.08203 0q0.474609 0 0.679687 -0.00293 0.205078 -0.00293 0.37793 -0.076172 0.175781 -0.073242 0.240234 -0.1933593 0.06738 -0.1230469 0.06738 -0.28125z" /> <path android:fillColor="#ffffff" android:pathData="M22.032227 7l-3.155274 0 0 -4.3623047 3.155274 0 0 0.84375 -2.036133 0 0 0.7529297 1.889648 0 0 0.84375 -1.889648 0 0 1.078125 2.036133 0 0 0.84375z" /> <path android:fillColor="#ffffff" android:pathData="M27.141602 3.4814453l-1.362305 0 0 3.5185547 -1.125 0 0 -3.5185547 -1.362305 0 0 -0.84375 3.84961 0 0 0.84375z" /> <path android:fillColor="#ffffff" android:pathData="M32.599609 7l-1.163086 0 -0.301757 -0.8818359 -1.617188 0L29.21582 7l-1.133789 0 1.611328 -4.3623047 1.294922 0L32.599609 7Zm-1.737304 -1.6816406l-0.536133 -1.5644531 -0.536133 1.5644531 1.072266 0z" /> </group> </vector>
  44. Performances

  45. Performances

  46. Performances

  47. Performances

  48. Performances <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportWidth="108" android:viewportHeight="108"> <path android:pathData="M 33.5

    62 L 75.5 62 C 76.05 62 76.5 62.45 76.5 63 L 76.5 71 C 76.5 71.55 76.05 72 75.5 72 L 33.5 72 C 32.95 72 32.5 71.55 32.5 71 L 32.5 63 C 32.5 62.45 32.95 62 33.5 62 Z" android:fillColor="#007feb"/> <path android:pathData="M 49.8 67.66 C 49.8 67.86 49.75 68.06 49.66 68.23 C 49.58 68.4 49.46 68.53 49.31 68.63 C 49.15 68.77 48.96 68.86 48.75 68.93 C 48.55 68.97 48.29 69 47.97 69 L 59.5 51 Z M 59.5 54 L 67.5 54 L 67.5 56 L 59.5 56 L 59.5 54 Z M 68.5 57 L 76.5 57 39 L 58.5 39 L 58.5 41 L 50.5 41 L 50.5 39 Z M 59.5 48 L 67.5 48 L 67.5 50 L 59………………………” android:fillColor="#ffffff"/> </vector>
  49. Performances ~ > svgo -p 2 sample.svg sample.svg: Done in

    24 ms! 0.269 KiB - 40.4% = 0.16 KiB ~ > avocado ic_add.xml ic_add.xml: Done in 13 ms! 0.303 Kb - 10.6% = 0.271 Kb
  50. Performances inflate (ms) draw (ms) original 10.90 2.00 optimized 3.51

    0.52
  51. workflows ?

  52. workflows

  53. workflows "

  54. workflows

  55. Another thing .. VectorDrawables are api 21 !

  56. Backport time !! defaultConfig { vectorDrawables.useSupportLibrary = true } ->

    VectorDrawableCompat -> AnimatedVectorDrawableCompat
  57. Backport time !! <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:drawableStart=“@drawable/dr” android:text=“@string/hello”/>

  58. Backport time !! <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:drawableStart=“@drawable/dr” android:text=“@string/hello”/>

  59. About these bonus features … Animations !!!

  60. About these bonus features …

  61. <objectAnimator xmlns:android="http://schemas.android.com/apk/res/ android" android:propertyName="trimPathEnd" android:valueFrom="0" android:valueTo="1" android:duration="1000" android:valueType="floatType" android:interpolator="@android:interpolator/linear"> </objectAnimator>

  62. None
  63. None
  64. Lottie

  65. None