Flutter 学习之旅(十五) 基础控件 Image
Image
说到这个控件就有好多问题了,比如在android面试中常常用被问到的内存大小分配问题,压缩问题,还有就是内存回收问题,
但是如果们切换了另外一种语言后,还会面临一些比较简单的常识问题,那就是如何去加载类似draw的图片比 比如占位图,如何去加载一个path 地址的图片文件,如何去加载一个url 地址的图片,加载完图片当页面被回收的时候如何释放image的图片内存.
先来一个使用例子
Scaffold(
appBar: AppBar(
title: Text('Image 学习'),
centerTitle: true,
),
body: Container(
color: Colors.white,
width: double.infinity,
height: double.infinity,
child: ListView(
children: <Widget>[
new Text('资源图片:'),
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Padding(
padding: const EdgeInsets.all(10.0),
child: Image.asset(
'images/bg_baby_handbook_en.png',
width: 100,
),
),
new Image.file(
File('/storage/emulated/0/tsm.jpg'),
width: 120,
height: 120,
///fill(全图显示且填充满,图片可能会拉伸),
///contain(尽可能的填充,直到宽度或者高度有一个被充满),
///cover(显示可能拉伸,也可能裁剪,充满)
///fitWidth(显示可能拉伸,可能裁剪,宽度充满),
///fitHeight显示可能拉伸,可能裁剪,高度充满),
///scaleDown(如果图片大小超过设定大小,则缩小,其他情况则不变)
fit: BoxFit.scaleDown,
),
],
),
new Text('网络占位图片FadeInImage:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: new Row(
children: <Widget>[
new FadeInImage.assetNetwork(
placeholder:'images/bg_baby_handbook_en.png',
image: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: new FadeInImage.assetNetwork(
///加载过程中的占位图
placeholder: 'images/bg_baby_handbook_en.png',
image: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
),
),
],
mainAxisAlignment: MainAxisAlignment.center,
),
),
new Text('圆形圆角图片:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
///圆形裁剪
new ClipOval(
child: Image.network(
imageUrl,
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
new Padding(
padding: const EdgeInsets.fromLTRB(1.0, 0.0, 1.0, 0.0),
child: ClipOval(
child: Image.asset(
'images/bg_baby_handbook_en.png',
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
///方形裁剪
new ClipRRect(
child: Image.network(
imageUrl,
scale: 8.5,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
)
],
),
),
new Text('颜色混合图片:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Image.asset(
'images/bg_baby_handbook_en.png',
color: Colors.red,
colorBlendMode: BlendMode.darken,
width: 100,
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: Image.network(
imageUrl,
scale: 8.5,
colorBlendMode: BlendMode.colorDodge,
color: Colors.blue,
width: 100,
),
),
],
),
),
new Text('centerSlice图片内部拉伸:'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: new Image.asset(
'images/bg_baby_handbook_en.png',
width: 388,
height: 700,
fit: BoxFit.contain,
///如果设定了 centerSlice 这个属性 类似android 的 .9图片,
///显示后的图片大小必须要超过图片原有大小,由于这个功能时为了放大图片,
///如果设定的图片小于原有大小则没有意义
///参数设定大小是以图片远点为中心,哪个区域进行缩放,如果想要整个图片放大,则需要设置原有图片按照比价设置大小
centerSlice: Rect.fromLTWH(0, 0, 351, 488),
),
),
new Text('matchTextDirection图片内部方向'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Directionality(
textDirection: TextDirection.ltr,
child: Image.network(
imageUrl,
height: 100,
matchTextDirection: true,
fit: BoxFit.fitHeight,
),
),
new Padding(
padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: Directionality(
textDirection: TextDirection.rtl,
child: Image.network(
imageUrl,
height: 100,
matchTextDirection: true,
fit: BoxFit.fitHeight,
),
),
),
],
),
),
new Text('点击替换图片'),
new Padding(
padding: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
child: Row(
children: <Widget>[
new RaisedButton(
onPressed: () {
setState(() {
networkImage =
new NetworkImage(imageUrl2, scale: 8.5);
});
},
child: Text('点击更换图片'),
),
new Image(
gaplessPlayback: false,
fit: BoxFit.contain,
image: networkImage,
width: 100,
),
],
),
)
],
),
),
);
}
![](https://img.haomeiwen.com/i11861448/442913b9911edcf0.gif)
1.加载资源文件
///第一种写法
Image.asset('images/bg_baby_handbook_en.png', width: 100, )
///第二种写法
Image( image: AssetImage('images/bg_baby_handbook_en.png'), width: 100,)
2.加载本地文件
Image.file(
File('/storage/emulated/0/tsm.jpg'),
width: 120,
height: 120,
fit: BoxFit.scaleDown,
)
3.加载网络图片
Image.network('url');
///这种写法是可以使用本地资源文件作为占位图,很流行的做法
FadeInImage.assetNetwork(
placeholder:'images/bg_baby_handbook_en.png',
image: imageUrl,
width: 120,
fit: BoxFit.fitWidth,
)
4.image fit属性介绍
fill
全图显示且填充满,图片可能会拉伸,类似android 的fitxy
contain
尽可能的填充,直到宽度或者高度有一个被充满,类似android fitcenter
cover
显示可能拉伸,也可能裁剪,充满,类似android 的centercrop
fitWidth
显示可能拉伸,高度可能裁剪,宽度充满,
fitHeight
显示可能拉伸,宽度可能裁剪,高度充满
scaleDown
如果图片大小超过设定大小,则缩小,其他情况则不变
5.image 裁剪
同样也可以使用Container 的decoration 设置圆形裁剪,但是他的属性不能影响child ,如果chlid 的边框如果有颜色,会使得裁剪失效
/// 圆形裁剪
ClipOval(
child: Image.network(
imageUrl,
width: 100,
height: 100,
fit: BoxFit.cover,
),
)
///方形裁剪
ClipRRect(
child: Image.network(
imageUrl,
scale: 8.5,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
)
6.图片拉伸 centerSlice 类似android 的 .9图片,
如果设定了 centerSlice 这个属性
显示后的图片大小也就是控件大小,必须要超过图片原有大小,由于这个功能是为了放大图片,
如果设定的图片小于原有大小则没有意义
参数设定大小是以图片远点为中心,哪个区域进行缩放,如果想要整个图片放大,则需要设置原有图片按照比价设置大小
Image.asset(
'images/bg_baby_handbook_en.png',
width: 388,
height: 700,
fit: BoxFit.contain,
centerSlice: Rect.fromLTWH(0, 0, 351, 488),
)
7 内存
关于内存方面,看了一篇闲鱼大神的文章,说的非常深刻,
连接https://www.jianshu.com/p/f875c61b680f
简单的说一下我的理解,image 在缓存文件的时候使用ImgaeCache缓存的是一个异步对象,这样做发的弊端就是在没有加载完图片的时候你无法知道图片的大小,而在大量加载图片的时候会产生大量的io操作,同时还发现在页面不可见的时候,页面的widgit 还在加载图片,这个地方需要控制一下就是在页面不可见时暂停图片加载
8 Image 自身的优化
大神们都在研究如何发现并解决实际遇到的问题,我只能查看源码,并认真解读源码中对于image 的描述,在阅读过程中发现了一个比较好的例子,那就是在实际使用过程中你可能需要用到同一张图片不懂宽高的展示的情况,构造函数允许通过cachewidth 和cacheheight 来设定解码时图像的大小,这样可以减少内存的消耗
关于图像缓存
在源码中记录了这样一段好,如果一个image加载的图标时机比较晚(类似image.netWork,fadeInImage),他可能加载的更快,他不需要构造一个相同的ImageCache,ImageCache将找到使用相同key的图片,并且这个图像是保存在内存中的,
我学习flutter的整个过程都记录在里面了
https://www.jianshu.com/c/36554cb4c804
最后附上demo 地址