繁体   English   中英

Xamarin.Forms:如何将图像从资源加载到字节数组中?

[英]Xamarin.Forms: How to load an image from Resources into a byte-array?

我有一个(希望如此)简单的问题(我没有找到适合我所有搜索的答案)。

我使用 Xamarin.Forms 1.4.1-Pre-1。 在我的应用程序中,我有:

byte[] AvatarErfassung; // Bytearray, to later update the webservice
var Avatar = new Image { HeightRequest = 71, WidthRequest = 61, HorizontalOptions = LayoutOptions.Start };
Avatar.Source = "SymbolMann.jpg";  

其中图像“SymbolMann.jpg”作为项目资源(在每个项目中)包含并显示在页面上(没有问题)。
我现在想将图像放入字节数组中以将其发送到我们的网络服务。 我没有找到任何方法来访问图像“SymbolMann.jpg”(将其加载到字节数组中)或因此使用(但是)Avatar.Source。

题:
如何将图像“SymbolMann.jpg”放入字节数组“AvatarErfassung”中?

谢谢你的每一个回答

嗨迪米特里斯

感谢您的(快速)重播。
正如我所写,我与 Xamarin 一起工作。 表格
图像存储为资源:在\\Resources\\Drawable\\(适用于Android)、\\Resources\\(适用于iOS)和根目录中(适用于WP)。 我必须在内容页面上加载图像。

如果我超过您的代码,请转到以下行:

var assembly = this.GetType().GetTypeInfo().Assembly;

我有错误信息(翻译成英文):
“System.Type 不包含 GetTypeInfo 方法的定义(是否缺少 using?)”

我必须添加特定的用途吗?

你写: // 你可以用“typeof(MyType)”替换“this.GetType()”,其中 MyType 是你的程序集中的任何类型。

我必须放置什么类型?

= typeof(????).GetTypeInfo().汇编:

感谢您的进一步答复。

更新#2:

首先,感谢您的耐心等待……

不幸的是,在VS2013 中,我没有找到任何“解决”功能显示我丢失的使用 - 但我已经使用我自己找到了丢失的东西:-)
与此同时,我添加了使用 System.Reflection;

现在,这一行: var assembly = this.GetType().GetTypeInfo().Assembly;

已解决并编译无错误

此外,我已将 .jpg 的类型从“Android Resource”更改为“Embedded Ressource”。
然后应用程序在行中崩溃: long length = s.Length; 因为似乎 s 为空(我认为找不到 jpg)

此外 - 如果我将类型更改为“嵌入式资源” - Xamarin.Forms(Avatar.Source =“SymbolMann.jpg”;)不再找到该图像

如果我将 .jpg 的类型更改为“编译”,我将无法编译该应用程序。
错误消息:命名空间不能直接包含字段或方法等成员。

那么……资源的正确类型是什么(以便它可以使用 assembly.GetManifestResourceStream() 加载?

我是否必须在文件名 (SymbolMann.jpg) 中添加一些内容才能找到它?

我如何更改 Avatar.Source = "SymbolMann.jpg" 以便找到它(如果我将类型从“Android Resource”更改为其他任何内容)?

再次我的需求:
在注册页面上,默认图像(符号)在标准 Xamarin.Forms 图像(avatar.source = “SymbolMann.jpg”/“SymbolFrau.jpg”)中显示为男性和女性的头像。
.jpg 存储在每个项目(Android、iOS 和 WP)的标准目录中,可以从图像对象访问这些文件而不会出现问题。
然后,用户可以使用默认图像之一或通过 mediapicker 加载另一个。
如果用户点击“注册”按钮,我必须从图像创建一个字节数组,以通过 json 将其发送到我们的网络服务。
如果用户通过 mediapicker 选择另一个图像,我可以访问流,问题是,从默认图像(在每个平台中)变成一个字节数组。

再次感谢您的帮助...

通过从程序集中读取资源流,您可以轻松地做到这一点:

var assembly = this.GetType().GetTypeInfo().Assembly; // you can replace "this.GetType()" with "typeof(MyType)", where MyType is any type in your assembly.
byte[] buffer;
using (Stream s = assembly.GetManifestResourceStream("SymbolMann.jpg”))
{
    long length = s.Length;
    buffer = new byte[length];
    s.Read(buffer, 0, (int)length);
}
// Do something with buffer

编辑:

要获取包含项目嵌入资源的程序集,您需要对该程序集中包含的类型调用GetTypeInfo方法。 因此, typeof(X) ,其中“X”是程序集中的任何类型。 例如,如果您使用名为MyCustomPage的自定义页面:

public class MyCustomPage : ContentPage {}

...这就是您要传递的内容: typeof(MyCustomPage)

您需要Type任何实例(这是typeof关键字返回的基本内容)。 另一种方法是对项目中包含的任何对象调用GetType()方法。

请注意,如果您对未包含在您的项目中的类型调用typeof(X) ,您将无法获得正确的程序集。 例如。 调用typeof(ContentPage).GetTypeInfo().Assembly ,将返回ContentPage类型所在的程序集。这不是包含您的资源的程序集。 我希望这不会令人困惑。 如果是,请告诉我。

现在, GetTypeInfo方法是包含在System.Reflection命名空间中的扩展方法。 当 Xamarin Studio 中的文本编辑器无法识别某些内容时,它会以红色突出显示。 右键单击它会在上下文菜单中为您提供一个解决选项:

在 Xamarin Studio 中右键单击

如果可以解析类型或方法,它将显示该选项。

有关 Xamarin Forms 中图像的更多信息,请访问此处

谢谢@FredWenger 和@Dimitris - 这是我基于你的回答的解决方案:

  1. 随时随地将图像添加到 Xamarin Forms(便携式)项目。 我只是去了根: MyProject\\DaffyDuck.jpg
  2. 将图像标记为Embedded Resource (文件属性 -> 构建操作)
  3. 像这样调用引用嵌入式资源的函数: ImageDataFromResource("MyProject.DaffyDuck.jpg")
    请注意,您需要包含项目名称才能获得程序集名称 *.
  4. 这是函数:

     public byte[] ImageDataFromResource(string r) { // Ensure "this" is an object that is part of your implementation within your Xamarin forms project var assembly = this.GetType().GetTypeInfo().Assembly; byte[] buffer = null; using (System.IO.Stream s = assembly.GetManifestResourceStream(r)) { if (s != null) { long length = s.Length; buffer = new byte[length]; s.Read(buffer, 0, (int)length); } } return buffer; }
  5. 如果您需要在 ImageView 控件中引用加载的数据,请执行以下操作:
    ImageView.Source = ImageSource.FromStream(() => new MemoryStream(imageData));

*注意:如果第 3 步不起作用,并且您想检查嵌入的资源名称,请调用: var names = assembly.GetManifestResourceNames(); 并查看您的项目中列出的内容。

首先,非常感谢 Dimitris,他引导我走向正确的方向!

结论:

基地:
我使用 VS2013 - 更新 2、Xamarin.Forms (1.4.1-Pre1) 并且我的应用程序基于模板“Blank App (Xamarin.Forms Shared)” (不是 PCL)并且我使用所有平台(Android、iOS、可湿性粉剂)。

在用户注册页面上,用户可以选择一个图像作为头像(然后显示在他的帖子中)。 根据用户的性别,显示了两个默认图像(一个是男性,一个是女性)。 然后用户可以取代默认图像之一或从媒体选择器中选择另一个图像。
默认图像存储在内容图像的默认位置(对于 Android,例如在/Resources/Drawable/ 下)。
图像显示在 XF-Standard-Image-Object 中,Image-Source = “xxx.jpg”。
这没有问题。

问题:
由于用户数据(包括头像)存储在 SQL 服务器上(通过 Json 网络服务),我必须将图像数据转换为字节数组(然后将其与其他数据作为字符串发送到网络服务)。
问题是,从默认目录加载图像并将其转换为字节数组(文件访问是问题-> 无法访问)。

解决方案:
感谢 Dimitris,他引导我朝着正确的方向前进,我已经实施了以下解决方案:

在所有项目(Android、iOS 和 WP)中,我添加了一个新目录“\\Embedded\\”(直接在根目录下) 然后,我将我的两个 jpg(“SymbolMann.jpg”和“SymbolFrau.jpg”)复制到此目录中(适用于所有平台)。
然后,我将资源类型更改为图像,例如从“AndroidResource”更改为“Embedded Resource” (对于所有平台都相同类型)。

在应用程序(app.cs)的启动代码中,我添加了:

//
string cNameSpace = "";
switch (Device.OS)
{
  case TargetPlatform.WinPhone:
    cNameSpace = "MatrixGuide.WinPhone";
    break;
  case TargetPlatform.iOS:
    cNameSpace = "MatrixGuide.iOS";
    break;
  case TargetPlatform.Android:
    cNameSpace = "MatrixGuide.Droid";
    break;
}
GV.cEmbeddedAblage = cNameSpace + ".Embedded.";

其中 GV.cEmbeddedAblage 只是一个全局变量。
要检查命名空间名称,我必须右键单击每个平台的项目并查看已确定的命名空间名称(在您的项目中可能会有所不同)。
.Embedded。 是新创建的目录(所有平台上的名称相同)。

此外,我为男性形象 (GV.ByteSymbolMann) 和女性形象 (GV.ByteSymbolFrau) 创建了全局字节数组。

在注册页面的启动代码中,我添加了:

string cFilename = "";
if (GV.ByteSymbolMann == null) // not yet loaded
{
  cFilename = GV.cEmbeddedAblage + "SymbolMann.jpg";
  var assembly = this.GetType().GetTypeInfo().Assembly; 
  byte[] buffer;
  using (Stream s = assembly.GetManifestResourceStream(cFilename))
   {
     long length = s.Length;
     buffer = new byte[length];
     s.Read(buffer, 0, (int)length);
     GV.ByteSymbolMann = buffer; // Overtake byte-array in global variable for male
   }
}
//
if (GV.ByteSymbolFrau == null) // not yet loaded
{
  cFilename = GV.cEmbeddedAblage + "SymbolFrau.jpg";
  var assembly = this.GetType().GetTypeInfo().Assembly; 
  byte[] buffer;
  using (Stream s = assembly.GetManifestResourceStream(cFilename))
   {
     long length = s.Length;
     buffer = new byte[length];
     s.Read(buffer, 0, (int)length);
     GV.ByteSymbolFrau = buffer; // Overtake byte-array in global variable for female
    }
}

将 .jpg 加载到图像对象中的代码,我不得不从:

//Avatar.Source = "SymbolMann.jpg";  // Load from default directory

Avatar.Source = ImageSource.FromStream(() => new MemoryStream(GV.ByteSymbolMann)); // Load (stream) from byte-array

然后我在另一个字节数组中接管选定的字节数组(与显示图像并行)(稍后作为字符串上传到网络服务)

AvatarErfassung = GV.ByteSymbolMann;

如果用户更改选择(例如更改为女性):

Avatar.Source = ImageSource.FromStream(() => new MemoryStream(GV.ByteSymbolFrau)); 
AvatarErfassung = GV.ByteSymbolFrau;

注意:如果用户从 mediapicker 选择另一个图像,我可以直接访问流,所以这从来都不是问题。

此解决方案适用于所有平台(Android、iOS、WP)。

我希望这可以帮助其他一些用户...... 

还有……最后感谢 Dimitris

添加到noelicus 的答案

设置

  1. 将图像添加到 Xamarin.Forms 项目的根文件夹,例如MyProject\\pizza.png

  2. 在 Visual Studio 中,右键单击图像文件并选择Build Action -> EmbeddedResource

在此处输入图片说明

代码

public byte[] GetImageFromFile(string fileName)
{
    var applicationTypeInfo = Application.Current.GetType().GetTypeInfo();

    byte[] buffer;
    using (var stream = applicationTypeInfo.Assembly.GetManifestResourceStream($"{applicationTypeInfo.Namespace}.fileName"))
    {
        if (stream != null)
        {
            long length = stream.Length;
            buffer = new byte[length];
            stream.Read(buffer, 0, (int)length);
        }
    }

    return buffer;
}

例子

public class MyPage() : ContentPage
{
    public MyPage()
    {
        var imageAsByteArray = GetImageFromFile("pizza.png");

        var pizzaImage = new Image();
        pizzaImage.Source = ImageSource.FromStream(() => new MemoryStream(imageAsByteArray));

        Content = pizzaImage;
    }

    byte[] GetImageFromFile(string fileName)
    {
         var applicationTypeInfo = Application.Current.GetType().GetTypeInfo();

        byte[] buffer;
        using (var stream = applicationTypeInfo.Assembly.GetManifestResourceStream($"{applicationTypeInfo.Namespace}.fileName"))
        {
            if (stream != null)
            {
                long length = stream.Length;
                buffer = new byte[length];
                stream.Read(buffer, 0, (int)length);
            }
        }

        return buffer;
    }
}

我在让它工作时遇到了很多问题。 我的项目已经在其他地方使用 FFImageLoading,我发现这是将 .png 作为流加载的最简单方法:

using (var stream = await ImageService.Instance.LoadCompiledResource("resource_image.png").AsPNGStreamAsync())
{
    localBitmap = SKBitmap.Decode(stream);
}

确保您的资源文件在 iOS 和 Droid 项目中具有相同的名称。 他们可能已经将构建操作设置为“BundleResource”(iOS)或“AndroidResource”(Droid)。

当然,您必须安装 FFImageLoading Nuget,但您会很高兴您这样做了。

我使用了 noelicus 给定的想法并打印了 ManifestResourceNames 数组中的所有名称,我在那里发现我的图像名称为ProjectName.Droid.image_name.jpeg 。然后我只是将方法调用从assembly.GetManifestResourceStream("image_name.jpg")更改为assembly.GetManifestResourceStream("ProjectName.Droid.image_name.jpeg") 。它起作用了。 所以正确答案是:

  1. 将图像存储在 PCL 的根文件夹中。
  2. 使其在属性中的构建操作作为嵌入式资源。
  3. 然后打印<b><i>var names = assembly.GetManifestResourceNames()</i></b>数组,这将是一个字符串数组,可以使用打印

    foreach(var data in names){Debug.WriteLine(data);}
  4. 在那里您将看到图像的资源名称,然后使用该方法

    Stream s = assembly.GetManifestResourceStream("resource_name_you_found.format")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM