Android上的Xamarin.Forms ListView OutOfMemoryError异常 [英] Xamarin.Forms ListView OutOfMemoryError exception on Android

查看:170
本文介绍了Android上的Xamarin.Forms ListView OutOfMemoryError异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有人用包含Image视图的ItemTemplate尝试了A Xamarin.Forms Listview?现在,当ListView包含大约20行或更多行时会发生什么?

Anyone ever tried A Xamarin.Forms Listview with an ItemTemplate containing a Image view? Now, what happens when ListView contains ca 20 or more rows?

至于我,我有一个大约4K大小的.png文件加载到图像视图中。在使用OutOfMemoryError崩溃应用程序之前,最多显示9到12行。在Android Manifest中请求一个大堆之后,应用程序在60-70行之后崩溃。

As for me, I have a .png file of around 4K in size loaded into the Image view. Got a maximum of 9 - 12 rows shown before application crashed with a OutOfMemoryError. After requesting a large heap in the android Manifest, the app crashes after 60 - 70 rows.

我知道Xamarin正在推广使用BitmapFactory类来缩小位图,但这不适用于Xamarin Forms Image View(开箱即用)。

I know that Xamarin is promoting the use of the BitmapFactory class to scale down bitmaps, but this is not applicable (out of the box) for the Xamarin Forms Image View.

我正试图摆弄ImageRenderer的Sub Class,看看我是否可以添加一个BitmapFactory.Options属性,如果这样可以解决问题。

I'm about trying to fiddle with a Sub Class of the ImageRenderer to see if I can add a BitmapFactory.Options property and if this will solve the problem.

另外,我可能需要查看在ViewCell滚动屏幕后Xamarin.Forms是否处理(回收)包含的位图。

Also, I might need to check out if Xamarin.Forms does dispose (recycle) the contained bitmap after the ViewCell is being scrolled of the screen.

在开始这个旅程之前,我非常希望得到任何可以使这个更简单或更简单的解决方案的评论,认为这个过程是不必要的。

Before setting out on this journey, I would be very keen to get any comments that could make this easier or a simpler solution that would deem this process unnecessary.

期待......

推荐答案

是的,我找到了解决方案。代码要遵循。但在此之前,让我解释一下我所做的一切。

Yes, I found a solution. Code to follow. But before that, let me explain a bit what I have done.

因此,我们需要自己动手处理图像及其底层资源(位图或可绘制,但你想调用它)。基本上,它归结为处置原生的'ImageRenderer'对象。

So, there's definitely a need to take maters in our own hands to dispose the image and its underlying resources (bitmap or drawable, however you want to call it). Basically, it comes down to dispose the native 'ImageRenderer' object.

现在,无法从任何地方获取对该ImageRenderer的引用,因为为了这样做,需要能够调用Platform.GetRenderer(... )。无法访问平台类,因为它的范围被声明为内部。

Now, there's no way to obtain a reference to that ImageRenderer from anywhere because in order to do so, one need to be able to call Platform.GetRenderer(...). Access to the 'Platform' class is inaccessible since its scope is declared as 'internal'.

所以,除了分类以外,我别无选择Image类及其(Android)Renderer并从内部销毁此Renderer本身(将'true'作为参数传递。不要尝试使用'false')。在渲染器内部,我挂钩到页面消失(如果是TabbedPage)。在大多数情况下,Page Disappear事件不能很好地发挥作用,例如当页面仍在屏幕堆栈中但由于另一页面正在顶部上绘制而消失。如果丢弃图像,则当页面再次被揭开(显示)时,它将不会显示图像。在这种情况下,我们必须挂钩主导航页面的'Popped'事件。

So, I have been left with no choice other than to sub-class the Image class and its (Android) Renderer and destroy this Renderer itself from inside (passing 'true' as argument. Don't try with 'false'). Inside the Renderer I hook on to page disappear (In case of a TabbedPage). In most situations the Page Disappear event will not serve the purpose well, such as when the page is still in the screen stack but disappears due to a another page is being drawn on Top of it. If you dispose the Image(s) than, when the page gets uncovered (shown) again, it will not display the images. In such case we have to hook on the the main Navigation Page's 'Popped' event.

我试图尽我所能解释。其余的 - 我希望 - 你将能够从代码中获得:

I have tried to explain to the best I could. The rest - I hope - you will be able to get from the code:

这是PCL项目中的图像子类。

This is the Image Sub-Class in the PCL Project.

using System;

using Xamarin.Forms;

namespace ApplicationClient.CustomControls
{
    public class LSImage : Image
    {
    }
}

以下代码位于Droid项目中。

The following code is in the Droid project.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Android.Util;
using Application.Droid.CustomControls;
using ApplicationClient.CustomControls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

    [assembly: ExportRenderer(typeof(ApplicationClient.CustomControls.LSImage), typeof(LSImageRenderer))]

    namespace Application.Droid.CustomControls
    {
        public class LSImageRenderer : ImageRenderer
        {
            Page page;
            NavigationPage navigPage;

            protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
            {
                base.OnElementChanged(e);
                if (e.OldElement == null)
                {
                    if (GetContainingViewCell(e.NewElement) != null)
                    {
                        page = GetContainingPage(e.NewElement);
                        if (page.Parent is TabbedPage)
                        {
                            page.Disappearing += PageContainedInTabbedPageDisapearing;
                            return;
                        }

                        navigPage = GetContainingNavigationPage(page);
                        if (navigPage != null)
                            navigPage.Popped += OnPagePopped;
                    }
                    else if ((page = GetContainingTabbedPage(e.NewElement)) != null)
                    {
                        page.Disappearing += PageContainedInTabbedPageDisapearing;
                    }
                }
            }

            void PageContainedInTabbedPageDisapearing (object sender, EventArgs e)
            {
                this.Dispose(true);
                page.Disappearing -= PageContainedInTabbedPageDisapearing;
            }

            protected override void Dispose(bool disposing)
            {
                Log.Info("**** LSImageRenderer *****", "Image got disposed");
                base.Dispose(disposing);
            }

            private void OnPagePopped(object s, NavigationEventArgs e)
            {
                if (e.Page == page)
                {
                    this.Dispose(true);
                    navigPage.Popped -= OnPagePopped;
                }
            }

            private Page GetContainingPage(Xamarin.Forms.Element element)
            {
                Element parentElement = element.ParentView;

                if (typeof(Page).IsAssignableFrom(parentElement.GetType()))
                    return (Page)parentElement;
                else
                    return GetContainingPage(parentElement);
            }

            private ViewCell GetContainingViewCell(Xamarin.Forms.Element element)
            {
                Element parentElement = element.Parent;

                if (parentElement == null)
                    return null;

                if (typeof(ViewCell).IsAssignableFrom(parentElement.GetType()))
                    return (ViewCell)parentElement;
                else
                    return GetContainingViewCell(parentElement);
            }

            private TabbedPage GetContainingTabbedPage(Element element)
            {
                Element parentElement = element.Parent;

                if (parentElement == null)
                    return null;

                if (typeof(TabbedPage).IsAssignableFrom(parentElement.GetType()))
                    return (TabbedPage)parentElement;
                else
                    return GetContainingTabbedPage(parentElement);
            }

            private NavigationPage GetContainingNavigationPage(Element element)
            {
                Element parentElement = element.Parent;

                if (parentElement == null)
                    return null;

                if (typeof(NavigationPage).IsAssignableFrom(parentElement.GetType()))
                    return (NavigationPage)parentElement;
                else
                    return GetContainingNavigationPage(parentElement);
            }
        }
    }

最后,我更改了PCL项目中ApplicationClient命名空间中的应用程序名称和Droid项目中的Application.Droid。您应该将其更改为您的应用名称。

Finally, I have changed the Name of the Application in the namespace to 'ApplicationClient' in the PCL project and to 'Application.Droid' in Droid project. You should change it to your app name.

此外,在Renderer类末尾的几个递归方法,我知道我可以将它组合成一个Generic方法。问题是,当需要出现时,我一次建立一个。所以,这就是我离开它的方式。

Also, the few recursive methods at the end of the Renderer class, I know that I could combine it into one Generic method. The thing is, that I have build one at a time as the need arose. So, this is how I left it.

快乐编码,

Avrohom

这篇关于Android上的Xamarin.Forms ListView OutOfMemoryError异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆