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

MG *_*ine 4 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));
    },
);
Run Code Online (Sandbox Code Playgroud)

它会一次下载所有项目,设备会耗尽内存。所以我想我只是释放图像占用的内存,然后允许用户加载更多图像而不必担心内存。

此外,我希望一次只加载 5 张图像,因此将显示 1 张,上方 2 张和下方 2 张,以便更快、更流畅地滚动。

我的问题:

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

编辑:承诺的 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

rmt*_*zie 5

我将用不回答来回答这个问题。理论上可以确定是否使用 Slivers 和 CustomScrollView 或可能使用自定义 RenderObjects 来绘制项目,但我认为这不是您真正想要的。

除非您对图像做一些奇怪的事情EDIT: SEE END,否则使用 ListView.builder 旨在防止您描述的确切场景。

ListView.builder 文档的第一行(重点是我的):

创建按需创建的可滚动的线性小部件数组。

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

如果您已经实现了这一点并且您实际上在真实设备上耗尽了内存,那么这将是一个很好的案例,可以向 Flutter 人员 =D 提出错误。

此外,设置cacheExtent为 1.0 实际上会降低性能。从cacheExtent 文档

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

落入该缓存区域的项目即使在屏幕上(尚)不可见,也会进行布局。cacheExtent 描述了缓存区域在视口的前缘之前和后缘之后延伸了多少像素。

通过将其设置为 1.0,您可以在图片离开视口时立即将其释放。并且下一个图像在它们几乎可见之前不会加载。因此,下次您滚动(向后或向前)时,(下一个或最后一个)图像必须在进入视图时加载。

如果您将其设置得更高(或将其保留为当前的默认值 250.0),您可以将其设置为让下一张图片提前加载,完全按照您的需要。

编辑:

所以这里还有一个额外的因素在起作用。感谢您发布您的 ImageView 代码。

问题是您使用的是带有 widthFactor 但没有 HeightFactor 的 FractionallySizedBox。您将宽度设置为 1.0,但没有高度(无论如何都行不通),因此它占用的空间为零,因此一直试图永远加载图像。

最好的办法是使用 SizedBox、ConstrainedBox 或 AspectRatio 为图像指定高度,然后使用Image.network的选项containcover其中fit之一。

这是一个有趣的例子 - 无限加载狗 =)

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,
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)