在我们的应用程序中,我们使用部分嵌入资源但也由 RestService 加载的 svg 图像文件,因为大多数这些图标都可以由用户自己添加和命名。 嵌入资源不是问题,您可以使用转换器将文件扩展从 .svg 更改为 .png,但这不适用于来自 url 的 svg 图像或来自 Microsoft.Maui.Storage.FileSystem.AppDataDirectory 或 Environment.SpecialFolder 的 svg 图像。本地应用程序数据路径。我们的 Xamrin.forms 应用程序使用 FFImageLoading 库来执行此操作,但我们现在将此应用程序迁移到 .net Maui,我找不到任何其他方式来显示 svg 图像。有没有人可以帮助我并有一个很好的解决方案来解决这个问题。
这就是我们下载 svg 文件的方式:
private async Task DownloadIcons(List<DownloadIcon> downloadIcons)
{
var lightIconsFolderPath = Path.Combine(iconFolderPath, lightIconFolderName);
var darkIconsFolderPath = Path.Combine(iconFolderPath, darkIconFolderName);
DirectoryManager(lightIconsFolderPath);
DirectoryManager(darkIconsFolderPath);
CurrentIcons = new List<Icon>();
foreach (var downloadIcon in downloadIcons)
{
Icon icon = new Icon();
icon.IconId = downloadIcon.IconId;
var ligthIconBytes = await
DownloadIconFromURL(downloadIcon.LightThemeIconLink);
icon.LightThemeIconPath = await WriteIconToDisk(lightIconsFolderPath, ligthIconBytes, downloadIcon.IconTranslations[0].Name);
var darkIconBytes = await DownloadIconFromURL(downloadIcon.DarkThemeIconLink);
icon.DarkThemeIconPath = await WriteIconToDisk(darkIconsFolderPath, darkIconBytes, downloadIcon.IconTranslations[0].Name);
CurrentIcons.Add(icon);
}
}
private async Task<byte[]> DownloadIconFromURL(string downloadedIconUrl)
{
try
{
byte[] bytes;
using (HttpClient httpClient = new HttpClient())
{
bytes = await httpClient.GetByteArrayAsync(downloadedIconUrl);
}
return bytes;
}
catch (System.Exception e)
{
log.Error("Exception while trying to download icon from url", e);
return null;
}
}
这里我们将图标写入磁盘:
private async Task<string> WriteIconToDisk(string pathToIconsFolder, byte[] iconBytes, string iconName)
{
try
{
string fileName = "";
string namingConventionName = ModifyLowerCaseLetterString(iconName);
fileName = Path.Combine(pathToIconsFolder, $"{namingConventionName}.svg");
File.WriteAllBytes(fileName, iconBytes);
return fileName;
}
catch (System.Exception e)
{
log.Error("Exception while trying to write icon on device", e);
return null;
}
}
xaml 中的图像:
<Image HeightRequest="24" Source="{Binding Icon.ActiveIconPath, Converter={StaticResource SvgImageSourceConverter}}"
WidthRequest="24" />
最后我通过转换器追踪它后,图像源得到如下路径:/storage/emulated/0/Android/data/[company]/files/icons/light/light.png 我也尝试过使用 .svg 而不是 .png。
将
SVG image
下载到本地目录后,您可以在 XAML 中直接将其引用为 PNG
,无需将其转换为 PNG
文件,这使得它可以在所有平台上工作:
<Image
Source="svgfilename.png"
SemanticProperties.Description="The SVG image scaled to a 100x100 PNG"
WidthRequest="100"
HeightRequest="100" />
为了解决这个问题并显示下载的 svg 图像,我创建了自己的 svg 控件。
public class SvgView : SKCanvasView, IDisposable
{
public static readonly BindableProperty PathProperty = BindableProperty.Create(
nameof(Path),
typeof(string),
typeof(SvgView),
defaultValue: default(string));
public static readonly BindableProperty DrawOnEntireSpaceProperty = BindableProperty.Create(
nameof(DrawOnEntireSpace),
typeof(bool),
typeof(SvgView),
defaultValue: false);
public string Path
{
get { return (string)GetValue(PathProperty); }
set { SetValue(PathProperty, value); }
}
public bool DrawOnEntireSpace
{
get { return (bool)GetValue(DrawOnEntireSpaceProperty); }
set { SetValue(DrawOnEntireSpaceProperty, value); }
}
private ILog log = LogManager.GetLogger<SvgView>();
private SKImageInfo info;
private SKSurface surface;
private SKCanvas canvas;
private SKSvg Svg { get; set; }
private SKPicture SKPicture { get; set; }
private SKCanvas Canvas
{
get => canvas;
set
{
canvas = value;
OnPropertyChanged(nameof(Canvas));
}
}
public SvgView()
{
BackgroundColor = Colors.Transparent;
this.Svg = new SKSvg();
this.SizeChanged += SvgView_SizeChanged;
InvalidateSurface();
}
~SvgView()
{
Dispose(false);
}
private void SvgView_SizeChanged(object sender, EventArgs e)
{
InvalidateSurface();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
SizeChanged -= SvgView_SizeChanged;
}
protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
try
{
if (e.Surface is null)
{
return;
}
e.Surface.Canvas.Clear(SKColors.Transparent);
e.Surface.Canvas.ResetMatrix();
base.OnPaintSurface(e);
info = e.Info;
surface = e.Surface;
Canvas = surface.Canvas;
Canvas.Clear();
if (Path != null)
{
var extension = System.IO.Path.GetExtension(this.Path);
if (extension == ".svg")
DrawPicture(Ioc.Default.GetService<IIconService>().GetCachedImage(Path));
}
}
catch(Exception ex)
{
log.Error($"Failed to draw svg image: {ex.Message}");
}
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(Path))
{
CacheSKPicture();
InvalidateSurface();
}
if (propertyName == nameof(DrawOnEntireSpace))
{
if (DrawOnEntireSpace && DeviceInfo.Platform == DevicePlatform.iOS)
{
var displayInfo = DeviceDisplay.MainDisplayInfo;
double screenWidth = displayInfo.Width / 2;
this.HeightRequest = this.Height;
this.WidthRequest = screenWidth;
InvalidateSurface();
}
}
}
private void CacheSKPicture()
{
try
{
if (!string.IsNullOrEmpty(Path))
{
if (Ioc.Default.GetService<IIconService>().GetCachedImage(Path) == null)
{
using (var stream = File.OpenRead(Path))
{
Svg.Load(stream);
}
Ioc.Default.GetService<IIconService>().CacheImage(Svg.Picture, Path);
}
}
}
catch (Exception ex)
{
log.Info("Icon with path: " + Path + " can't get load by stream: " + ex.Message);
}
}
private void DrawPicture(SKPicture picture)
{
Canvas.Translate(info.Width / 2f, info.Height / 2f);
var bounds = picture.CullRect;
var xRatio = info.Width / bounds.Width;
var yRatio = info.Height / bounds.Height;
xRatio *= .95f;
yRatio *= .95f;
var ratio = Math.Min(xRatio, yRatio);
Canvas.Scale(ratio);
Canvas.Translate(-bounds.MidX, -bounds.MidY);
Canvas.Clear();
Canvas.DrawPicture(picture);
}
}
使用此命名空间:
using SkiaSharp;
using SkiaSharp.Views.Maui;
using SkiaSharp.Views.Maui.Controls;
using System.Runtime.CompilerServices;
using SKSvg = SkiaSharp.Extended.Svg.SKSvg;
不需要你使用CacheSKPicture方法就可以使用
using (var stream = File.OpenRead(Path))
{
Svg.Load(stream);
}
在OnPaintSurface方法中然后调用
DrawPicture(Svg.Picture)
加载 svg 流后,您可以使用该控件进行操作,并根据需要找到自己的方式来缓存图像,您现在可以像这样使用该控件:
<controls:SvgView
Opacity="{Binding ImageOpacityLevel}"
Path="Your_Path"
DrawOnEntireSpace="True"/>