使用Xamarin Forms显示图像

10
已解决:答案是更新所有的NuGet包并针对较新版本的Android进行目标设置。现在图像按预期加载。我对此并不满意,因为我使用的正是Xamarin提供的代码,并且针对较新的版本已经废弃了一些代码依赖的项目。初始版本为Xamarin.Forms v23,我已更新到v25。

我有一个全新的Xamarin Forms项目,其中包含一个简单的视图,我试图在其中显示一张图片。我尝试了几种方法来显示图片,但完全没有成功。

我正在使用<image>,我也尝试过FFImageLoader控件。

<StackLayout Orientation="Vertical">

        <ff:CachedImage Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" WidthRequest="100" HeightRequest="100" />

        <Button x:Name="btn" Text="Image" Clicked="Button_Clicked" />

        <Frame OutlineColor="Red">
            <Image x:Name="StupidImage" Source="{Binding Thumbnail}"  Aspect="Fill" HeightRequest="100" WidthRequest="100"   />
        </Frame>

        </StackLayout>

这是当前的视图。我已经直接设置了一个值,但没有结果。

我可以获取图像流。我能够读取流中的所有字节。我建立了一个调试可视化器来将字节显示为图像。从源获取图像不是问题。问题在于如何让图像控件显示图像。

我尝试使用视图模型进行绑定。当失败时,我尝试直接设置源。

StupidImage.Source = ImageSource.FromStream(() => result.Stream);

我也复制了这些字节并尝试过。

StupidImage.Source = ImageSource.FromStream(() => new MemoryStream(imageBytes));

我尝试过ImageSource.FromFile().FromUri。我尝试将图像作为资源添加到项目中,每次尝试都是一样的,资源都被读取并且字节可用,但图像控件就是不显示它。

我想也许是大小问题,所以我设置了控件的大小。没有用。我认为也许是分辨率问题,所以我使用了一个较小的图像。我尝试了几个不同质量的图像。

然后我放弃了图像控件,我得到了FFImageLoading nuget软件包,并直接给了它一个图像的url。与FFImageLoading示例使用的相同示例。仍然没有图像。

我尝试了模拟器和两个不同的物理设备。相同的结果。

我还尝试使用btn.Image = "whatever.jpg"在按钮上设置图像,但结果相同。

这每次都是这样的结果。我迷失了。如何让图像显示?

编辑: 我确实让这个工作了,但只在模拟器上。

<Image x:Name="StupidImage" Source="https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg" />

同理也适用于

StupidImage.Source = ImageSource.FromUri(new Uri("https://static.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg"));

编辑2 - 澄清

我的目标是允许用户从设备中选择一张照片,然后显示它的预览。

在此输入图像描述


将您的图片放入内容视图中,然后尝试它是否显示。 - Ziyad Godil
除了 Android 平台,你有在其他平台上尝试过吗?这是官方文档链接:https://developer.xamarin.com/guides/xamarin-forms/user-interface/images/#Downloading_Images 试着禁用缓存... - EvZ
@EvZ 我正在尝试从设备中加载文件,而不是URL。我确实尝试了您提供链接中的URI示例,并且在模拟器上使其工作了(至少),但仍无法使用流或文件使其正常工作。 - Dustin Davis
你把那个图片文件放在哪里了?是放在PCL还是Android项目中? - ColeX
5个回答

8
如果您想在应用程序中使用图像,可以将它们加载到共享项目中,例如:

Embedded resource

请确保将构建操作更改为嵌入式资源
然后在您的代码中:
image.Source = ImageSource.FromResource("App5.Images.useravatar.png");

请注意资源名称。
还有XAML。
<ContentPage.Content>
    <StackLayout>
        <Image x:Name="image" WidthRequest="50"/>
    </StackLayout>
</ContentPage.Content>

目标是允许用户从他们的设备中选择一张图片,然后显示预览。 - Dustin Davis
你看过这个插件吗?https://github.com/jamesmontemagno/MediaPlugin - jack_tux
1
我之前没有看到过这个,但是看了一下,我可能知道我的问题出在哪里了。我读到了,在图像选择过程中拍照时,你必须保存一份副本,然后引用该副本,也许我需要这样做。 - Dustin Davis

2
您可以尝试实现CrossMedia插件。然后在按钮点击的代码部分,加入以下内容:
        Button_Clicked.Clicked += async (sender, args) =>
        {

            if ( !CrossMedia.Current.IsPickPhotoSupported )
            {
                DisplayAlert("Error message here", "More message", "OK");
                return;
            }

            var file = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions
            {
                PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
            });


            if (file == null)
                return;

            image.Source = ImageSource.FromStream(() =>
            {
                var stream = file.GetStream();
                file.Dispose();
                return stream;
            });
        };

点击按钮后,画廊/目录将被显示。您可以选择所需的照片。点击“确定”后,图像将显示在图像控件/标记中。我不确定这是否是您要寻找的解决方案。希望它能让您走上正确的方向。


我一直在尝试使它工作,但是一直没有成功。版本兼容性问题一直阻碍着我。 - Dustin Davis

2

以下是一些可以从列表中删除的内容:

[x] 从Visual Studio添加图像:

  1. 右键单击正确的文件夹
  2. 选择添加 >> 新文件... 注意:您必须使用Visual Studio添加它,而不能只是将其放入文件夹中。 Visual Studio需要知道它的存在

[x] 添加图像时是否在正确的位置:

  • 对于Android:它必须位于

ProjectName.Driod.Resources.drawable 文件夹中

  • 对于iOS:它必须位于

ProjectName.iOS.Resources 文件夹中

[x] 命名规则

  • 在Android和iOS上始终最好使用.png全部小写没有空格或特殊字符。

  • iOS通常会为同一个图像提供3个不同尺寸的图像,并采用以下命名约定

    • theman.png
    • theman@2x.png
    • theman@3x.png
  • 它们都是相同的图像,只是大小不同

[x] 在XAML中显示它:

   <StackLayout>
     <Image Source="thedog.png" HeightRequest="100" WidthRequest="100" />
   </StackLayout>
  • 在你的例子中,你使用了一个框架,如何使用stacklayout?框架的要求更多。
  • 对于MVVM,您可以使用以下内容更改Source,不要忘记twoway :)
    • Source =“{Binding Thumbnail,Mode = TwoWay}”

NB 这是非常基本的解释


0

这可能有帮助,我会添加一些代码。关于Xamarin表单、Android和使用内存流的惊人之处是,即使您没有使用资源(如果我记得正确),设备密度乘数仍然适用,因此我想如果您正在查看ADB接口,您将看到内存问题,这就是为什么您无法显示图像的原因...我以前通过采样解决了这个问题。

我解决它的方法是创建一个新的Image子类-ResourceImage。

public class ResourceImage :Image
{
    public enum SourceTypes{

        Database,

        File,

        Function,
    }

    private bool _LoadAct = false;

    public bool LoadAct { get{

            return _LoadAct;

        }



        set{ _LoadAct = value; OnPropertyChanged ("LoadAct");



        }

    }

    public Func<Stream> Func{ get; set; }

    public SourceTypes SourceType{ get; set;}

    public string ResName{ get; set;}

    public ResourceImage ()

    {

    }



    public ResourceImage (string name)

    {
        ResName = name;

    }



    public ResourceImage(Func<Stream> func){

        SourceType = SourceTypes.Function;

        Func = func;

    }

}

那么在Android渲染器中:我做了以下操作

    public class ResourceImageRenderer : ImageRenderer

{

    protected override void OnElementChanged (ElementChangedEventArgs<Image> e)

    {

        base.OnElementChanged (e);



        if (e.OldElement == null) 

        {

            var el = (ResourceImage)Element;

            if (el.SourceType == ResourceImage.SourceTypes.Database) {

                //Ignore for now

            } else if (el.SourceType == ResourceImage.SourceTypes.File) {

                using (global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ()) {

                    options.InJustDecodeBounds = false;
                    options.InSampleSize = 1;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);

                    var gd = Context.Resources.GetIdentifier (el.ResName.Split (new char[]{ '.' }) [0], "drawable", Context.PackageName);

                    using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {

                        var bitmap = global::Android.Graphics.BitmapFactory.DecodeResource (Context.Resources, gd, options);//DecodeStream (ms, rt, options);

                        bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;

                        Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));

                    }

                }

            } else if (el.SourceType == ResourceImage.SourceTypes.Function) {



                new Task (() => {


                    var ms = el.Func();

                    if(ms == null)return;

global::Android.Graphics.BitmapFactory.Options options = new global::Android.Graphics.BitmapFactory.Options ();



                        options.InJustDecodeBounds = false;



                        options.InSampleSize = 2;//calculateInSampleSize (options, outS.X / 4, outS.Y / 4);

                        ms.Position = 0;
                        Device.BeginInvokeOnMainThread(()=>{

                            using (global::Android.Graphics.Rect rt = new global::Android.Graphics.Rect (0, 0, 0, 0)) {



                                try{

                                    var bitmap = global::Android.Graphics.BitmapFactory.DecodeStream (ms, rt, options);

                                    bitmap.Density = global::Android.Graphics.Bitmap.DensityNone;

                                    Control.SetImageDrawable (new global::Android.Graphics.Drawables.BitmapDrawable (bitmap));

                                }catch(Exception  eee){


                                }

                        }

                        });




                }).Start(); 

            }

        }       

    }

回顾代码(已经多年没有碰过了),有很多地方需要改进。我不得不添加采样来解决同样的问题,用户选择要在消息应用程序中显示的图像,在iOS上运行得非常完美,但从未在Android上显示过。

FFImageLoading插件可以处理这个问题 https://github.com/luberda-molinet/FFImageLoading - Steve Chadbourne

0

这是我如何允许用户选择图像,然后在页面上显示它。

我调用我的图像服务的“选择图像”方法,传递一个回调方法。

 await _imageService.SelectImage(ImageSelected);

这是我的SelectImage方法。在开始时进行了一些权限检查。它使用Media插件来显示画廊并允许用户选择图像。
public async Task SelectImage(Action<MediaFile> imageAction)
{
    var allowed = await _permissionService.CheckOrRequestStoragePermission();
    if (!allowed) return;

    if (!_media.IsPickPhotoSupported)
    {
        throw new GalleryUnavailableException("Gallery unavailable");
    }

    var file = await _media.PickPhotoAsync(new PickMediaOptions
    {
        PhotoSize = PhotoSize.Small,
        CompressionQuality = 92
    });

    imageAction(file);
}

它返回一个MediaFile

这是图像选择回调方法

private void ImageSelected(MediaFile image)
{
    if (image == null)
    {
        return;
    }

    ChosenImage = new IncidentImage
    {
        ImageBytes = image.ToByteArray()
    };
}

ChosenImage 是我的视图模型中的一个属性

public IncidentImage ChosenImage {get; set;}

我使用 PropertyChanged.Fody 来触发属性更改通知,但您也可以使用 INotifyPropertyChanged。
而 IncidentImage 是我用来存储和显示图像的类。
public class IncidentImage
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }

    public int IncidentDetailsId { get; set; }

    public byte[] ImageBytes { get; set; }

    [Ignore]
    public ImageSource ImageSource
    {
        get
        {
            ImageSource retval = null;
            try
            {
                if (ImageBytes != null)
                {
                    retval = ImageSource.FromStream(() => new MemoryStream(ImageBytes));
                }
            }

            catch (Exception e)
            {
                Debug.WriteLine(e);
            }
            return retval;
        }
    }

}

这里是XAML代码

<Image Source="{Binding ChosenImage.ImageSource}"
       Aspect="AspectFit"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接