Предлагаю вашему вниманию перевод статьи "VectorDrawables – Part 1" с сайта blog.stylingandroid.com.


По долгу службы потребовалось мне как-то разобраться с векторной графикой. Во время поиска наткнулся я на серию статей под названием “VectorDrawable” в блоге https://blog.stylingandroid.com/. Ответов на все интересующие меня вопросы я, конечно, не нашел, но статьи очень понравились своей последовательностью и четко выверенным объемом необходимого материала. Решил поделиться переводом этих статей с обитателями хабра.


Я не переводил названия, activity, bitmap и тому подобное, потому что считаю, что так легче воспринимать информацию, ведь разработчики, в силу профессии, эти слова в русском варианте практически никогда не у потребляют. Далее следует перевод:


Одна из действительно интересных новых фишек в Lollipop – это включение VectorDrawable и связанных с ним классов, которые обеспечивают чрезвычайно мощные новые возможности для добавления сложной векторной графики (позволяет гораздо удобнее масштабировать изображения не зависимо от размеров экрана и плотности, чем растровые изображения), а также предоставляет несколько мощных инструментов для анимации оных. В этой серии статей мы рассмотрим некоторые из преимуществ, которые они нам дают. Так же рассмотрим, как можно получить действительно впечатляющие результаты из относительно небольшого количества кода.


Векторная графика – способ описания графических элементов используя геометрические фигуры. Они особенно хорошо подходят для графических элементов, созданных в приложениях, таких как Adobe Illustrator или Inkscape, где простые геометрические формы могут быть объединены в гораздо более сложные элементы. Работа с растровой графикой, с другой стороны, определяет значение цвета для каждого пикселя и особенно хорошо подходит для фото. Большим преимуществом использования векторной графики (в соответствующих случаях) является то, что изображения рендерятся в рантайме и размеры автоматически высчитываются в зависимости от плотности пикселей. Таким образом получается четкая картинка с плавными линиями, независимо от возможностей устройства. Векторные картинки, как правило, занимают значительно меньший объем памяти, чем их растровый аналог. Однако, векторные изображения требуют больше вычислительных мощностей для отрисовки, что может быть проблемой при большом количестве сложных графических элементах.


Векторная графика в андроиде была реализована с использованием нового класса – VectorDrawable, который был введен в Lollipop. Это означает, что для графических элементов, которые хорошо подходят для векторного представления мы можем заменить растровые изображения в папках mdpi, hdpi, xhdpi, xxhdpi, и xxxhdpi на один VectorDrawable в папке Drawable, который, с большой вероятностью, займет даже меньше пространства на диске, чем растровое изображение для mdpi.


Чтобы продемонстрировать это, давайте рассмотрим следующий файл svg (найти его можно по ссылке https://code.google.com/archive/p/svg-android/downloads):


image


Этот svg-файл занимает 2265 байт, если мы отрисуем его в bitmap с размерами 500 х 500 пикселей, и сохраним как png, тогда он займет уже 13272 байта, плюс к этому, мы должны будем использовать несколько таких картинок для разный плотностей экрана. Но SVG – это не то же самое, что VectorDrawable, поэтому мы не можем использовать его непосредственно. Тем не менее, VectorDrawable поддерживает некоторые элементы SVG. Основные компоненты, которые мы будем использовать из нашего SVG – это path. Давайте посмотрим на исходный код SVG:


android.svg
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     width="500px" height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve">
<g id="max_width__x2F__height" display="none">
    <path display="inline" d="M499.001,1v498H1V1H499.001 M500.001,0H0v500h500.001V0L500.001,0z"/>
</g>
<g id="androd">
    <path fill="#9FBF3B" d="M301.314,83.298l20.159-29.272c1.197-1.74,0.899-4.024-0.666-5.104c-1.563-1.074-3.805-0.543-4.993,1.199
        L294.863,80.53c-13.807-5.439-29.139-8.47-45.299-8.47c-16.16,0-31.496,3.028-45.302,8.47l-20.948-30.41
        c-1.201-1.74-3.439-2.273-5.003-1.199c-1.564,1.077-1.861,3.362-0.664,5.104l20.166,29.272
        c-32.063,14.916-54.548,43.26-57.413,76.34h218.316C355.861,126.557,333.375,98.214,301.314,83.298"/>
    <path fill="#FFFFFF" d="M203.956,129.438c-6.673,0-12.08-5.407-12.08-12.079c0-6.671,5.404-12.08,12.08-12.08
        c6.668,0,12.073,5.407,12.073,12.08C216.03,124.03,210.624,129.438,203.956,129.438"/>
    <path fill="#FFFFFF" d="M295.161,129.438c-6.668,0-12.074-5.407-12.074-12.079c0-6.673,5.406-12.08,12.074-12.08
        c6.675,0,12.079,5.409,12.079,12.08C307.24,124.03,301.834,129.438,295.161,129.438"/>
    <path fill="#9FBF3B" d="M126.383,297.598c0,13.45-10.904,24.354-24.355,24.354l0,0c-13.45,0-24.354-10.904-24.354-24.354V199.09
        c0-13.45,10.904-24.354,24.354-24.354l0,0c13.451,0,24.355,10.904,24.355,24.354V297.598z"/>
    <path fill="#9FBF3B" d="M140.396,175.489v177.915c0,10.566,8.566,19.133,19.135,19.133h22.633v54.744
        c0,13.451,10.903,24.354,24.354,24.354c13.451,0,24.355-10.903,24.355-24.354v-54.744h37.371v54.744
        c0,13.451,10.902,24.354,24.354,24.354s24.354-10.903,24.354-24.354v-54.744h22.633c10.569,0,19.137-8.562,19.137-19.133V175.489
        H140.396z"/>
    <path fill="#9FBF3B" d="M372.734,297.598c0,13.45,10.903,24.354,24.354,24.354l0,0c13.45,0,24.354-10.904,24.354-24.354V199.09
        c0-13.45-10.904-24.354-24.354-24.354l0,0c-13.451,0-24.354,10.904-24.354,24.354V297.598z"/>
</g>
</svg>

Немного разберемся. Есть некоторые атрибуты родительского элемента , которые определяют размер 500х500, Есть элемент (group), который определяет границы – его мы будем игнорировать. Есть еще один элемент с id = “android”. Это и есть изображение логотипа, которое нам нужно. Он состоит из шести элементов , которые определяют голову, правый глаз, левый глаз, левая рука, тело и ноги, правая рука. Атрибут “fill” определяет цвет заливки (и мы можем видеть, что все они зеленые, за исключением глаз, которые залиты белым цветом), а атрибут “d” содержит маршрут линий, из которых состоит элемент. Для тех, кто хочет разобраться более детально в элементе , следует изучить SVG Path Specification, но для данной статьи это не важно, потому что мы можем просто взять их, как они есть, и использовать в наших VectorDrawables.

Итак, давайте создадим наш VectorDrawable:


res/drawable/android.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:viewportWidth="500"
    android:viewportHeight="500"
    android:width="500px"
    android:height="500px">
    <group android:name="android">
        <path
            android:name="head"
            android:fillColor="#9FBF3B"
            android:pathData="M301.314,83.298l20.159-29.272c1.197-1.74,0.899-4.024-0.666-5.104c-1.563-1.074-3.805-0.543-4.993,1.199L294.863,80.53c-13.807-5.439-29.139-8.47-45.299-8.47c-16.16,0-31.496,3.028-45.302,8.47l-20.948-30.41c-1.201-1.74-3.439-2.273-5.003-1.199c-1.564,1.077-1.861,3.362-0.664,5.104l20.166,29.272c-32.063,14.916-54.548,43.26-57.413,76.34h218.316C355.861,126.557,333.375,98.214,301.314,83.298" />
        <path
            android:name="left_eye"
            android:fillColor="#FFFFFF"
            android:pathData="M203.956,129.438c-6.673,0-12.08-5.407-12.08-12.079c0-6.671,5.404-12.08,12.08-12.08c6.668,0,12.073,5.407,12.073,12.08C216.03,124.03,210.624,129.438,203.956,129.438" />
        <path
            android:name="right_eye"
            android:fillColor="#FFFFFF"
            android:pathData="M295.161,129.438c-6.668,0-12.074-5.407-12.074-12.079c0-6.673,5.406-12.08,12.074-12.08c6.675,0,12.079,5.409,12.079,12.08C307.24,124.03,301.834,129.438,295.161,129.438" />
        <path
            android:name="left_arm"
            android:fillColor="#9FBF3B"
            android:pathData="M126.383,297.598c0,13.45-10.904,24.354-24.355,24.354l0,0c-13.45,0-24.354-10.904-24.354-24.354V199.09c0-13.45,10.904-24.354,24.354-24.354l0,0c13.451,0,24.355,10.904,24.355,24.354V297.598z" />
        <path
            android:name="body"
            android:fillColor="#9FBF3B"
            android:pathData="M140.396,175.489v177.915c0,10.566,8.566,19.133,19.135,19.133h22.633v54.744c0,13.451,10.903,24.354,24.354,24.354c13.451,0,24.355-10.903,24.355-24.354v-54.744h37.371v54.744c0,13.451,10.902,24.354,24.354,24.354s24.354-10.903,24.354-24.354v-54.744h22.633c10.569,0,19.137-8.562,19.137-19.133V175.489H140.396z" />
        <path
            android:name="right_arm"
            android:fillColor="#9FBF3B"
            android:pathData="M372.734,297.598c0,13.45,10.903,24.354,24.354,24.354l0,0c13.45,0,24.354-10.904,24.354-24.354V199.09c0-13.45-10.904-24.354-24.354-24.354l0,0c-13.451,0-24.354,10.904-24.354,24.354V297.598z" />
    </group>
</vector>

Мы создали родительский элемент , который содержит информацию о размерах изображения, внутрь которого поместили элемент с шестью элементами , которые были немного модифицированы, по сравнению с svg – файлом. В данной статье поле “name” служит только для облегчения понимая того, где какой элемент. В следующих статьях они буду использоваться. Получившийся файл по-прежнему может похвастаться скромным размером в 2412 байт.

Теперь мы можем использовать этот файл как любой другой drawable:


res/layout/activity_vector_drawables.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".VectorDrawablesActivity">

    <ImageView
        android:id="@+id/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/android"
        android:contentDescription="@null" />

</RelativeLayout>

… и если мы запустим это, то увидим красивую отрисовку:


image


Так что теперь мы можем существенно уменьшить размер APK, если мы используем VectorDrawable, где это уместно. Так же это позволяет упростить разработку приложения, особенно, если нужно добавлять поддержку новых плотностей экрана. Тем не менее, это не все, на что способен VectorDrawable. В следующей части статьи мы рассмотрим, как его анимировать.


Исходники к этой части статьи можно найти здесь.

Поделиться с друзьями
-->

Комментарии (4)


  1. dzigoro
    24.05.2016 16:41

    А слух был, мол, в compatibility это тоже затащили. Это правда?


    1. IIblBko
      24.05.2016 17:20

      Да. Это правда. Статья, которую переводил, не самая свежая. Сейчас в саппорт либе есть поддержка VectorDrawable и более старых версий. По памяти не скажу насколько старые версии они начали поддерживать.


    1. kaftanati
      24.05.2016 22:51

      Правда-правда.

      You’ll be able to use VectorDrawableCompat back to API 7 and AnimatedVectorDrawableCompat on all API 11 and higher devices.


      1. danikula
        02.06.2016 16:01
        +1

        Но в весьма ограниченном виде. Нельзя например использовать векторные картинки в drawableLeft и прочих drawable* для TextView, нельзя использовать для иконок системных нотификаций. Зато смело можно использовать в ImageView при помощи нового атрибута app:srcCompat.