如何在毛伊岛下载后显示 svg 图像

问题描述 投票:0回答:2

在我们的应用程序中,我们使用部分嵌入资源但也由 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。

image svg maui
2个回答
3
投票

SVG image
下载到本地目录后,您可以在 XAML 中直接将其引用为
PNG
,无需将其转换为
PNG
文件,这使得它可以在所有平台上工作:

<Image 
    Source="svgfilename.png"
    SemanticProperties.Description="The SVG image scaled to a 100x100 PNG"
    WidthRequest="100"
    HeightRequest="100" />

0
投票

为了解决这个问题并显示下载的 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"/>
© www.soinside.com 2019 - 2024. All rights reserved.