Fresco

学习资源汇总:

Demo效果图:

  • 首页:
     
  • 带进度条的图片:

            

  • 图片的不同裁剪

           

  • 圆形和圆角图片

               

  • 渐进式展示图片

          

  • GIF动画图片

          

  • 多图请求及图片复用

          

  • 图片加载监听

           

  • 图片修改和旋转

          

  • 修改图片

         

  • 动态展示图片
         
         

Fresco框架简要说明:

[下面文字摘自Fresco中文教材,主要方便自己后期回顾博客复习]

一,Fresco 是一个强大的图片加载组件:

  • Fresco 中设计有一个叫做 image pipeline 的模块。它负责从网络,从本地文件系统,本地资源加载图片。为了最大限度节省空间和CPU时间,它含有3级缓存设计(2级内存,1级文件)。
  • Fresco 中设计有一个叫做 Drawees 模块,方便地显示loading图,当图片不再显示在屏幕上时,及时地释放内存和空间占用。
  • Fresco 支持 Android2.3(API level 9) 及其以上系统。

二,特性:

1.内存管理:

解压后的图片,即Android中的Bitmap,占用大量的内存。大的内存占用势必引发更加频繁的GC。在5.0以下,GC将会显著地引发界面卡顿。
在5.0以下系统,Fresco将图片放到一个特别的内存区域。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。
Fresco 在低端机器上表现一样出色,你再也不用因图片内存占用而思前想后。
2.图片的渐进式呈现:
渐进式的JPEG图片格式已经流行数年了,渐进式图片格式先呈现大致的图片轮廓,然后随着图片下载的继续,呈现逐渐清晰的图片,这对于移动设备,尤其是慢网络有极大的利好,可带来更好的用户体验。
Android 本身的图片库不支持此格式,但是Fresco支持。使用时,和往常一样,仅仅需要提供一个图片的URI即可,剩下的事情,Fresco会处理。
3.Gif图和WebP格式:
支持加载Gif图,支持WebP格式。

三,图像的呈现:

  1. 自定义居中焦点(对人脸等图片显示非常有帮助)
  2. 圆角图,当然圆圈也行。
  3. 下载失败之后,点击重现下载
  4. 自定义占位图,自定义overlay, 或者进度条
  5. 指定用户按压时的overlay四,图像的加载:
  1. Fresco 的 image pipeline 设计,允许用户在多方面控制图片的加载:
  2. 为同一个图片指定不同的远程路径,或者使用已经存在本地缓存中的图片
  3. 先显示一个低解析度的图片,等高清图下载完之后再显示高清图
  4. 加载完成回调通知
  5. 对于本地图,如有EXIF缩略图,在大图加载完成之前,可先显示缩略图
  6. 缩放或者旋转图片
  7. 处理已下载的图片
  8. WebP 支持

四.注意事项:

 1问题处理:

   1)重复的边界:这是使用圆角的一个缺陷。 参考圆角和圆圈 来获取更多信息。

2)图片没有加载:你可以从 image pipeline 打出的日志来查看原因。 这里提供一些通常会导致问题的原因:

3)文件不可用 :无效的路径、链接会导致这种情况。
判断网络链接是否有效,你可以尝试在浏览器中打开它,看看是否图片会被加载。若图片依然加载不出来,那么这不是Fresco的问题。
判断本地文件是否有效,你可以通过下面这段代码来校验:

FileInputStream fis = new FileInputStream(new File(localUri.getPath()));

如果这里抛出了异常,那么这不是Fresco的问题,可能是你的其他代码导致的。有可能是没有获取到SD卡读取权限、路径不合法、文件不存在等。

4)OOM - 无法分配图片空间:加载特别特别大的图片时最容易导致这种情况。如果你加载的图片比承载的View明显大出太多,那你应该考虑将它Resize一下。

5)Bitmap太大导致无法绘制:Android 无法绘制长或宽大于2048像素的图片。这是由OpenGL渲染系统限制的,如果它超过了这个界限,Fresco会对它进行Resize。

6)通过Logcat来判断原因:在加载图片时会出现各种各样的原因导致加载失败。 在使用Fresco的时候,最直接的方式就是查看 image pipeline 打出的VERBOSE级别日志。

7)启动日志:默认情况下Fresco是关闭日志输出的,你可以配置image pipeline让它启动.

Set<RequestListener> requestListeners = new HashSet<>();
requestListeners.add(new RequestLoggingListener());
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
   // other setters
   .setRequestListeners(requestListeners)
   .build();
Fresco.initialize(context, config);
FLog.setMinimumLoggingLevel(FLog.VERBOSE);

8)查看日志:你可以通过下面这条shell命令来查看Fresco日志:

adb logcat -v threadtime | grep -iE 'LoggingListener|AbstractDraweeController|BufferedDiskCache'


它的输出为如下格式:

08-12 09:11:14.791 6690 6690 V unknown:AbstractDraweeController: controller 28ebe0eb 0 -> 1: initialize
08-12 09:11:14.791 6690 6690 V unknown:AbstractDraweeController: controller 28ebe0eb 1: onDetach
08-12 09:11:14.791 6690 6690 V unknown:AbstractDraweeController: controller 28ebe0eb 1: setHierarchy: null
08-12 09:11:14.791 6690 6690 V unknown:AbstractDraweeController: controller 28ebe0eb 1: setHierarchy: com.facebook.drawee.generic.GenericDraweeHierarchy@2bb88e4
08-12 09:11:14.791 6690 6690 V unknown:AbstractDraweeController: controller 28ebe0eb 1: onAttach: request needs submit
08-12 09:11:14.791 6690 6690 V unknown:PipelineDraweeController: controller 28ebe0eb: getDataSource
08-12 09:11:14.791 6690 6690 V unknown:RequestLoggingListener: time 11201791: onRequestSubmit: {requestId: 1, callerContext: null, isPrefetch: false}
08-12 09:11:14.792 6690 6690 V unknown:RequestLoggingListener: time 11201791: onProducerStart: {requestId: 1, producer: BitmapMemoryCacheGetProducer}
08-12 09:11:14.792 6690 6690 V unknown:RequestLoggingListener: time 11201792: onProducerFinishWithSuccess: {requestId: 1, producer: BitmapMemoryCacheGetProducer, elapsedTime: 1 ms, extraMap: {cached_value_found=false}}
08-12 09:11:14.792 6690 6690 V unknown:RequestLoggingListener: time 11201792: onProducerStart: {requestId: 1, producer: BackgroundThreadHandoffProducer}
08-12 09:11:14.792 6690 6690 V unknown:AbstractDraweeController: controller 28ebe0eb 1: submitRequest: dataSource: 36e95857
08-12 09:11:14.792 6690 6734 V unknown:RequestLoggingListener: time 11201792: onProducerFinishWithSuccess: {requestId: 1, producer: BackgroundThreadHandoffProducer, elapsedTime: 0 ms, extraMap: null}
08-12 09:11:14.792 6690 6734 V unknown:RequestLoggingListener: time 11201792: onProducerStart: {requestId: 1, producer: BitmapMemoryCacheProducer}
08-12 09:11:14.792 6690 6734 V unknown:RequestLoggingListener: time 11201792: onProducerFinishWithSuccess: {requestId: 1, producer: BitmapMemoryCacheProducer, elapsedTime: 0 ms, extraMap: {cached_value_found=false}}
08-12 09:11:14.792 6690 6734 V unknown:RequestLoggingListener: time 11201792: onProducerStart: {requestId: 1, producer: EncodedMemoryCacheProducer}
08-12 09:11:14.792 6690 6734 V unknown:RequestLoggingListener: time 11201792: onProducerFinishWithSuccess: {requestId: 1, producer: EncodedMemoryCacheProducer, elapsedTime: 0 ms, extraMap: {cached_value_found=false}}
08-12 09:11:14.792 6690 6734 V unknown:RequestLoggingListener: time 11201792: onProducerStart: {requestId: 1, producer: DiskCacheProducer}
08-12 09:11:14.792 6690 6735 V unknown:BufferedDiskCache: Did not find image for http://www.example.com/image.jpg in staging area
08-12 09:11:14.793 6690 6735 V unknown:BufferedDiskCache: Disk cache read for http://www.example.com/image.jpg
08-12 09:11:14.793 6690 6735 V unknown:BufferedDiskCache: Disk cache miss for http://www.example.com/image.jpg
08-12 09:11:14.793 6690 6735 V unknown:RequestLoggingListener: time 11201793: onProducerFinishWithSuccess: {requestId: 1, producer: DiskCacheProducer, elapsedTime: 1 ms, extraMap: {cached_value_found=false}}
08-12 09:11:14.793 6690 6735 V unknown:RequestLoggingListener: time 11201793: onProducerStart: {requestId: 1, producer: NetworkFetchProducer}
08-12 09:11:15.161 6690 7358 V unknown:RequestLoggingListener: time 11202161: onProducerFinishWithSuccess: {requestId: 1, producer: NetworkFetchProducer, elapsedTime: 368 ms, extraMap: null}
08-12 09:11:15.162 6690 6742 V unknown:BufferedDiskCache: About to write to disk-cache for key http://www.example.com/image.jpg
08-12 09:11:15.162 6690 6734 V unknown:RequestLoggingListener: time 11202162: onProducerStart: {requestId: 1, producer: DecodeProducer}
08-12 09:11:15.163 6690 6742 V unknown:BufferedDiskCache: Successful disk-cache write for key http://www.example.com/image.jpg
08-12 09:11:15.169 6690 6734 V unknown:RequestLoggingListener: time 11202169: onProducerFinishWithSuccess: {requestId: 1, producer: DecodeProducer, elapsedTime: 7 ms, extraMap: {hasGoodQuality=true, queueTime=0, bitmapSize=600x400, isFinal=true}}
08-12 09:11:15.169 6690 6734 V unknown:RequestLoggingListener: time 11202169: onRequestSuccess: {requestId: 1, elapsedTime: 378 ms}
08-12 09:11:15.184 6690 6690 V unknown:AbstractDraweeController: controller 28ebe0eb 1: set_final_result @ onNewResult: image: CloseableReference 2fd41bb0

在这个示例中,我们发现名为28ebe0eb的 DraweeView 向名为36e95857的 DataSource 进行了图像请求。首先,图片没有在内存缓存中找到,也没有在磁盘缓存中找到,最后去网络上下载图片。下载成功后,图片被解码,之后请求结束。最后数据源通知 controller 图片就绪,显示图片(set_final_result)。


2.一些陷阱:
1)不要使用 ScrollViews:如果你想要在一个长的图片列表中滑动,你应该使用 RecyclerView,ListView,或 GridView。这三者都会在你滑动时不断重用子视图。Fresco 的 view 会接收系统事件,使它们能正确管理内存。

ScrollView 不会这样做。因此,Fresco view 不会被告知它们是否在屏幕上显示,并保持图片内存占用直到你的 Fragment 或 Activity 停止。你的 App 将会面临更大的 OOM 风险。

2)不要向下转换:不要试图把Fresco返回的一些对象进行向下转化,这也许会带来一些对象操作上的便利,但是也许在后续的版本中,你会遇到一些因为向下转换特性丢失导致的难以处理的问题。

3)不要使用getTopLevelDrawable:DraweeHierarchy.getTopLevelDrawable() 仅仅 应该在DraweeViews中用,除了定义View中,其他应用代码建议连碰都不要碰这个。
在自定义view中,也千万不要将返回值向下转换,也许下个版本,我们会更改这个返回值类型。

4)不要复用 DraweeHierarchies:永远不要把 DraweeHierarchy 通过 DraweeView.setHierarchy 设置给不同的View。DraweeHierarchy 是由一系列 Drawable 组成的。在 Android 中, Drawable 不能被多个 View 共享。

5)不要在多个DraweeHierarchy中使用同一个Drawable:原因同上。不过你可以在占位图、重试图、错误图中使用相同的资源ID,Android 实际会创建不同的 Drawable。 如果你使用GenericDraweeHierarchyBuilder,那么需要调用Resources.getDrawable来通过资源获取图片。不过请不要只调用一次然后将结果传给不同的Hierarchy!

6)不要直接控制 hierarchy:不要直接使用 SettableDraweeHierarchy 方法(reset,setImage,…)。它们应该仅由 controller 使用。不要使用setControllerOverlay来设置一个覆盖图,这个方法只能给 controller 调用。如果你需要显示覆盖图,可以参考Drawee的各种效果配置

7)不要直接给 DraweeView 设置图片:目前 DraweeView 直接继承于 ImageView,因此它有 setImageBitmap,
setImageDrawable 等方法。
如果利用这些方法直接设置一张图片,内部的 DraweeHierarchy 就会丢失,也就无法取到image
pipeline 的任何图像了。

8)使用 DraweeView 时,请不要使用任何 ImageView 的属性:在后续的版本中,DraweeView 会直接从 View 派生。任何属于 ImageView 但是不属于 View 的方法都会被移除。


3.   为什么不之策wrap_content:人们经常会问,为什么Fresco中不可以使用wrap_content?
主要的原因是,Drawee永远会在getIntrinsicHeight/getIntrinsicWidth中返回-1。
这么做的原因是 Drawee 不像ImageView一样。它同一时刻可能会显示多个元素。比如在从占位图渐变到目标图时,两张图会有同时显示的时候。再比如可能有多张目标图片(低清晰度、高清晰度两张)。如果这些图像都是不同的尺寸,那么很难定义”intrinsic”尺寸。
如果我们要先用占位图的尺寸,等加载完成后再使用真实图的尺寸,那么图片很可能显示错误。它可能会被根据占位图的尺寸来缩放、裁剪。唯一防止这种事情的方式就只有在图片加载完成后强制触发一次layout。这样的话不仅会影响性能,而且会让应用的界面突变,很影响用户体验!如果用户正在读一篇文章,然后在图片加载完成后整篇文章突然向下移动,这是非常不好的。
所以你必须指定尺寸或者用match_parent来布局。
你如果从服务端请求图片,服务端可以做到返回图片尺寸。然后你拿到之后通过setLayoutParams 来给View设置宽高。
当然如果你必须要使用wrap_content,那么你可以参考StackOverflow上的一个回答。但是我们以后会移除这个功能,Ugly things should look ugly。

4.共享元素动画:使用 ChangeBounds,而不是ChangeImageTransform

Android 5.0 (Lollipop) 引入了 共享元素动画,允许在多个Activity切换时共享相同的View!
你可以在XML中定义这个变换。有个ChangeImageTransform变换可以在共享元素切换时对ImageView进行变换,可惜Fresco暂时不支持它,因为Drawee维护着自己的转换Matrix。
幸运的是你可以有另一种做法:使用ChangeBounds。你可以改变layout的边界,这样Fresco会根据它进行自适应,也能够达到你想要的功能。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值