Pull to refresh

Легко переходим на векторный формат картинок вместо нарезки под разные плотности экранов в Android 4.0+. Часть 1 из 2

Development of mobile applications *Designing and refactoring *Development for Android *


Обычно дизайн приложения рисуется в векторном редакторе (например, Sketch), но типичным форматом картинок в приложении под Android является растровый (как правило, PNG). При разработке приложения необходимо для каждого векторного изображения заниматься утомительной работой по изготовлению набора растровых картинок для разных плотностей экранов. Количество таких комплектов может доходить до шести по числу возможных плотностей: ldpi, mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi (плотность xxxhdpi необходима только для иконки приложения). При верстке иногда приходится задать в разметке явные размеры для изображения, что может потребовать перемасштабирования растровой картинки, а это, в свою очередь, наверняка приведет к появлению артефактов. К тому же наличие нескольких комплектов картинок отрицательно сказывается на размере выходного apk.

Все решают эти проблемы по-разному: кто-то пытается подключить SVG-библиотеку к проекту, кто-то генерирует нарезку с помощью утилиты.

Как мне кажется, наиболее правильным решением является отказ от использования растровой графики в приложении в пользу векторной. При этом хотелось бы по максимуму задействовать системные возможности. В Android 5.0 появился VectorDrawable – поддержка векторного формата для картинок, которые размещаются в виде ресурсов с расширением xml в папке drawable. На такие картинки можно ссылаться обычным образом из XML-разметки.

Использование VectorDrawable было бы отличным решением, если бы не необходимость поддержки устройств с Android 4.0+, коих большинство. VectorDrawable нет в support library и неизвестно, когда он там появится (хотя начало положено). Но не стоит печалиться: есть замечательная библиотека BetterVectorDrawable с открытым исходным кодом и лицензией Apache 2.0, которая фактически переносит VectorDrawable на Android 4.0+, предоставляя тот же интерфейс, и позволяет при необходимости использовать системный VectorDrawable на Android 5.0+. Нужно отметить, что есть еще пара аналогичных библиотек, но они не стоят Вашего внимания, поскольку не дают полноценно ссылаться на vector drawable ресурсы из разметки.

К сожалению, разработчики Android не предусмотрели поддержку в VectorDrawable градиентов, текстур, масок. Это небольшая проблема, но об этом следует помнить при составлении дизайна приложения. Если от этих элементов невозможно отказаться, то можно как прежде использовать в отдельных местах растровую графику / shape drawable, преимущественно перейдя на вектор.

Итак, чтобы перейти на векторный формат картинок в приложении надо:
  1. Подключить к приложению библиотеку BetterVectorDrawable
  2. Выгрузить из векторного редактора изображения в SVG-формате
  3. С помощью конвертера сконвертировать их все сразу в XML-формат vector drawable
  4. Положить полученные файлы в директорию приложения res/drawable
  5. Использовать векторные изображения в разметке и в коде как обычные ресурсы
  6. Profit

Библиотека BetterVectorDrawable


Подключаем библиотеку


Чтобы подключить библиотеку к приложению достаточно добавить одну строчку в секции dependencies файла build.gradle, расположенном в директории модуля приложения:

dependencies {
    …
    compile 'com.bettervectordrawable:lib:0.4+'
}

Библиотека распространяется через репозиторий JCenter, который используется по умолчанию в новых проектах Android Studio.

Если Вы создавали проект давно, то, возможно, у Вас используется репозиторий Maven Central. Чтобы это проверить, надо в файлах build.gradle поискать вхождения строки

mavenCentral()

и добавь рядом с ней

jcenter()

Включаем перехват ресурсов


Библиотеке необходимо передать список идентификаторов векторных ресурсов, чтобы она понимала, какие из них являются vector drawable. BetterVectorDrawable будет перехватывать обращения к ним и создавать экземпляры VectorDrawable.
Поскольку передать список нужно один раз, лучше всего это сделать в методе onCreate() класса Application, для чего придется создать его наследника:

package com.bettervectordrawable.demo;

import android.app.Application;
import com.bettervectordrawable.VectorDrawableCompat;

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        // вызов VectorDrawableCompat.enableResourceInterceptionFor()
    }
}

И указать этого наследника в манифесте приложения:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bettervectordrawable.demo">

    <application
        android:name=".App"
…

Существует три способа передать список: удобный, быстрый и ручной.

Удобный способ


int[] ids = VectorDrawableCompat.findAllVectorResourceIdsSlow(getResources(), R.drawable.class);
VectorDrawableCompat.enableResourceInterceptionFor(getResources(), ids);

Метод findAllVectorResourceIdsSlow сканирует все drawable XML-ресурсы и убеждается, что каждый возвращаемый ресурс является vector drawable. Разработчики советуют использовать этот метод по умолчанию, тем не менее, это наименее производительный способ, т.е. на старых устройствах время запуска приложения может существенно возрасти.

На Google Nexus 5 в приложении с 400 векторными ресурсами findAllVectorResourceIdsSlow отрабатывает менее чем за 150 мс.

Быстрый способ


int[] ids = VectorDrawableCompat.findVectorResourceIdsByConvention(getResources(), R.drawable.class, Convention.ResourceNameHasVectorSuffix);
VectorDrawableCompat.enableResourceInterceptionFor(getResources(), ids);

Метод findVectorResourceIdsByConvention подразумевает, что названия всех векторных ресурсов начинаются на vector_ или заканчиваются на _vector. Соглашение по именованию нужно указать с помощью параметра resourceNamingConvention.

На Google Nexus 5 в приложении с 400 векторными ресурсами findVectorResourceIdsByConvention отрабатывает менее чем за 20 мс.

Ручной способ


VectorDrawableCompat.enableResourceInterceptionFor(getResources(),
    R.drawable.your1_vector,
    R.drawable.your2_vector,
    R.drawable.your3_vector);

Просто передается список всех идентификаторов векторных картинок. 0 мс.

Используем vector drawable


В коде:

Drawable drawable = getResources().getDrawable(R.drawable.your_vector);

Или из разметки:

<View
    android:layout_width="210dp"
    android:layout_height="210dp"
    android:background="@drawable/your_vector" />

Как видите, все просто.

Если у Вас возникли вопросы, то можно задать их мне либо посмотреть демо-приложение. О проблемах с библиотекой лучше сообщать разработчикам в GitHub.

В следующей части мы обсудим конвертацию изображений из SVG в vector drawable XML.
Tags:
Hubs:
Total votes 25: ↑24 and ↓1 +23
Views 33K
Comments Comments 13