帮酷LOGO
0 0 评论
文章标签:asset  Assets  form  图像  Xamarin  SHA  for  xamarin-forms  

本系列文章中的文章

钙和示例应用的源代码

本文的源代码位于位于 https://calcium.codeplex.com/SourceControl/latest的钙源代码库中。

请参阅解决方案 TrunkSourceCalciumXamarinInstallation CalciumTemplates.Xamarin.sln

介绍

在使用Xamarin构建跨平台应用时,图像资源管理是一个挑战。 每个平台都需要以不同方式处理图像。 Android和iOS项目都使用自己的系统;将映像放在不同的目录中。 然而,Windows Phone 可以让你在任何地方放置图片。 如果你希望能够在整个项目中使用相同的图像,那么你需要将它们链接到不同的位置。 Windows Phone 是最大的失败者,因为图像必须放在项目的root 目录中,这远远不是理想的。 另一种方法是将图像资源嵌入到程序集中,当存在程序集大小增加程序集时不是很好。

Visual Studio Android和iOS项目要求所有图像都放置在单个目录中。 我不希望将图像放入单个目录中。 我更喜欢用功能相关来分组事物。 看到大型的web项目已经被孤立的图像所填充后,它们之后的页面已经很长时间了。

我设计了一种更好的方法来在项目之间共享图像资源;一个可以保持 Windows Phone 系统的灵活性。 我的方法使用一个T4模板,将共享项目中的文件复制到各种iOS和Android资源目录,将它们添加到你的iOS和Android项目中,并正确设置映像的生成操作。 在本文后面,你将看到自定义标记扩展如何转换图像资源 url,以便正确解决图像。 这种方法提供了 Windows Phone 资源系统的所有灵活性,同时仍然保持与iOS和Android的兼容性。

图像元素通常用于Xamarin窗体中,在页面上显示PNG或者JPG图像。 请参见下面的示例:

<ImageSource="CalciumLogo.png"/>

要在每个平台上正确显示图像,必须位于iOS项目的资源目录。Android项目的资源/或者 Windows 电话的root 目录。

在Xamarin网站上对图片元素进行了全面的介绍,在 http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/images/网站上。
尽管有很好的理解Xamarin形式的图像资源,但在使用本文描述的方法时,大部分摩擦都会消失。

有一个名为 CalciumLogo.png的PNG映像位于共享项目的/Views/MainView/Images 目录中。 Windows Phone 不同,在使用Xamarin窗体时不解析以下路径:

<ImageSource="/Views/MainView/Images/CalciumLogo.png"/>

让它工作吧。

图像元素的Source 属性是一个 ImageSource。 类型转换器用于将XAML中提供的字符串值转换为实际的ImageSource 实例。 我们可以通过创建自定义标记扩展来接管从指定URL创建 ImageSource 对象的责任。

Xamarin表单的一个出色特性是包含了标记扩展。 我惊喜地看到他们出现在Xamarin表单的第一个版本中。 拥有创建自定义标记扩展的能力为我们提供了扩展在XAML中可以做的事情的强大方法。 我们并没有等待'官方'支持一切;我们可以去 MacGyver,让它自己。

位于钙源代码库中 Outcoder.Calcium.Android 项目中的ImageUrlTransformer 类将字符串值转换为特定于平台的URL。 它有一个名为 TransformForCurrentPlatform的方法。 如清单 1所示,该方法接受一个 URL,如果应用程序在iOS或者Android上运行,它将用下划线替换路径分隔符斜杠。 例如如果我们在iOS或者Android上传递 "/Views/MainView/Images/CalciumLogo.png" 这个方法,该方法应该返回" views_mainview_images_calciumlogo.png"

相反,如果应用程序在 Windows Phone 上运行,任务就会变得更加直接;标记扩展使用提供的未更改路径解析图像。

当然,为了在iOS和Android上正常工作,必须有一个名为 Views_MainView_Images_CalciumLogo.png的映像,位于每个平台的各自资源目录中。 我们将在本节后面的GREATER 细节中进行。

如果你的平台是iOS或者 Android,指定的URL以 file:///,请注意,它表示一个Uri对象,而不是字符串,用于指定图像位置。 可以,但必须从图像的URL中删除,才能正确解析。

清单 1。ImageUrlTransformer.TransformForCurrentPlatform 方法

publicstring TransformForCurrentPlatform(string url)
{
 string result = ArgumentValidator.AssertNotNull(url, "url");
 if (Device.OS == TargetPlatform.Android || Device.OS == TargetPlatform.iOS)
 {
 conststring filePrefix = "file:///";
 if (url.StartsWith(filePrefix))
 {
 result = url.Substring(filePrefix.Length);
 }
 result = result.Replace("/", "_").Replace("", "_");
 if (result.StartsWith("_") && result.Length >1)
 {
 result = result.Substring(1);
 }
 }
 elseif (Device.OS == TargetPlatform.WinPhone)
 {
 if (url.StartsWith("/") && url.Length >1)
 {
 result = result.Substring(1);
 }
 }
 return result;
}

自定义标记扩展为你提供了在运行时使用自定义逻辑解析值的扩展点。 iPhone 7 还没出来,我们已经在iPhone上获取细节 8,或者不管是想到下一步。 Xamarin.Forms.Xaml.IMarkupExtension 接口包含名为 ProvideValue的单个方法,它用于处理值并将对象返回到XAML元素中的属性集。

ImageResourceExtension 类中的类是一个自定义标记扩展,它利用 ImageUrlTransformer 类。 见清单 2.

清单 2。ImageResourceExtension类( 摘录)

[ContentProperty("Source")]publicclass ImageResourceExtension : IMarkupExtension
{
 publicstring Source { get; set; }
 publicobject ProvideValue(IServiceProvider serviceProvider)
 {
. . .
 }
}

从方法的ImageResourceExtensionProvideValue 返回一个 ImageSource 对象。 参见清单 3。该方法使用 IImageUrlTransformer的实现将 Source 属性的值转换为特定于平台的URL。

注意 NOTE。如果要更改url的处理方式和修改类的行为,可以通过注册你自己的IImageUrlTransformer的实现来完成。

类使用Xamarin窗体 Device.OS 属性确定应用程序运行的平台。 如果应用程序在iOS或者Android上运行,那么URL将被平整,图像将驻留在资源目录中。 static ImageSource.FromFile 方法用于创建一个 ImageSource 对象。

如果应用程序在 Windows Phone 上运行,则创建一个流来读取图像文件,该流在lambda表达式中传递到 static ImageSource.FromStream 方法。

清单 3。ImageResourceExtension.ProvideValue 方法

publicobject ProvideValue(IServiceProvider serviceProvider)
{
 if (Source == null)
 {
 returnnull;
 }
 ImageSource imageSource = null;
 var transformer = Dependency.Resolve<IImageUrlTransformer, ImageUrlTransformer>(true);
 string url = transformer.TransformForCurrentPlatform(Source);
 if (Device.OS == TargetPlatform.Android)
 {
 imageSource = ImageSource.FromFile(url);
 }
 elseif (Device.OS == TargetPlatform.iOS)
 {
 imageSource = ImageSource.FromFile(url);
 }
 elseif (Device.OS == TargetPlatform.WinPhone)
 {#if WINDOWS_PHONEif (url.StartsWith("/") && url.Length >1)
 {
 url = url.Substring(1);
 }
 var stream = System.Windows.Application.GetResourceStream(new Uri(url, UriKind.Relative));
 if (stream!= null)
 {
 imageSource = ImageSource.FromStream(() => stream.Stream);
 }
 else {
 ILog log;
 if (Dependency.TryResolve<ILog>(out log))
 {
 log.Debug("Unable to located create ImageSource using URL:" + url);
 }
 }#endif }
 if (imageSource == null)
 {
 imageSource = ImageSource.FromFile(url);
 }
 return imageSource;
}

有了 ImageResourceExtension 类之后,现在就可以指定像这样的映像文件的路径了:

<ImageSource="{calcium:ImageResource/Views/MainView/Images/CalciumLogo.png}"/>

我们可以在这里停止,但这将使我们不得不重复。重命名和重新排列每个平台的图像。 我们甚至可以尝试链接和重命名文件。 但是我们很快就遇到了同样的可维护性问题,我们开始尝试解决这些问题。 让我们做一些更好的事情。

使用T4共享图像资源

在本节中,你将看到如何创建可以复制的T4模板,该模板将共享项目中的所有图像复制到你的iOS或者Android项目的资源目录。 然后自动将文件导入项目,根据平台为文件设置正确的生成操作。

这不是我们在本系列第 3部分中看到的本地化解决方案之后提出的扩展: 构建可以本地化的跨平台平台应用Xamarin形式和钙。 T4对于这样的任务很方便。 你可能使用定制的MS生成任务实现相同的结果。 因为我觉得 LESS 很难配置和工作,所以我选择了T4方法。 代码易于修改且不被隐藏在外部程序集中。 然而,我猜,在长期以来,Xamarin可能会选择 MS,。 但现在T4完成了任务。

使用T4将图像导入到Android项目中

让我们先看一下将T4模板集成到Android项目中。 可以下载示例中的ProjectTemplate.Xamarin.CSharp.Android 项目包含与这里示例相关的两个目录: 基于xml的/Resources/Drawable 插件目录,它是默认解析的必需位置;以及 /ResourcesModel/T4Templates 目录,其中包含一些必需的T4文件。

以下步骤概述了设置T4映像共享模板的过程:

  • 创建一个名为ResourcesModel的目录,并在你的Android项目中创建一个T4Templates目录。
  • 将 Images.ttinclude 文件和 MultiFileOutput.ttinclude 文件添加到/ResourcesModel/T4Templates 目录中,这些文件位于可以下载的示例中。
  • 将 Images.ttinclude 文件的生成操作和 MultiFileOutput.ttinclude 文件设置为无。
  • 在/Resources/Drawable 项目的目录中,使用studio对话框Visual添加一个文本文件。 单击确定之前,请重命名文件 Images.tt.
  • 将以下文本粘贴到 Images.tt file: 中
    <#@ 包括 file="。resourcesmodelt4templatesimages。ttinclude"#>
    <#@ 输出 extension=". txt"#>
    <#@ 模板 language="C#"hostspecific="true"#> <#

    处理("calciumsampleapp","androidresource") ;

    #>
  • 将文本"calciumsampleapp"更改为共享项目的NAME。

注意 Process 方法的第二个参数定义了图像的生成动作。 对于 Android,必须将它的指定为 AndroidResource。

如果路径已经正确指定,当保存 Images.tt 文件时,图像应当显示为 Images.tt 文件的子项。 参见图 1.共享项目中的图像被成功复制。重命名并包含到Android项目中。

向资源目录中导入图 1.

使用T4将图像导入到iOS项目中

为导入共享映像而设置一个iOS项目的过程与Android项目的过程几乎相同。 但是,有两种差异: iOS的映像目录为 /resources,并且必须将生成操作参数指定为 BundleResource。 Images.tt的内容应类似于以下内容:

<#@includefile="..ResourcesModelT4TemplatesImages.ttinclude"#><#@outputextension=".txt"#><#@templatelanguage="C#"hostSpecific="true"#><#Process("CalciumSampleApp","BundleResource");#>

在场景后面使用T4导入图像

生成图像的逻辑包含在 Images.ttinclude 文件的Process 方法中。 方法使用 Visual Studio DTE对象来遍历项目和文件的解决方案。

在流程方法中,可以像这样检索 DTE:

IServiceProvider hostServiceProvider = (IServiceProvider)Host;
EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));

方法使用名为 GetProjects的方法检索解决方案的所有项目。 由于项目可能位于解决方案文件夹中,因此检索项目的List 是一个递归的Activity。

随着项目的List,Process 方法尝试通过在解决方案中迭代项目来找到图像所在的项目。

var solution = dte.Solution;var items = GetProjects();foreach (EnvDTE.Project item in items)
{ 
 if (item.Name.EndsWith(projectName))
 {
 project = item;
 break;
 }
}

GetFiles 函数使用尾部递归来检索具有指定文件扩展名的所有文件。 见清单 4.

清单 4 GetFiles function功能

void GetFiles(ProjectItem projectItem, string fileExtension, IList<string> fileList)
{
 string fullPath = projectItem.Properties.Item("FullPath").Value.ToString();
 if (fullPath.ToLower().EndsWith(fileExtension))
 {
 fileList.Add(fullPath);
 }
 var childItems = projectItem.ProjectItems;
 if (childItems!= null)
 {
 foreach (ProjectItem childItem in childItems)
 {
 GetFiles(childItem, fileExtension, fileList); 
 }
 }
}

Process 函数使用 GetFiles 函数填充包含所有PNG和JPG文件路径的List,如下所示:

var fileExtensions = new string[] { ".png", ".jpg" };var fileList = new List<string>();var projectItems = project.ProjectItems;if (projectItems!= null)
{
 foreach (ProjectItem projectItem in project.ProjectItems)
 {
 foreach (string extension in fileExtensions)
 {
 GetFiles(projectItem, extension, fileList);
 }
 }
}

过程函数然后通过将路径分隔符替换为下划线来创建每个图像路径中的展平 NAME。 参见清单 5.如果图像已经作为资源存在,则对两个文件的LastWriteTime 值进行比较。 如果原始图像具有较旧的LastWriteTime 值,则不需要执行任何操作。 这里步骤避免从源代码管理中不必要地签出文件。

清单 5 过程函数中的copy。Images.ttinclude 复制文件

string templateDirectory = Path.GetDirectoryName(Host.TemplateFile); foreach (string item in fileList)
{
 if (!item.StartsWith(projectDirectory))
 {
 continue;
 }
 string fileNameInProject = Path.GetFileName(item);
 string fileDirectory = Path.GetDirectoryName(item);
 string pathSubstring = fileDirectory.Substring(projectDirectoryLength);
 if (pathSubstring.StartsWith(""))
 {
 pathSubstring = pathSubstring.Substring(1);
 }
 string flattenedPathSubstring = pathSubstring.Replace("","_");
 if (flattenedPathSubstring.Length >0)
 {
 flattenedPathSubstring = flattenedPathSubstring + "_";
 }
 string flattenedFileName = flattenedPathSubstring + fileNameInProject;
 string newOutputPath = Path.Combine(templateDirectory, flattenedFileName);
 if (File.Exists(newOutputPath))
 {
 var projectFileInfo = new System.IO.FileInfo(item);
 var newFileInfo = new System.IO.FileInfo(newOutputPath);
 if (projectFileInfo.LastWriteTimeUtc < newFileInfo.LastWriteTimeUtc)
 {
 continue;
 }
 }
 File.Copy(item, newOutputPath, true);
. . .
}

最后,使用 MultiFileOutput.ttinclude 文件中的AddProjectItem 函数将该项添加到项目中,如下面的摘录所示:

AddProjectItem(flattenedFileName, buildAction);

感谢go在 Oleg Sych上提供关于从单个T4模板生成多个输出的有用的文章。

如果你阅读上一篇文章,第 4部分: 使用Xamarin窗体和钙创建一个跨平台应用程序条,你可能会想起,icon 图像是使用IImageUrlTransformer来解析的。 在清单 6中,你再次看到TransformForCurrentPlatform如何返回与 Images.tt 模板平滑输出兼容的图像 URL。

清单 6。使用IImageUrlTransformer解析 icon 映像。

var uri = appBarItem.IconUri;if (uri!= null)
{
 string url = uri.ToString();
 string transformedUrl = imageUrlTransformer.TransformForCurrentPlatform(url);
 item.Icon = transformedUrl;
}

注意:在生成项目时,Visual Studio 可能会抱怨文件x 已经定义。 确定(. tt ) 模板的生成操作设置为无。 Visual Studio 有时喜欢对这些文件进行 switch 构建操作。

结束语

如何为各种平台解决图像的差异将在Xamarin窗体中放置图像的一些限制。 使用本文提供的技术可以避免这些限制;提供 Windows Phone 资源系统的灵活性,同时仍保持iOS和 Android。

在本文中,你了解了如何以统一的方式共享和使用项目之间的映像文件。 你已经看到了如何将图像文件从共享项目复制到iOS和Android资源目录;。 最后,你了解了如何使用自定义标记扩展来翻译图像资源 url,以便在XAML中引用图像,而不管平台如何使用。

我希望你能找到这个项目。 如果是这样,那么我会感谢你的价格,并且/或者离开反馈 below。 这将帮助我更好地阅读下一篇文章。

在下一篇文章中,你将探索钙选项系统用户,它允许你在一行代码中定义一个选项。 然后在应用程序的选项视图中自动显示选项,当用户修改选项时,选项系统会自动保存更改。

历史记录

2014

  • 初始出版物。


文章标签:图像  for  form  SHA  表单  分享  asset  Xamarin  

Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语