Уже довольно долгое время мы занимаемся разработкой детских приложений для Android, постепенно постигая множество нюансов этой платформы.
В каждом приложении нас ждут одни грабли – это фрагментация экрана.
Если сделать одно изображение только для маленького телефона, то на планшете оно будет выглядеть, мягко говоря, «не очень».
А если сделать образ в высоком разрешении для планшетов и попробовать использовать его на телефонах, то с очень большой вероятностью приложение вылетит с OutOfMemory. Вам предстоит подготовить несколько копий одного и того же изображения для разных экранов.
Облака еще больше сгущаются благодаря новому монстру Galaxy Nexus 10 с безумным разрешением 2560x1600.
В общем, неплохо было бы что-то изменить, решили мы.
Что, если вы используете в своих приложениях векторные изображения, а не растровые? Такие изображения можно легко масштабировать под разные разрешения экрана без потери качества.
Вы можете использовать только одно изображение для разных разрешений и размеров.
Сказано - сделано.
Итак, под катом история внедрения векторных изображений в одно из наших приложений.
В этой статье мы поделимся своим опытом и особенностями использования векторных изображений формата SVG в Android-приложениях.
Погуглив, мы выяснили, что векторные изображения для Интернета и приложений обычно используются в формате SVG. С этим форматом работают векторные редакторы Adobe Illustrator и Inkscape. Вооружившись Inkscape, мы нарезали тестовые изображения и начали искать способы их загрузки и отображения в Android-приложении.
Мне не хотелось разбираться с устройством формата SVG и писать свой парсер — наверняка люди сталкивались с этим до нас! Что ж, давайте погуглим «android svg».
В результате происходит:
- 2 проекта на коде Google: code.google.com/p/svg-android code.google.com/p/svg-android-2
- подробная статья с использованием NDK: horribileru.blogspot.ru/2011/10/android-imageview-svg.html
- и несколько ссылок на мертвые проекты на различных форумах.
Подключаем библиотеку, помещаем векторное изображение в res/raw, загружаем и устанавливаем во вью:
Загружаем тестовый проект с изображениями — всё отлично! Соединяем наши изображения — пусто.SVG svg = SVGParser.getSVGFromResource(getResources(), R.raw.filename); Drawable drawable = svg.createPictureDrawable(); imageView.setImageDrawable(drawable);
Как оказалось, эта библиотека поддерживает только базовый формат SVG 1.1, который не поддерживается Inkspace, но доступен только в Adobe Illustrator. Давайте попробуем вторую библиотеку SVG-Android-2, которая является ответвлением первого проекта и идет немного дальше.
Он уже понимает Inkscape, а также поддерживает другие возможности этого формата, о которых вы можете прочитать здесь .
Здесь все прошло проще, картинки загружались и отлично смотрелись как на телефоне, так и на планшете.
Вот где мы остановились.
Пример изображения SVG и изображения PNG, не адаптированного к размеру планшета.
(просмотреть изображение в оригинальном размере 1280x800 )
Первый — SVG (10 КБ), второй — PNG (22 КБ).
Второе изображение имеет размытый контур и ступенчатый градиент.
Масштабирование изображений
Изображения масштабируются только с сохранением соотношения сторон.Поэтому использовать их в качестве фона, который может немного менять пропорции при разных разрешениях, не получится.
В этом случае мы обычно делаем какое-то абстрактное изображение в PNG, которое довольно легко меняет свои пропорции для разных экранов.
Не забудьте установить для свойства AdjustViewBounds значение true для SVG, иначе изображение может рассчитать свои границы иначе, чем вы предполагали.
Размер изображений с тенями и светлыми участками
Некоторые элементы в нашем приложении изначально визуализировались с небольшими тенями и бликами — например, этот смайлик имеет серую подсветку.Но это приводит к колоссальному увеличению размера файла SVG. 118 КБ против 1 КБ без этой подсветки.
Чем больше размер файла, тем больше времени потребуется на его загрузку в программе, поэтому мы решили отказаться от этого эффекта.
Изображения с тенью и без нее: 118 КБ против 1 КБ.
Подсветку можно отключить либо в графическом редакторе, либо непосредственно отредактировав SVG-файл – убрав тег с огромным содержанием.
Отображение градиентов
На некоторых изображениях вместо фона внезапно появились черные пятна.Оказалось, что градиент не поддерживается! Проблему с градиентами решил удалением ненужных тегов из svg (описано далее в статье).
Но в принципе, мы могли бы с этим смириться и заменить градиент на равномерную заливку в наших простых изображениях, если бы не еще один нюанс — значительное время загрузки изображений.
Вот как это выглядело на экране: слева черное небо в виде градиента, справа правильная картинка.
Время загрузки изображения
Приложению требовалось 6 изображений на одной странице ViewPager, а так как они загружаются при прокрутке (если не кэшируются), интерфейс при прокрутке заметно дергался.Мне этого очень не хотелось и было решено загружать все изображения при запуске приложения.
Мы получили время инициализации около 8 секунд, что было слишком долго.
Мы решили разобраться в проблеме.
Мы скачали исходники проекта SVG-Android-2 и начали искать, что именно его тормозит. Оказалось, что XML-файл изображения парсится в классе SVGParser. дважды : первый раз собирает информацию о дополнительных атрибутах, которые используются во втором проходе.
И, что самое интересное, анализируется только атрибут xlink:href, который представляет собой своего рода гиперссылки внутри самого документа.
Наши проблемные изображения содержали именно такие ссылки, и они никуда не вели.
После того, как мы избавились от этих ссылок, отредактировав SVG-код на некоторых изображениях, градиент стал отображаться корректно.
Более того, убрав этот предварительный проход и немного оптимизировав процесс загрузки, мы смогли снизить скорость загрузки с 8 секунд до 1,8-2. Следует отметить, что это сравнимо с PNG среднего размера — загрузка тех же изображений в память заняла 1,7 секунды.
Ниже приведено сравнение загрузки 35 файлов SVG и PNG.
SVG | PNG (~500x500) | |
Размер, КБ | 327 | 943 |
Время загрузки, с | 1,9 | 1,7 |
Прозрачность и цветные фильтры
Часто в играх мы используем полупрозрачные изображения для неактивных элементов.В этом проекте помимо прозрачности нужны были цветовые фильтры для генерации элементов в играх, то есть чтобы один элемент можно было использовать один раз, но раскрасив его по-разному, мы бы получили разные элементы.
Оказалось, что мы не можем использовать ни альфа, ни colorFilter, потому что библиотека загружает не типичный bitmapDrawable, а PictureDrawable, а в исходниках Android мы видим пустые методы для этого класса: @Override
public void setColorFilter(ColorFilter colorFilter) {}
@Override
public void setAlpha(int alpha) {}
Мы никогда раньше не сталкивались с классом PictureDrawable, и это стало большим сюрпризом.
Снова покопавшись в исходниках библиотеки, мы обнаружили в классе SVGHandler поле fillPaint типа Paint, которое используется для отрисовки всех компонентов.
Если вы установите colorFilter для элемента перед его загрузкой, он будет работать как положено.
Нас это вполне устроило, поэтому мы немного изменили способ загрузки SVG, добавив возможность передавать цвет фильтра, который при необходимости задается перед загрузкой изображения.
Теперь изображения загружаются так: SVG svg = SVGParser.getSVGFromResource(getResources(), rawSvgId, filterColor);
А в самом SVGHandler появился такой метод: public void setFilterColor(int filterColor) {
fillPaint.setColorFilter(new PorterDuffColorFilter(filterColor, Mode.MULTIPLY));
}
В результате нам удалось получить из одной картинки столько изображений разных оттенков, сколько мы хотели.
Для fillPaint также можно задать Alpha, но в играх это свойство требуется в динамическом виде (нажмите на элемент — он становится полупрозрачным), а загружать каждый раз новое изображение неудобно.
Поэтому этот эффект был заменен масштабированием (нажал — элемент стал меньше).
Нюанс с принудительной обработкой GPU
После запуска приложения мы начали получать следующие ошибки: java.lang.UnsupportedOperationException
at android.view.GLES20Canvas.drawPicture(GLES20Canvas.java:895)
at android.graphics.drawable.PictureDrawable.draw(PictureDrawable.java:73)
Оказалось, что если на устройстве включена настройка «Принудительный рендеринг с помощью графического процессора» (Параметры разработчика — Принудительный рендеринг с помощью графического процессора), то наше приложение вылетает, поскольку метод drawPicture() Canvas не поддерживает аппаратное ускорение.
Вы можете прочитать об этом на Android-разработчик .
Более того, простое указание в манифесте android:hardwareAccelerated="false" не решает проблему — пользовательский флажок в настройках имеет более высокий приоритет. Было найдено довольно простое решение: для всех представлений, которые работают с нашими PictureDrawables, полученными из SVG, отключаем аппаратное ускорение.
Поскольку функция аппаратного ускорения появилась в Android 3.0 (api 11), для работы с этой функциональностью нам пришлось изменить целевой SDK нашего проекта с 8 на 11. И, конечно, надо помнить об обратной совместимости — эти методы не доступно на более ранних платформах.
public static void setSoftwareLayerType(View view) {
try {
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} catch (NoSuchMethodError e) {
//Do nothing - this happens on API < 11
}
}
выводы
Подведем итоги работы с векторными изображениями SVG в Android. Плюсы:- Один огромный плюс, из которого вытекают все остальные, это одно векторное изображение .
- Поскольку изображение векторное, оно отлично отображается на экранах всех размеров.
- Размер изображений SVG небольшой.
- Одно изображение используется несколько раз для разных разрешений.
- Сокращается процесс подготовки изображений для приложения.
- Изображения масштабируются только пропорционально.
- Прозрачность не поддерживается.
- Графику нужно упростить — чем больше векторных элементов, тем больше весит файл.
Нежелательно использовать тени и свечения, так как это многократно увеличивает размер SVG-файлов.
Посмотреть приложение можно в Google Play: play.google.com/store/apps/detailsЭid=com.whisperarts.kids.forms Количество полученных изображений:
- PNG — 3 (заставка и 2 фона для меню);
- SVG-элементов – 97;
- Размер приложения составляет 3,5 МБ.
Для себя мы решили использовать в наших приложениях SVG-изображения, так как это значительно ускоряет процесс разработки и адаптации изображений под разные разрешения экрана, а также существенно снижает вес приложения.
Надеемся, что опыт, которым мы поделились в этой статье, поможет вам также пересмотреть процесс подготовки изображений для приложений и задуматься об использовании формата SVG. PS: Если вы уже использовали SVG в своих проектах или иным образом обошли проблемы, с которыми мы столкнулись при использовании SVG, напишите в комментариях.
Мы будем рады услышать ваш опыт. Теги: #Android #svg #мобильная разработка #детские приложения #Разработка мобильных приложений #Разработка Android
-
Основы И Советы По Поисковой Оптимизации
19 Oct, 24 -
Создаем Свои Теги В Тестах Rspec
19 Oct, 24 -
Итоги Topcoder Open 2009
19 Oct, 24 -
Эластикс 5 Бета
19 Oct, 24 -
5 Минут Славы
19 Oct, 24