Использование SVG ресурсов в Xamarin

от автора

При разработке мобильного приложения есть масса моментов, на которые необходимо обращать внимание. Это и выбор технологии, на которой оно будет написано, и разработка архитектуры приложения, и, собственно, написание кода. Рано или поздно наступает момент, когда костяк приложения есть, вся логика прописана и приложение , в общем-то, работает, но… нет внешнего вида. Тут стоит задуматься о графических ресурсах, которые будут использованы, поскольку графика составляет львиную долю размера итоговой сборки, будь то .apk на Android или .ipa на iOS. Сборки огромных размеров в принципе ожидаемы для мобильных игр, уже сейчас из PlayMarket порой приходится загружать объемы данных вплоть до 2 Гб и хорошо, если во время загрузки есть возможность подключиться к Wi-Fi или мобильный оператор предоставляет скоростное безлимитное подключение. Но для игр это ожидаемо, а бизнес-приложение, обладающее таким размером, невольно вызывает вопрос “Откуда столько?”. Одной из причин большого размера сборки бизнес-приложения может стать значительное количество иконок и картинок, которые в нем приходится отображать. А также не следует забывать о том, что большое количество графики пропорционально влияет на быстродействие приложения.

При создании графической составляющей приложения часто возникает серьезная проблема. Мобильных устройств существует великое множество начиная с часов и заканчивая планшетами, и разрешения их экранов очень разнятся. Из-за этого зачастую приходится включать в сборку графические ресурсы отдельными файлами для каждого из существующих типов. По 5 копий для Android и по 3 для iOS. Это существенно влияет на размер итоговой сборки, которую Вы будете выкладывать в сторы.

О том, что можно сделать для того, чтобы не попасть в такую ситуацию, мы расскажем в этой статье.

Сравнение форматов PNG и SVG

Основное различие между форматами PNG и SVG заключается в том, что PNG — формат растровой графики, а SVG — векторной.

Растровое изображение представляет собой сетку пикселей на мониторе и используется повсеместно, будь то небольшие иконки или огромные баннеры. Среди преимуществ этого типа графики также следует отметить следующее:

  1. Растровая графика позволяет создавать рисунки практически любой сложности, без ощутимых потерь в размере файла;
  2. Если не требуется масштабирование изображения, то скорость обработки сложных изображений весьма высока;
  3. Растровое представление изображений естественно для большинства устройств ввода-вывода.

Однако, есть и минусы.

Например, растровое изображение невозможно идеально масштабировать. При увеличении небольшой картинки изображение «мылится» и видно пиксели из которых оно состоит.

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

Формат SVG, в свою очередь, является не совсем изображением. Согласно статье на википедии SVG является языком разметки масштабируемой векторной графики, что позволяет читать и при необходимости редактировать файл.

Также, будучи языком разметки, SVG позволяет внутри документа применять фильтры (например, размытие, выдавливание и т.д.). Они объявляются тегами, за визуализацию которых отвечает средство просмотра, а значит, они не влияют на размер исходного файла.

Помимо перечисленного, формат SVG имеет еще ряд преимуществ:

  1. Будучи векторным форматом, SVG позволяет масштабировать любую часть изображения без потерь в качестве;
  2. В SVG-документ можно вставлять растровую графику;
  3. Легко интегрируется с HTML- и XHTML-документами.

Однако, есть и недостатки данного формата:

  1. Чем больше в изображении мелких деталей, тем быстрее растет размер SVG-данных. В ряде случаев, SVG не только не дает преимуществ, но и проигрывает растру;
  2. Сложность использования в картографических приложениях, поскольку для корректного отображения малой части изображения, требуется прочитать документ целиком.

В случае с разработкой мобильных приложений на платформе Xamarin есть еще один недостаток.

Для корректной работы с этим форматом необходимо подключать дополнительные библиотеки, либо искать обходные пути.

Работа с форматом SVG в Xamarin.Android

Как уже было сказано выше, Xamarin “из коробки” не поддерживает работу с форматом SVG. Чтобы решить эту проблему – можно использовать сторонние библиотеки или VectorDrawable. В последнее время разработчики все чаще отдают предпочтение последним, так как большая часть библиотек для Xamarin ориентирована на кроссплатформенное использование в Xamarin.Forms, а решения для нативного андроида либо больше не поддерживаются их разработчиками и устарели, либо возникают серьезные проблемы при попытке создать на их основе библиотеку для Xamarin. В связи с этим здесь рассмотрим использование VectorDrawable.

Для реализации данного подхода потребуется использовать Vector Asset Studio. Найти ее достаточно просто для этого всего лишь нужна Android Studio. Итак, начнем:

  1. Создаем новый проект в Android Studio с помощью File -> New -> New Project;
    Поскольку все, для чего нам понадобится этот проект — это использование Vector Asset Studio, то его можно создать пустым.
  2. Правый клик на папке drawable -> New -> Vector Asset;

    Это откроет Asset Studio. Она предоставляет возможность конвертировать иконки из Material Design, а также файлы SVG и PSD, в xml-файлы. Таким образом мы сможем использовать их в приложении.

  3. Окно Asset Studio

    Здесь мы выбираем, что именно нужно сделать, размер итогового изображения и настраиваем прозрачность.

  4. По нажатию на кнопку Next открывается окно сохранения итогового файла

    После его сохранения мы сможем использовать его в своем проекте Xamarin.Android.

Достаточно емкая подготовительная работа, требующая наличия установленной Android Studio. К сожалению, на момент написания статьи нам не удалось найти корректно работающих онлайн-конвертеров. Некоторые при попытке конвертации представленного файла SVG выдавали ошибку, некоторые указывали, что лучше воспользоваться Vector Asset Studio.

Как бы то ни было, мы получили требуемый файл и теперь можем использовать его в своем проекте. Не будем описывать процесс создания проекта Xamarin.Android, перейдем сразу к сути.
Первым делом мы поместили полученный файл в папку drawable проекта, затем в качестве демонстрации работы с этим файлом, создали 3 ImageView на экране.

код здесь

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 	xmlns:android        ="http://schemas.android.com/apk/res/android" 	xmlns:app            ="http://schemas.android.com/apk/res-auto" 	android:layout_width ="match_parent" 	android:layout_height="match_parent" 	android:minWidth="25px" 	android:minHeight="25px"> 	<ImageView 		android:layout_width="150dp" 		android:layout_height="150dp" 		android:layout_alignParentLeft="true" 		android:id="@+id/imageView1" 		android:adjustViewBounds="true" 		app:srcCompat="@drawable/question_svg" 		android:background="@android:color/holo_red_dark"/> 	<ImageView 		android:layout_width="150dp" 		android:layout_height="150dp" 		android:layout_alignParentRight="true" 		android:id="@+id/imageView2" 		android:adjustViewBounds="true" 		app:src="@drawable/question" 		android:background="@android:color/holo_red_dark"/> 	<ImageView 		android:layout_width="150dp" 		android:layout_height="150dp" 		android:id="@+id/imageView3" 		android:adjustViewBounds="true" 		android:layout_marginTop="30dp" 		app:srcCompat="@drawable/question" 		android:layout_below="@id/imageView1"/> </RelativeLayout> 

В первом ImageView мы попытались присвоить оригинальный файл SVG в свойстве android:src, во втором — конвертированный XML-файл в том же свойстве, а в третьем — в свойстве app:srcCompat (обратите внимание, что это пространство имен необходимо прописать как указано в коде).
Результаты были заметны уже в дизайнере, до запуска приложения — единственно верный способ корректно отобразить наш файл, приведен в третьем ImageView.

Теперь необходимо убедиться, что изображение отображается корректно на разных устройствах. Для сравнения мы выбрали Huawei Honor 20 Pro и Nexus 5. А также, вместо некорректных способов отображения указали нужный и изменили размеры ImageView на 150х150 dp, 200×200 dp и 300х300 dp.

Результаты работы

Huawei Honor 20 Pro

Nexus 5

Как видно, качество изображений одинаковое, а значит, мы добились нужного результата.

Полученный файл используется из кода так же, как и обычно
image.SetImageResource(Resource.Drawable.<имя файла>);

Работа с форматом SVG в Xamarin.iOS

В случае с Xamarin.iOS дело обстоит так же, как и с Xamarin.Android, в том смысле, что “из коробки” работа с SVG-файлами не поддерживается. Однако, для ее реализации не требуется особых усилий. В случае нативной iOS-разработки для использования SVG требуется подключить SVGKit. Он позволяет обрабатывать такие файлы, не прибегая к сторонним решениям. В случае с Xamarin.iOS мы можем использовать библиотеку SVGKit.Binding. Это С# обертка над оригинальной SVGKit.

Все, что нам потребуется это подключить NuGet-пакет в наш проект. На момент написания статьи последняя версия — 1.0.4.

К сожалению, судя по всему, поддержка этой библиотеки прекращена, но для использования в проекте она вполне годится.

Здесь реализован класс SVGKImage (собственно SVG-изображение) и SVGKFastImageView (контрол для отображения таких изображений).

код для использования

void CreateControls() { 	var image_bundle_resource = new SVGKImage("SVGImages/question.svg"); 	img1 = new SVGKFastImageView(image_bundle_resource); 	Add(img1); 	img1.Frame = new CGRect(View.Frame.Width / 4, 50, View.Frame.Width / 2, View.Frame.Width / 2);  	var image_content = new SVGKImage(Path.Combine(NSBundle.MainBundle.BundlePath, "SVGImages/question1.svg")); 	img2 = new SVGKFastImageView(image_content); 	Add(img2); 	img2.Frame = new CGRect(5, img1.Frame.Bottom + 5, View.Frame.Width - 5, View.Frame.Width - 5); } 

Для создания SVGKImage в библиотеке реализовано два варианта в зависимости от BuildAction файла — BundleResource (в коде обозначен image_bundle_resource) и Content (в коде обозначен image_content).
К сожалению, в библиотеке есть ряд недостатков:

  1. У SVGKFastImageView не поддерживается инициализация без предоставления SVGKImage — выдается ошибка, указывающая на необходимость использования приведенного в коде конструктора;
  2. Нет возможности использования SVG-файлов в других контролах. Однако, если в этом нет необходимости, то использовать ее можно.

Итог работы программы

В случае если для приложения критично использование SVG-файлов не только в ImageView, можно воспользоваться другими библиотеками. Например, FFImageLoading. Это мощная библиотека, позволяющая не только выгружать SVG-изображения в родной для Xamarin.iOS UIImage, но и делать это напрямую в нужный контрол, экономя время на разработку. Также имеется функционал для загрузки изображений из интернета, но рассматривать его в этой статье мы не будем.

Для использования библиотеки понадобится подключить в проект два NuGet-пакета — Xamarin.FFImageLoading и Xamarin.FFImageLoading.SVG.

В тестовом приложении мы использовали метод, загружающий SVG-изображение в UIImageView напрямую и выгружающий изображение в родной UIImage.

Пример кода

private async Task CreateControls() {     img1 = new UIImageView();     Add(img1);     img1.Frame = new CGRect(View.Frame.Width / 4, 50, View.Frame.Width/2, View.Frame.Width/2);      ImageService.Instance                 .LoadFile("SVGImages/question.svg")                 .WithCustomDataResolver(new SvgDataResolver((int)img1.Frame.Width, 0, true))                 .Into(img1);      var button = new UIButton() { BackgroundColor = UIColor.Red};     Add(button);     button.Frame = new CGRect(View.Frame.Width/2 - 25, img1.Frame.Bottom + 20, 50, 50);      UIImage imageSVG = await ImageService.Instance                 .LoadFile("SVGImages/question.svg")                 .WithCustomDataResolver(new SvgDataResolver((int)View.Frame.Width, 0, true))                 .AsUIImageAsync();     if(imageSVG != null)         button.SetBackgroundImage(imageSVG, UIControlState.Normal); } 

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

Пример кода

    var svgString = @"<svg><rect width=""30"" height=""30"" style=""fill:blue"" /></svg>";      UIImage img = await ImageService.Instance 		.LoadString(svgString) 		.WithCustomDataResolver(new SvgDataResolver(64, 0, true)) 		.AsUIImageAsync(); 

В библиотеке также имеется еще ряд функционала, однако рассматривать его здесь мы не будем. Единственное, о чем действительно стоит упомянуть, данная библиотека может быть использована и в Xamarin.Android проектах. Для использования в проектах Xamarin.Forms есть аналоги этой библиотеки, о работе с которыми мы расскажем далее.

Работа с форматом SVG в Xamarin.Forms

По большому счету необязательно искать библиотеки для форм, можно просто подключить известные в проекты платформ и долго, и муторно переписывать рендеры для каждого контрола, которым вы захотите воспользоваться. К счастью, есть решение, о котором мы расскажем.

Это уже описанные библиотеки Xamarin.FFImageLoading и Xamarin.FFImageLoading.SVG, точнее их вариант для Xamarin.Forms (Xamarin.FFImageLoading.Forms и Xamarin.FFImageLoading.SVG.Forms). Как при работе с большинством библиотек в Xamarin.Forms перед тем, как ее использовать, необходима небольшая стартовая настройка проекта:

  1. Во все проекты (PCL и проекты платформ) необходимо добавить два NuGet-пакета — Xamarin.FFImageLoading.Forms и Xamarin.FFImageLoading.SVG.Forms;
  2. Прописать в AppDelegate.cs iOS-проекта:
    public override bool FinishedLaunching( UIApplication app, NSDictionary options ) {     global::Xamarin.Forms.Forms.Init();     FFImageLoading.Forms.Platform.CachedImageRenderer.Init();     CachedImageRenderer.InitImageSourceHandler();     var ignore = typeof(SvgCachedImage);     LoadApplication(new App());     return base.FinishedLaunching(app, options); }
  3. Прописать в MainActivity.cs Android-проекта:
    protected override void OnCreate( Bundle savedInstanceState ) {     TabLayoutResource = Resource.Layout.Tabbar;     ToolbarResource = Resource.Layout.Toolbar;     base.OnCreate(savedInstanceState);      Xamarin.Essentials.Platform.Init(this, savedInstanceState);     global::Xamarin.Forms.Forms.Init(this, savedInstanceState);     FFImageLoading.Forms.Platform.CachedImageRenderer.Init(true);     CachedImageRenderer.InitImageViewHandler();     var ignore = typeof(SvgCachedImage);     LoadApplication(new App()); } 

После этого библиотекой можно пользоваться.
В библиотеке реализованы обращения к SVG-ресурсам, находящимся как в платформенных проектах, так и EmbeddedResouce PCL проекта. Для отображения SVG-картинок используется SvgCachedImage. В отличие от оригинального контрола Image, использующегося в Xamarin.Forms и принимающего в себя ImageSource, SvgCachedImage работает с SvgImageSource.

ВАЖНО!

Перед использованием пространства имен svg, показанного далее, необходимо его объявить:

xmlns:svg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"

Предоставить его можно несколькими способами:

  1. Указать в XAML-файле имя SVG-файла, находящегося в ресурсах проектов платформ:
    <svg:SvgCachedImage             Source="question.svg"             WidthRequest="100"             HeightRequest="100"/> 
  2. Указать в XAML-файле полный путь к файлу SVG, являющегося встроенным ресурсом PCL-проекта:
    <svg:SvgCachedImage             Source="resource://SVGFormsTest.SVG.question1.svg"             WidthRequest="100"             HeightRequest="100"/> 

    При загрузке из встроенных ресурсов можно указать имя другой сборки:

    resource://SVGFormsTest.SVG.question1.svg?assembly=[ASSEMBLY FULL NAME] 
  3. При работе из кода сначала даем контролы имя:
    <svg:SvgCachedImage             x:Name="image"             WidthRequest="100"             HeightRequest="100"/> 

    Затем, в зависимости от того какой ресурс используется, выбираем один из вариантов:

    • для использования встроенных ресурсов
      image.Source = SvgImageSource.FromResource("SVGFormsTest.SVG.question1.svg");

    • для использования платформенных ресурсов
      image.Source = SvgImageSource.FromFile("question.svg");

  4. С помощью Binding есть 2 варианта:
    • хранить в переменной модели, на которую осуществлена привязка самого SvgImageSource. В этом случае со стороны кода ничего не изменяется, а в XAML-файле остается стандартная привязка:
      <svg:SvgCachedImage             Source="{Binding Source}"             WidthRequest="100"             HeightRequest="100"/> 
    • хранить в переменной модели строку с путем к файлу либо ко встроенному ресурсу. В этом случае необходимо использование конвертера, предоставляемого библиотекой. Для этого его нужно добавить в ресурсы страницы:
      <ContentPage.Resources>         <ResourceDictionary>             <svg:SvgImageSourceConverter                 x:Key="SourceConverter"/>         </ResourceDictionary>     </ContentPage.Resources> 

      затем указать необходимость его использования при привязке:

      <svg:SvgCachedImage             Source="{Binding Source1, Converter={StaticResource SourceConverter}}"             WidthRequest="100"             HeightRequest="100"/> 

      В переменную модели можно передавать строку с именем файла для ресурса платформы:

      public MainVM() {     source1 = "question.svg"; }  string source1; public string Source1 {     get => source1;     set     {         source1 = value;     } } 

      или путь к данным встроенного ресурса:

      public MainVM() {     source1 = "resource://SVGFormsTest.SVG.question1.svg"; }  string source1; public string Source1 {     get => source1;     set     {         source1 = value;     } } 

      Однако, при предоставлении пути к файлу встроенного ресурса в качестве строки конвертер не срабатывает и изображение не загружается. Разумеется, можно создать свой конвертер, наследовать его от предоставленного библиотекой и обработать этот случай.

Недостатки и исключения

Недостатки использования SVG-ресурсов в проектах, прямо вытекают из недостатков самого формата. К примеру, если графическая составляющая вашего приложения очень сложна и в ней должны использоваться изображения с множеством мелких деталей, градиентом, большим количеством цветовых переходов, и таких изображений не одно и не два, то использовать их, как SVG-ресурсы может быть невыгодно. Кроме того, PNG-файлы готовы к отображению быстрее, чем SVG, поэтому для использования на экране загрузки приложения подходят больше.

Еще у SVG-ресурсов есть незначительный минус. Их нельзя предоставить в качестве иконки приложения, так как в соответствии с гайд-лайнами Google и Apple в качестве иконки могут использоваться только PNG и JPEG файлы.

ссылка на оригинал статьи https://habr.com/ru/post/516576/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *