在ListView中显示图像时内存不足

时间:2018-07-24 19:15:29

标签: android dart flutter

好吧,所以我想从LinearView中获取项目并获得其可见性,以便释放隐藏项目上的内存。

有可能这样做吗? 您还有其他想法吗?

因此,基本上,我想显示从URL动态下载的图像流,我希望它们位于ListView中,以便该人员可以滚动浏览它们,但是如果我们仅使用像这样的常规构建器加载ListView ,

ListView lvb = new ListView.builder(
    cacheExtent: 1.0,
    itemBuilder: (BuildContext context, int index) {
      return new ImageView(MyApp.imageLinks.values.elementAt(index));
    },
);

它将立即下载所有项目,并且设备将耗尽内存。所以我在想,我只是释放图像占用的内存,然后允许用户加载更多图像而不必担心内存问题。

此外,我想一次只加载5张图像,因此将显示1张,上方显示2张,下方显示2张,以实现更快更流畅的滚动。

我的问题:

  • 有没有一种方法可以确定项目是否已绘制(在视图中)?
  • 我可以限制我想在LinearView中有多少个ImageView,但是要保持滚动能力。

编辑:承诺的ImageView类

class ImageView extends StatelessWidget {
  String url;

  ImageView([String url]) {
    if (url != null) {
      this.url = url;
    }
    print("New image");
  }

  @override
  Widget build(BuildContext context) {
    FractionallySizedBox fsb;
    if (url != null) {
      Image el = Image.network(
        url,
        scale: 0.1,
      );
      fsb = new FractionallySizedBox(
        widthFactor: 1.0,
        child: el,
      );
    print("New image created");
    return fsb;
  }
}

ListView一旦开始列出项目,就会开始向控制台发送“创建新图像”的垃圾邮件。

2 个答案:

答案 0 :(得分:2)

我将以不回答的方式回答。从理论上讲,可以使用Slivers和CustomScrollView或通过使用自定义RenderObjects来确定是绘制项目还是不绘制项目,但我认为这并不是您真正想要的。

除非您对图像做一些奇怪的事情,请使用ListView.builder来防止您描述的 exact 场景。

documentation for ListView.builder(强调我的名字)的第一行开始:

  

创建可滚动的线性小部件数组,这些小部件是按需创建的

使用ListView.builder而不是新的ListView([items])的想法是,它应自动处理您正在谈论的内容-仅创建当前可见的元素。

如果您已实现此功能,而实际上是在真实设备上用尽了内存,那么这是向Flutter人员= D提出错误的好例子。

此外,将cacheExtent设置为1.0实际上会降低性能。来自cacheExtent docs

  

视口在可见区域前后都有一个要缓存的区域   用户滚动时将变为可见的项目。

     

落入此缓存区域的项目即使它们是   在屏幕上尚未显示。 cacheExtent描述多少像素   缓存区在前沿之前和之后延伸   视口的边缘。

通过将其设置为1.0,就可以使它离开视口后立即被释放。而且下一个图像要等到视图很清晰才加载。因此,下次滚动(向后或向前)时,(下一个或最后一个)图像必须在其进入视图时加载。

如果将此值设置得较高(或将其设置为当前的默认值250.0),则可以进行设置,以使下一幅图像可以完全根据您的需要提前加载。

编辑:

因此,这里还有其他因素在起作用。感谢您发布ImageView代码。

问题是您正在使用带有widthFactor但没有HeightFactor的FractionallySizedBox。您将宽度设置为1.0,但没有设置高度(无论如何都无法工作),因此它占用的空间为零,因此会一直尝试永久加载图像。

最好的选择是使用SizedBox,ConstrainedBox或AspectRatio为图像指定高度,然后为containcover使用Image.network的fit选项。

这是一个有趣的示例-无限加载dogs =)

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: Text("Title"),
        ),
        body: ListView.builder(
          cacheExtent: 1000.0,
          itemBuilder: (BuildContext context, int index) {
            return new ImageView(
              num: index,
              url: "https://loremflickr.com/640/480/dog?random=$index",
            );
          },
        ),
      ),
    );
  }
}

class ImageView extends StatelessWidget {
  final int num;
  final String url;

  ImageView({@required this.num, @required this.url});

  @override
  Widget build(BuildContext context) {
    print("Building image number $num");
    return new AspectRatio(
      aspectRatio: 3.0 / 2.0,
      child: Image.network(
        url,
        fit: BoxFit.cover,
      ),
    );
  }
}

答案 1 :(得分:0)

我知道这是一个老话题,但仍然与我相关,我没有发现这些答案中的任何一个真正有效,并且提到的插件/小部件并没有真正提供很大的灵活性。我最终找到了小部件 photo_gallery

它足够灵活,可以加载特定专辑而不是所有专辑,并且 ThumbnailProvider 可以生成出色的图像而不会占用太多内存。我在 GridView 的同一页面上加载了多达 50 个 1+MB 的文件。

我不会在这里添加任何代码,因为 repo 中的示例就是您所需要的。我给您的唯一建议是设置 ThumbnailProvider 的宽度和高度。默认为 72x72,这些天相当低。我在旧的 Nexus 6 上使用了 256x256,没有任何内存问题。抱歉,我不能说 iOS 设备。