- Используем векторные изображения SVG в приложениях Android, или как убить фрагментацию экранов и не потерять в качестве (плюсы, минусы, особенности)
- Масштабирование изображений
- Размер изображений с тенями и подсветками
- Отображение градиентов
- Время загрузки изображений
- Прозрачность и цветовые фильтры
- Нюанс с принудительной обработкой GPU
- Выводы
- Android working with SVG / vector drawables
- 1. What is VectorDrawable?
- 2. Creating Android Project
- 2.1 Creating VectorDrawable from Material Icons.
- 2.2 Creating VectorDrawable from SVG or PSD
- 3. Using VectorDrawable
- 3.1 Using VectorDrawable with ImageView from xml
- 3.2 Using VectorDrawable with ImageView from Java
- 3.3 Using VectorDrawable as Drawable with TextView
- 3.4 Using VectorDrawable to Customise RadioButton
Используем векторные изображения SVG в приложениях Android, или как убить фрагментацию экранов и не потерять в качестве (плюсы, минусы, особенности)
Достаточно долгое время мы занимаемся разработкой детских приложений под Android, постепенно постигая множество нюансов этой платформы. Есть одни грабли, которые подстерегают нас в каждом приложении, – это фрагментация экранов. Если делать одно изображение только под телефон маленького размера, то на планшете оно выглядит мягко говоря “не очень”. А если делать изображение высокого разрешения для планшетов и пытаться использовать его на телефонах, то с очень большой вероятность приложение вывалится с OutOfMemory.
Приходится готовить несколько экземляров одного и того же изображения под разные экраны. Еще сильнее облака сгущает новый монстр Galaxy Nexus 10 с безумным разрешением 2560х1600.
В общем, неплохо бы что-то изменить, решили мы. А что если использовать в приложениях не растровые изображения, а векторные? Такие изображения легко масштабируются под разные разрешения экранов, при этом не теряя в качестве. Можно использовать всего одно изображение под разные разрешения и размеры.
Сказано — сделано. Итак, под катом история внедрения векторных изображений в одно из наших приложений. В статье мы поделимся опытом и особенностями использования векторных изображений в формате SVG в приложениях Android.
Немного погуглив, выяснили, что векторные изображения для web и приложений обычно используются в формате SVG. С данным форматом работают векторные редакторы Adobe Illustrator и Inkscape. Вооружившись Inkscape, нарезали пробных картинок и принялись искать способы их загрузки и отображения в приложении Android.
Разбираться с устройством формата SVG и писать свой парсер не хотелось — наверняка же люди сталкивались с этим и до нас! Что ж, гуглим «android svg».
В итоге есть:
- 2 проекта на гуглокоде:
code.google.com/p/svg-android
code.google.com/p/svg-android-2 - подробная статья с использование NDK:
horribileru.blogspot.ru/2011/10/android-imageview-svg.html - и несколько ссылок на мертвые проекты на разных форумах.
Берем самый популярный — SVG-Android (он, кстати, переехал на Github, но новых коммитов там нет). Подключаем библиотеку, векторное изображение помещаем в res/raw, загружаем её и устанавливаем ее во вьюшку:
Загружаем тестовый проект с изображениями — всё отлично! Подключаем наши изображения — пусто. Как оказалось, данная библиотека поддерживает только формат SVG basic 1.1, который не поддерживается Inkspace, а рождается только в Adobe Illustrator.
Пробуем вторую библиотеку SVG-Android-2, которая является форком первого проекта и ушла чуть-чуть дальше.
Она уже понимает Inkscape, а также поддерживает другие фишки этого формата, о чем можно почитать тут. Здесь всё пошло проще, картинки загрузились и выглядели шикарно и на телефоне, и на планшете. На нем мы и остановились.
Пример SVG-изображения и неадаптированного по размеру под планшет PNG-изображения на планшете.
(просмотреть изображение в оригинальном размере 1280х800)
Первое — SVG (10 Кб), второе — PNG (22 Кб). Второе изображение имеет размытый контур и ступенчатый градиент
Масштабирование изображений
Изображения масштабируются только с сохранением пропорций. Поэтому использовать их в качестве фона, который может немного менять пропорции на разных разрешениях, не получится. В этом случае мы обычно делаем какое-то абстрактное изображение в PNG, которое довольно легко меняет свои пропорции для разных экранов.
Не забываем для SVG устанавливать свойство аdjustViewBounds в значение true, иначе изображение может рассчитывать свои границы не так, как вы задумали.
Размер изображений с тенями и подсветками
Некоторые элементы в нашем приложении изначально были отрисованы с небольшими тенями и подстветками — например, этот смайлик имеет серую подсветку сзади. Но это приводит к колоссальному увеличению размера файла SVG. 118 Кб против 1 Кб без этой подсветки. Чем больше размер файла — тем больше времени надо на его загрузку в программе, поэтому мы решили отказаться от этого эффекта.
Изорбражения с тенью и без: 118 Кб vs 1 Кб
Подсветку можно отключить или в графическом редакторе, или же прямым редактированием SVG-файла — удаляем тэг с огромным содержимым.
Отображение градиентов
На некоторых изображениях вдруг обнаружились черные пятна вместо фона. Оказалось, что градиент не поддерживается!
Проблема с градиентами решилась удалением лишних тэгов из svg (описано далее в статье). Но в принципе, и с этим можно было бы жить и в наших простых изображениях заменить градиент однородной заливкой, если бы не другой нюанс — значительное время загрузки изображений.
Вот как это выглядело на экране: слева — черное небо в виде градиента, справа — корректная картинка.
Время загрузки изображений
В приложении нужно было по 6 изображений на одной странице ViewPager, а поскольку они подгружаются в процессе прокрутки (если не кэшировать), интерфейс заметно дергался при скроллинге. Этого очень не хотелось, и было решено загружать все изображения при старте приложения. Получили время инициализации порядка 8 секунд, что было слишком долго.
В проблеме решили разобраться. Выкачали исходники проекта SVG-Android-2 и стали искать, что именно так тормозит. Оказалось, что в классе SVGParser XML-файл изображения парсится дважды: первый раз он собирает информацию о дополнительных атрибутах, которые используются при втором проходе. И, что самое интересное, — анализируется лишь атрибут xlink:href, который является некоторым подобием гиперссылок внутри самого документа. В наших проблемных изображениях как раз были такие ссылки, и вели они никуда. После того, как мы избавились от данных ссылок, отредактировав код SVG в некоторых изображениях, градиент стал корректно отображаться. Более того, убрав этот предварительный проход и немного оптимизирорав процесс загрузки, мы смогли уменьшить скорость загрузки с 8 секунд до 1,8-2. Следует заметить, что это соизмеримо с PNG среднего размера — загрузка этих же изображений в память заняла 1,7 секунд.
Ниже приведено сравнение загрузки 35 файлов в формате SVG и PNG.
SVG | PNG( 500×500) | |
Размер, КБ | 327 | 943 |
Время загрузки, с | 1,9 | 1,7 |
Прозрачность и цветовые фильтры
Часто в играх мы используем полупрозрачные картинки для неактивных элементов. В этом проекте помимо прозрачности нужны были цветовые фильтры для генерации элементов в играх, то есть чтобы один элемент можно было использовать один раз, но, раскрашивая его по-разному, мы получали бы разные элементы.
Оказалось, что ни alpha, ни colorFilter мы применить не сможем, т.к. библиотека загружает не типичные bitmapDrawable, а pictureDrawable, и в исходниках Android мы видим пустые методы для этого класса:
До этого с классом pictureDrawable никогда не сталкивались, и это было большой неожиданностью.
Опять покопавшись в исходниках библиотеки, мы нашли в классе SVGHandler поле fillPaint типа Paint, которым рисуются все компоненты. Если до загрузки элемента ему установить colorFilter, то он будет работать как положено. Нас это вполне устраивало, поэтому мы чуть-чуть изменили метод загрузки SVG, добавив возможность передавать туда цвет фильтра, который при необходимости устанавливается перед загрузкой изображения. Теперь изображения загружались так:
А в самом SVGHandler появился такой метод:
В итоге мы смогли получать из одной картинки сколько угодно изображений разных оттенков.
Также можно установить и Alpha для fillPaint, но в играх это свойство требуется в динамической форме (нажали на элемент — сделался полупрозрачным), и подгружать каждый раз новое изображение неудобно. Поэтому этот эффект заменили масштабированием (нажали — элемент уменьшился).
Нюанс с принудительной обработкой GPU
После запуска приложения к нам стали такие поступать ошибки:
Оказалось, что если на устройстве включена настройка “Принудительная обработка GPU” (Developer options — Force GPU Rendering), то наше приложение валится, т.к. метод drawPicture() у Canvas не поддерживает аппаратное ускорение. Об этом можно почитать на android developer.
Причем простое указание в манифесте android:hardwareAccelerated=«false» проблему не решает — пользовательская галочка в настройках имеет более высокий приоритет.
Было найдено довольно простое решение: для всех view, которые работают с нашими pictureDrawable, полученными из SVG, отключить аппаратное ускорение.
Так как функция аппаратного ускорения появилась в Аndroid 3.0 (api 11), то для работы с этим функционалом пришлось изменить target sdk нашего проекта с 8 на 11. И, конечно же, надо помнить про обратную совместимость — на более ранних платформах этих методов нет.
Выводы
Давайте подведем краткий итог работы с векторными изображениями в формате SVG в Android.
Плюсы:
- Один огромный плюс, из которого следуют все остальные, — это одна векторная картинка.
- Так как картинка векторная, она отлично отображается на всех размерах экранов.
- Размер SVG-картинок мал.
- Одна картинка используется несколько раз для разных разрешений.
- Сокращается процесс подготовки изображений для приложения.
Минусы:
- Картинки масштабируются только пропорционально.
- Не поддерживается прозрачность.
- Графику нужно упрощать — чем больше векторных элементов, тем больше весит файл.Нежелательно использовать тени и свечения, так как это в разы увеличивает размер SVG-файлов.
В результате экспериментов с SVG родилось приложение для детей “Учим формы и фигуры”. Ознакомится с приложением можно в Google Play:
play.google.com/store/apps/details?id=com.whisperarts.kids.forms
Количество получившихся изображений:
- PNG — 3 (сплэшскрин и 2 фона для меню);
- SVG-элементов — 97;
- Размер приложения 3,5 Мб.
В сравнении с почти похожим по функционалу нашим приложением “Учим цвета” (размер которого 8 Мб) выигрыш более 50% налицо.
Для себя мы приняли решение использовать SVG-изображения в наших приложениях, так как это существенно ускоряет процесс разработки и адаптации картинок под разные разрешения экранов, а также существенно уменьшает вес приложения.
Надеемся, опыт, которым мы поделились в статье, поможет вам также пересмотреть процесс подготовки изображений для приложений и задуматься над использованием формата SVG.
PS: Если вы уже использовали SVG в своих проектах или по другому обходили проблемы, с которыми столкнулись мы при использовании SVG — пишите в комментариях. Будем рады услышать Ваш опыт.
Источник
Android working with SVG / vector drawables
While developing Android Applications, supporting multiple resolutions are sometime nightmare to developers. Including multiple images for different resolutions also increases the project size. The solution is to use Vector Graphics such as SVG images. While Android does not support SVGs (Scalable Vector Graphics) directly, with the launch of Lollipop a new class was introduced called VectorDrawable, which allows designers and developers to draw assets in a similar fashion using only code.
Simply explained, vector graphics are a way of describing graphical elements using geometric shapes. They are particularly well suited to graphical elements created in packages such as Adobe Illustrator or Inkscape where simple geometric shapes may be combined in to much more complex elements.
1. What is VectorDrawable?
As the name implies, vector drawables are based on vector graphics, as opposed to raster graphics, vector graphics are a way of describing graphical elements using geometric shapes. It is similar to a SVG file. In Android Vector Drawable are created as XML files. Now there is no need to create different size image for mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi etc. We need only one vector file for multiple screen size devices. Also for older versions of Android that don’t support vector drawables, Vector Asset Studio can turn your vector drawables into different bitmap sizes for each screen density at runtime.
Here is the detailed information Vector Asset Studio.
Note: For this project I’m using gradle version 2.2, my suggession is to use 2.0+. However if you’re using gradle version below 2.0 then you’ll need to add some more code to the app level build.gradle file which I’ve mentioned below.
2. Creating Android Project
1. Create a new project in Android Studio from File ⇒ New Project and fill the project details.
2. Open build.gradle in app module, add the below line inside defaultConfig block.
So the complete build.gradle looks like
If you are using gradle version below 2.0 then use following
2.1 Creating VectorDrawable from Material Icons.
So let us start by creating VectorDrawables from Material Icons (Material Icons are the official icon set from Google that are designed under the material design guidelines, these icons are open source and are beautifully crafted, delightful, and easy to use in your web, Android, and iOS projects).
3. In the project Right click on the drawable directory
4. Go to New ⇒ Vector Asset
5. Click on the launcher icon to browse Material Icons
6. Select an icon and click on ok
7. Review the name of the file then click on next.
8. Now Vector Asset Studio will show the location about where the file is being saved, review it and click on finish.
9. The drawable folder will now consist of a newly created file (In my case ic_flight_takeoff)
below is the file code –
You can modify the width and height of the vector as per your requirement (by default it stays as 24dp), a vector may consist of one or more paths. A path may have many attributes among which fillColor and pathData are the most important. fillColor attribute defines the color of the path, it must be a color attribute (hash code in #aarrggbb, or may also point to a color resource). pathData defines the path shape. In this case I’ve modified the vector width, height and path fillColor.
2.2 Creating VectorDrawable from SVG or PSD
Now we have created vectorDrawable from Material Icon, what if we want a separate icon? We can create it from SVG or PSD, below are the procedure, but it also has some restrictions you can check out here – https://developer.android.com/studio/write/vector-asset-studio.html#PSD (though the restriction listed here for PSD, it applies to SVG also).
10. In the project Right click on the drawable directory
11. Go to New ⇒ Vector Asset
12. Click on the radio button saying “Local File (SVG, PSD)”
13. Click on the browse icon and navigate to your SVG or PSD file to select it
14. After selecting click ok
15. Verify the image in preview and click Next ⇒ Finish.
below is the file code generated from svg
3. Using VectorDrawable
We have successfully added VectorDrawables to the project, now it’s time to use them.
16. Open the layout file of main activity (activity_main.xml) and add the below xml. This layout contains shows how to use VectorDrawable with ImageView and other Views (as background) which is explained later in this tutorial.
The above layout generates a screen something like this.
3.1 Using VectorDrawable with ImageView from xml
So let us start with a simple ImageView, and even simpler by assigning the VectorDrawable to the ImageView through xml layout.
As you can see, we used app:srcCompat instead of android:src, so that the support library can do the rest to display the image seamlessly on all versions of android. (The support library converts VectorDrawables to raster graphics automatically on android versions below 5.0 – API 21, details are here). So it was that easy.
3.2 Using VectorDrawable with ImageView from Java
Now Let us do the same thing with Java code, i.e. assigning VectorDrawable to ImageView through Java Code.
17. Add another Vector Drawable (name it ic_android.xml) with the steps mention in section 2.1 or 2.2, you can also create a file with that name and paste the below code.
18. Open MainActivity.java and paste the below code.
Now, let me explain the code line by line.
19. For using VectorDrawable from java or to use it as background (in xml also) you need to intimate AppCompatDelegate to enable compat vector from resource. Below is the code for that.
20. Whenever you are going to use VectorDrawable from java or to use it as background (in xml also) remember to use AppCompatView instead of normal view, here I have used AppCompatImageView, please also refer to the layout we created before.
21. Now assign the VectorDrawable as src or background as you do it normally, AppCompat will take care of the rest.
22. We have added some flavors to it, We implemented OnClickListener on the ImageView to set random color filter on it, upon click.
3.3 Using VectorDrawable as Drawable with TextView
You may want to use VectorDrawable as background or as compound drawable with TextView, so below is an example.
As you can see from above code we have used a VectorDrawable in the drawableLeft attribute of android.support.v7.widget.AppCompatTextView, we can also do it from the java code using AppCompatTextView#setCompoundDrawables .
3.4 Using VectorDrawable to Customise RadioButton
So far the use was straightforward, what if you want to customise a RadioButton or CheckBox while using VectorDrawable? Now lets see that. If you go through the layout we created, once again then you can find that a RadioGroup which contains a series of AppCompatRadioButton, all the AppCompatRadioButtons there uses @drawable/radio_selector as background, below is the code for that.
As you can see this is a selector drawable as usual, the only difference is that it refers to VectorDrawables (vector image resources) instead of raster image resources. The remaining is taken care by android.support.v7.widget.AppCompatRadioButton itself as mentioned earlier (remember to use AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) for that also, even if you don’t consider backward compatibility).
The final output looks something like this.
I hope this article gave you a very good overview about Using Vector Drawables in Android. Feel free to ask any queries / doubts in comments section below.
Rivu Chakraborty is a Sr. Tech. Member of Institution of Engineers(India), Diploma holder Engineer in Computer Science. He is presently at MaxMobility Pvt. Ltd. as a Sr. Software Engineer (Android)
Источник