【问题标题】:How to load a svg image into tab header in Xamarin如何将 svg 图像加载到 Xamarin 中的选项卡标题中
【发布时间】:2020-10-17 01:05:43
【问题描述】:

我正在构建一个简单的应用程序,它有 3 个选项卡,每个选项卡都有一个图标和一个文本。 我的代码基于的示例项目存在于此处:

link

我发现它的唯一问题是,它使用像素化图像作为标签标题,我想将其更改为 SVG 资源文件。

我添加了一个 FFImageLoading 库并从这里添加了 svg 支持

link

我设法将图像添加到其中一个选项卡内容页面,但我没有看到我试图在标题中看到的 svg 图像。

这是我目前的代码:

TodayPage.xaml.cs

public partial class TodayPage : ContentPage
    {
        public TodayPage()
        {
            InitializeComponent();
            InitializeComponent();
            // IconImageSource = "today.png"; <-- this will show the pixilated image
            IconImageSource = SvgImageSource.FromResource("today.svg"); //<--not working
            Title = "Today";
        }
    }

TodayPage.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:forms="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
             x:Class="TabsApp.TodayPage">
    <ContentPage.Content>
        <StackLayout>
            <forms:SvgCachedImage WidthRequest="200" HeightRequest="200" Source="resource://TabsApp.Resources.today.svg"/>
            <Label Text="Today's appointments go here going" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

MainPage.xaml

<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:local="clr-namespace:TabsApp;assembly=TabsApp" 
            x:Class="TabsApp.MainPage">
    <local:TodayPage />
    <NavigationPage Title="Schedule" IconImageSource="schedule.png">
        <x:Arguments>
            <local:SchedulePage />
        </x:Arguments>
    </NavigationPage>
    <local:SettingPage />
</TabbedPage>

谢谢

编辑

感谢 Leon,我设法在 Android 中加载了 svg 文件。 我仍在努力在 iOS 中加载 svg 文件。

我设法在 iOS 中创建了一个自定义选项卡渲染器,代码如下:

[assembly: ExportRenderer(typeof(TabPage), typeof(PageTabRenderer))]
    namespace TabsApp.iOS
{
    [Foundation.Preserve(AllMembers = true)]
    public class PageTabRenderer : TabbedRenderer
    {
        protected override async Task<Tuple<UIImage, UIImage>> GetIcon(Page page)
        {
            var navigationPage = page as NavigationPage;
            if (navigationPage != null && navigationPage.CurrentPage != null)
            {
                var imageSource = navigationPage.IconImageSource == null ? navigationPage.CurrentPage.IconImageSource : navigationPage.IconImageSource;
                return await this.GetNativeUIImage(imageSource);
            }

            return await this.GetNativeUIImage(page.IconImageSource);
        }

        private async Task<Tuple<UIImage, UIImage>> GetNativeUIImage(ImageSource imageSource)
        {
            var imageicon = await GetNativeImageAsync(imageSource);
            return new Tuple<UIImage, UIImage>(imageicon, null);
        }

        private async Task<UIImage> GetNativeImageAsync(ImageSource imageSource)
        {
            if (imageSource is FileImageSource fileImage && fileImage.File.Contains(".svg"))
            {
                var imageicon = await ImageService.Instance.LoadFile(fileImage.File).WithCustomDataResolver(new SvgDataResolver(15, 15, true)).AsUIImageAsync();
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
            }
            
            else
            {
                var imageicon = await GetUIImage(imageSource);
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
            }
        }
        
        private Task<UIImage> GetUIImage(ImageSource imageSource)
        {
            var handler = GetImageSourceHandler(imageSource);
            return handler.LoadImageAsync(imageSource);
        }

        private static IImageSourceHandler GetImageSourceHandler(ImageSource source)
        {
            IImageSourceHandler sourceHandler = null;
            if (source is UriImageSource)
                sourceHandler = new ImageLoaderSourceHandler();
            else if (source is FileImageSource)
                sourceHandler = new FileImageSourceHandler();
            else if (source is StreamImageSource)
                sourceHandler = new StreamImagesourceHandler();
            else if (source is FontImageSource)
                sourceHandler = new FontImageSourceHandler();

            return sourceHandler;
        }
      
        
        
    }
    
}

这是我的 mainpage.xaml

<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:local="clr-namespace:TabsApp;assembly=TabsApp" 
            x:Class="TabsApp.MainPage">
    <local:TodayPage />
    <NavigationPage Title="Schedule" IconImageSource="today1.svg">
        <x:Arguments>
            <local:SchedulePage />
        </x:Arguments>
    </NavigationPage>
    <local:SettingPage />
</TabbedPage> 

我还复制了 TabPage 类,但没有看到 svg 图标加载。我尝试调试它,似乎从未调用过 GetIcon。

****** 编辑 2 *******

在到处玩弄代码之后,我设法弄明白了。如果有人有兴趣实现类似的设计

ma​​in.xaml

    <?xml version="1.0" encoding="UTF-8"?>

<custom:TabPage xmlns="http://xamarin.com/schemas/2014/forms"
                xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                xmlns:local="clr-namespace:TabsApp;assembly=TabsApp"
                xmlns:custom="clr-namespace:TabsApp.custom;assembly=TabsApp"
                x:Class="TabsApp.MainPage"
                xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
                android:TabbedPage.ToolbarPlacement="Bottom"
                android:TabbedPage.IsSmoothScrollEnabled="True"
                android:TabbedPage.IsSwipePagingEnabled="False"
                xmlns:iOS="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
                xmlns:ffimageloadingsvg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
                iOS:Page.UseSafeArea="true" NavigationPage.HasNavigationBar="False"
                BarTextColor="{DynamicResource SecondaryTextColor}" UnselectedTabColor="Black" SelectedTabColor="Blue"
                NavigationPage.HasBackButton="False">
   
   
            <local:TodayPage IconImageSource="{x:OnPlatform Android=ic_today, iOS=today-24px.svg}"/>
   
    
   
            <local:SchedulePage IconImageSource="{x:OnPlatform Android=ic_schedule, iOS=schedule-24px.svg}"/>
      
  
            <local:SettingPage IconImageSource="{x:OnPlatform Android=ic_settings, iOS=settings-24px.svg}"/>
     
    
</custom:TabPage>

重要的是要注意两点:IconImageSource 将根据平台而改变,SelectedTabColor 将是 Android 和 iOS 在选择选项卡项时的色调颜色。

您需要像 Lean 写的那样将 svg 文件转换为可绘制的矢量文件并将它们放在 /drawable 文件夹中。

对于 iOS,您需要添加一个自定义选项卡渲染器,这是我使用的:

[assembly: ExportRenderer(typeof(TabPage), typeof(PageTabRenderer))]
namespace TabsApp.iOS
{
    [Foundation.Preserve(AllMembers = true)]
    public class PageTabRenderer : TabbedRenderer
    {
        readonly nfloat imageYOffset = 7.0f;

        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);

            if (TabBar.Items != null)
            {
                foreach (var item in TabBar.Items)
                {
                    item.Title = null;
                    item.ImageInsets = new UIEdgeInsets(imageYOffset, 0, -imageYOffset, 0);
                }
            }
        }
        
        protected override async Task<Tuple<UIImage, UIImage>> GetIcon(Page page)
        {
            var navigationPage = page as NavigationPage;
            if (navigationPage != null && navigationPage.CurrentPage != null)
            {
                var imageSource = navigationPage.IconImageSource == null ? navigationPage.CurrentPage.IconImageSource : navigationPage.IconImageSource;
                return await this.GetNativeUIImage(imageSource);
            }

            return await this.GetNativeUIImage(page.IconImageSource);
        }

        private async Task<Tuple<UIImage, UIImage>> GetNativeUIImage(ImageSource imageSource)
        {
            var imageicon = await GetNativeImageAsync(imageSource);
            return new Tuple<UIImage, UIImage>(imageicon, null);
        }

        private async Task<UIImage> GetNativeImageAsync(ImageSource imageSource)
        {
            if (imageSource is FileImageSource fileImage && fileImage.File.Contains(".svg"))
            {
                var imageicon = await ImageService.Instance.LoadFile(fileImage.File).WithCustomDataResolver(new SvgDataResolver(15, 15, true)).AsUIImageAsync();
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.Automatic);
            }
            
            else
            {
                var imageicon = await GetUIImage(imageSource);
                return imageicon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
            }
        }
        
        private Task<UIImage> GetUIImage(ImageSource imageSource)
        {
            var handler = GetImageSourceHandler(imageSource);
            return handler.LoadImageAsync(imageSource);
        }

        private static IImageSourceHandler GetImageSourceHandler(ImageSource source)
        {
            IImageSourceHandler sourceHandler = null;
            if (source is UriImageSource)
                sourceHandler = new ImageLoaderSourceHandler();
            else if (source is FileImageSource)
                sourceHandler = new FileImageSourceHandler();
            else if (source is StreamImageSource)
                sourceHandler = new StreamImagesourceHandler();
            else if (source is FontImageSource)
                sourceHandler = new FontImageSourceHandler();

            return sourceHandler;
        }
      
        
        
    }
    
}

把所有的*.svg 文件放到*.iOS 文件夹的/Resources 文件夹中就可以了。

【问题讨论】:

  • today.svg 被添加为嵌入式资源对吗? (很抱歉这个明显的问题,但只是检查)

标签: svg xamarin.forms tabs


【解决方案1】:

我得到了相同的结果,这是我在标签栏中的 svg 图像的解决方法。

对于安卓,请访问此页面 https://shapeshifter.design/

然后将你的SVG文件导入网站,然后下载xml文件,如下操作(点击Import,选择svg,选择svg文件,然后选择Export,矢量drawable,下载xml文件)。将xml文件复制到AndroidResource/ Drawable文件夹(请检查xml文件的build Action是AndroidResource)。注意:如果你想改变svg文件的线条颜色,请打开xml,喜欢填充颜色,改变它。

然后你可以像下面的代码一样在 pcl XML 中使用它。

<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            xmlns:local="clr-namespace:App3" xmlns:ffimageloadingsvg="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
            BarBackgroundColor="Green"
            x:Class="App3.MainPage">


    <local:TodayPage IconImageSource="{x:OnPlatform Android=ic_today,iOS=today.svg}"  >
      
    </local:TodayPage>
    <NavigationPage Title="Schedule" IconImageSource="myIcon.png">
        <x:Arguments>
            <local:Page1 />
        </x:Arguments>
    </NavigationPage>
    
</TabbedPage>

这是运行截图。

IOS请参考本帖Dinesh_Official的回复:

https://forums.xamarin.com/discussion/179773/how-to-use-svg-image-for-tabbed-page-tabbar-icon

【讨论】:

  • android 解决方案对我有用,我仍然不确定如何实现 iOS 变体以及在加载不同资源/渲染时如何区分 android 和 iOS
  • Lean Lu,我需要为我将拥有的每个选项卡创建选项卡渲染器吗?
  • @DemoDemo 在IOS中,你应该为你的标签页创建一个自定义渲染器,然后重写GetIcon方法,这里是演示代码。github.com/dinesh4official/XFPage/blob/master/XFPage/XFPage.iOS/…。你也可以下载。
  • 我更新了我的问题,似乎从未调用过 GetIcon。我在原始帖子中附加了我的新代码。感谢您的帮助!
【解决方案2】:

对于调试 SVGS,以下步骤可能会有所帮助。

  1. 如果您更换其他已确认工作的 SVG,它会起作用吗?如果不是,ffimageloading 是否已初始化?

  2. 确认您的嵌入式资源。 (将其放在 App.xaml.cs 中)

    public static void PrintEmbeddedResources()
    {
        #if DEBUG
        var assembly = typeof(App).GetTypeInfo().Assembly;
        var logTxt = "Embedded Resources:" + Environment.NewLine;
        foreach (var res in assembly.GetManifestResourceNames())
        {
            Debug.WriteLine("found resource: " + res);
        }
        #endif
    }
  1. 验证您的 svg 是否已优化。这里是 ffimageloading 推荐的工具:https://jakearchibald.github.io/svgomg/

  2. ffimageloading 有时会遇到特定 SVGS 的问题。 检查他们 github 上的问题,看看其他人是否报告了与您类似的问题,希望发布了解决方法。

【讨论】:

  • 1.我不确定它们是否经过优化我从这里下载了一个material.io/resources/icons/?style=baseline 2. 资源是嵌入式资源,我可以在调试时看到它以正确的名称进入循环。 3.我使用了CachedImageRenderer.Init(true); var 忽略 = typeof(SvgCachedImage);就像在 ffimageloading 的 wiki 中一样
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-13
  • 2021-07-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多