Android之图片加载框架Fresco基本使用(二)

PS:最近看到很多人都开始写年终总结了,时间过得飞快,又到年底了,又老了一岁。

学习内容:

1.进度条

2.缩放

3.ControllerBuilder,ControllerListener,PostProcesser,Image Request

4.渐进式JPEG与动图的显示

 

  最近这两天把Fresco的官方文档算是看了个差不多,就剩下Fresco的基本原理还有结合okHttp等类库如何使用的问题,虽然官方文档给出的功能比较的多,比如说自定义View,缩略图显示等等,这些我也基本就看了个大概,觉得实际需求应该没有那么高的要求,因此有些东西我这里就不介绍了,详细的情况可以参考官方文档,我这里只是针对一些使用情况比较多的功能进行一个简单的介绍。

1.进度条

  进度条也算是Fresco的一个功能,Fresco内部本身提供了一个ProgressBarDrawable类,效果其实就是一个矩形的蓝色进度条,当图片处于加载状态时,进度条会跟着进行加载,当图片加载完毕之后,那么进度条也随之消失,但是这个进度并不是实时更新的,如果我们想要精确的加载进度,那么我们需要重写内部的方法。

class CustomProgressBar extends Drawable{

    @Override
    protected boolean onLevelChange(int level) {
        //doSomething
        return super.onLevelChange(level);
    }

}

自定义进度条我们就需要实现onLevelChange方法,这里只做一个简单的介绍,如果大家想自定义进度条,那么可以参照ProgressBarDrawable去重写一个进度条,个人感觉这个功能一般般,与其在加载图片的时候使用进度条,不如使用ProgressBarImage属性为图片加载时设置一个进度图片,同时还支持旋转属性。

2.缩放

 DraweeView的缩放和ImageView的缩放类型基本上是相同的,fitXY,centerCrop,唯一与ImageView有区别的就是,他不支持matrix属性,但是追加了一个focusCrop属性来替代matrix属性,这里在设置属性的时候,xml使用fresco:actualScaleType来设置DraweeView的缩放属性或者是使用GenericDraweeHierarchy属性去设置。

GenericDraweeHierarchyBuilder progressHierarchyBuilder = new GenericDraweeHierarchyBuilder(getResources());
GenericDraweeHierarchy progressHierarchy = progressHierarchyBuilder
                .setProgressBarImage(new ProgressBarDrawable(), ScalingUtils.ScaleType.CENTER_INSIDE)
                .build();
progressImageDraweeView.setHierarchy(progressHierarchy);

这里不要使用setScaleType()或者是在xml中android:scaleType设置缩放属性,这是无效的。官方给出focusCrop去替代matrix是有一定道理的,虽然说效果会比matrix要好,不过到底效果如何这个确实无法评判。先说说这个属性的具体作用。

 我们在实际需求中可能会遇到这样的情况,在显示人脸图片的时候,尽量的将图片居中显示,以前我也遇到过这个需求,不过当时是用matrix属性实现的,服务器会传递给我们人脸的重心坐标位置,然后客户端需要根据人脸重心位置将图像进行平移,同时需要将图片进行缩放,是用matrix的话就需要使用matrix.postScale()和matrix.postTranslate()两个方法去实现。简单贴一下当时的处理方式。

@Override
public void onLoadingComplete(String s, View view, Bitmap bitmap) {
   float bitmapWidth = bitmap.getWidth();
   float bitmapHeight = bitmap.getHeight();
   Scale[o] = (params[o].height / bitmapHeight >= params[o].width / bitmapWidth) ? params[o].height / bitmapHeight : params[o].width / bitmapWidth;
   float scaleBitmapWidth = Scale[o] * bitmapWidth;
   float scaleBitmapHeight = Scale[o] * bitmapHeight;
   Matrix matrix = new Matrix();
   matrix.postScale(Scale[o], Scale[o]);
   if (scaleBitmapWidth > scaleBitmapHeight) {  //宽度图
       if (imagedata.get(o).getFace_center_x() == 0 && imagedata.get(o).getFace_center_y() == 0) {
           if(scaleBitmapWidth - params[o].width < 0.5 * scaleBitmapWidth - params[o].width / 2){
              matrix.postTranslate( params[o].width - scaleBitmapWidth ,0);
           }else{
              matrix.postTranslate(-(0.5f * scaleBitmapWidth - params[o].width / 2), 0);
           }
       } else {
           if(scaleBitmapWidth - params[o].width < scaleBitmapWidth * imagedata.get(o).getFace_center_x() - params[o].width / 2) {
              matrix.postTranslate(params[o].width - scaleBitmapWidth, 0);
           } else {
              if (scaleBitmapWidth * imagedata.get(o).getFace_center_x() - params[o].width / 2 < 0) {
                  matrix.postTranslate(0, 0);
              } else {
                  matrix.postTranslate(-(scaleBitmapWidth * imagedata.get(o).getFace_center_x() - params[o].width / 2), 0);
              }
           }
       }
    } else {  //高度图
       if (imagedata.get(o).getFace_center_x() == 0 && imagedata.get(o).getFace_center_y() == 0) {
           if(scaleBitmapHeight - params[o].height < 0.5 * scaleBitmapHeight - params[o].height / 2){
              matrix.postTranslate(0, params[o].height - scaleBitmapHeight);
           }else{
              matrix.postTranslate(0,-(0.5f * scaleBitmapHeight - params[o].height / 2));
           }
       } else {
           if (scaleBitmapHeight - params[o].height < scaleBitmapHeight * imagedata.get(o).getFace_center_y() - params[o].height / 2) {
              matrix.postTranslate(0, params[o].height - scaleBitmapHeight);
           } else {
              if (scaleBitmapHeight * imagedata.get(o).getFace_center_y() - params[o].height / 2 < 0) {
                 matrix.postTranslate(0, 0);
              } else {
                 matrix.postTranslate(0, -(scaleBitmapHeight * imagedata.get(o).getFace_center_y() - params[o].height / 2));
              }
           }
       }
    }
    vh.image[o].setImageMatrix(matrix);}                                    

这格式整的真蛋疼,这就是当时我们实际的项目需求,当时是一个GridView,其中一行有三张图片,需要同时对三张图片进行控制,如果是人像图片,那么需要将头像平移到中央,如果不是人像图片,那么就显示中央位置就可以了,当时实现的还是挺麻烦的,对宽度图和高度图进行了一个判断,因为图片分为宽度图和高度图,那么在计算缩放比例的时候,就需要对二者进行判断,缩放比例的计算 = 实际显示的尺寸 / 图片真正的尺寸,由于图片的不同,我们需要取二者的最大值来设置缩放比例,才不会导致出现缩放过度的问题,同时平移的尺寸不能过度。

 举个例子,比如说我们ImageView的实际显示宽度是100,我们对图片进行了缩放,缩放之后图片的宽度是110,那么我们可以平移的最大距离就是10个单位,不能超过10个单位,否则显示的时候就会出现问题。并且我们需要将图片尽可能的居中显示,也就是说尽可能的使图片损失的单位要小一点,那么我们就只能讲图片平移5个单位,也就是说,左右两边各损失5个单位,这样的显示要比完全偏向一边损失10个单位要好得多。这里可以看到使用matrix还是挺复杂的,也是这里的代码是可以进行优化的,但是优化完之后其实还是比较的麻烦。

 如果我们使用Fresco的focusCrop属性的话,那么事情就会变得很简单。

fresco:actualImageScaleType="focusCrop"

xml中设置缩放类型为focusCrop,然后在Java代码中设置:

PointF focusPoint;
// your app populates the focus point
mSimpleDraweeView
    .getHierarchy()
    .setActualImageFocusPoint(focusPoint);

这里focusPoint就是我们中心点的相对位置,float类型,(0.5,0.5)就相当于centerCrop中央位置,(1.0,1.0)也就是图片的最下角位置,这样如果在显示人像图片的时候问题就非常的轻松了,只需要传递头像重心的相对位置,那么Fresco就会自动的以坐标点为显示中心。看起来好像确实是蛮简单的。

 有时候现有的 ScaleType 不符合你的需求,我们允许你通过实现 ScalingUtils.ScaleType 来拓展它,这个接口里面只有一个方法:getTransform,它会基于以下参数来计算转换矩阵,简单解释一下官方给出的例子。

官方的例子其实就是这样,给了一个实际的View显示尺寸,然后给了一个图片的显示尺寸,如果直接将图片铺上去,那么图片确实可以显示完整但是却损失了一些像素点,那么这时需要对宽度进行缩放,将图片缩放为400,那么这时横向就能够完全的显示在屏幕上了,但是高度却变低了图片从210变成了200,然而实际显示的高度为300,这时使用fitCenter保持宽高比,缩小或者放大,使得图片完全显示在显示边界内,且宽或高契合显示边界。居中显示。总体差不多就是这个意思。但是自我感觉没什么意义,高度确实缩放了,但是宽度也缩放了,那么宽度还是会损失像素的。只不过是放大后的像素而已。

 这里就是官方给出的例子,这里min表示的最小才对,官方翻译为最大的一个。没太具体的研究下面的方法,感觉和使用matrix差不多,但是确实是简化了不少。

public static abstract class AbstractScaleType implements ScaleType {
    @Override
    public Matrix getTransform(Matrix outTransform, Rect parentRect, int childWidth, int childHeight, float focusX, float focusY) {
      // 取宽度和高度需要缩放的倍数中最小的一个
      final float sX = (float) parentRect.width() / (float) childWidth;
      final float sY = (float) parentRect.height() / (float) childHeight;
      float scale = Math.min(scaleX, scaleY);

      // 计算为了均分空白区域,需要偏移的x、y方向的距离
      float dx = parentRect.left + (parentRect.width() - childWidth * scale) * 0.5f;
      float dy = parentRect.top + (parentRect.height() - childHeight * scale) * 0.5f;

      // 最后我们应用它
      outTransform.setScale(scale, scale);
      outTransform.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
      return outTransform;
    }
}

3.ControllerBuilder,ControllerListener,PostProcesser,Image Request

  ControllerBuilder是用来构建Controller的,上一次已经简单的介绍过Controller,主要是控制器,设置图片的uri,能否重新加载等等,那么ControllerBuilder就是使用build模式来构建Controller的。

  ControllerListener则是用来控制下载的监听事件的,如果我们需要在图片下载完成或者之后需要设置一切属性,那么ControllerListener可以帮助我们实现这个功能。但是这个监听事件中是无法修改图片的,如果我们需要修改图片,那么就需要使用到PostProcesser后处理器去修改图片。ImageRequest用于配置更多的属性。也可以设置相关的uri,是否支持渐进式加载,或者设置后处理器。可以看到这几者是存在必然的联系的,因此将这三个功能放在一起进行介绍。

  这里我们为gif图片设置了一个ControllerListener,如果图片获取成功,并且图片存在动画效果,那么播放动画效果,否则toast消息。

 ControllerListener controllerListener = new BaseControllerListener(){

     @Override
     public void onFinalImageSet(String id, Object imageInfo, Animatable animatable) {
         if(animatable!=null){
             animatable.start();
         }
     }

     @Override
     public void onFailure(String id, Throwable throwable) {
         Toast.makeText(context,"图片加载失败",Toast.LENGTH_SHORT).show();
     }

 };

 DraweeController gifController = Fresco.newDraweeControllerBuilder()
                .setUri(Uri.parse("http://img.huofar.com/data/jiankangrenwu/shizi.gif"))
                .setOldController(gifImageView.getController())
                .setControllerListener(controllerListener)
                .build();
 gifImageView.setController(gifController);

理解起来都比较的简单。那么这里再使用后处理器简单处理一下。

 Postprocessor redMeshPostProcessor = new BasePostprocessor() {
      @Override
      public void process(Bitmap bitmap) {
          for (int x = 0; x < bitmap.getWidth(); x+=2) {
              for (int y = 0; y < bitmap.getHeight(); y+=2) {
                  bitmap.setPixel(x, y, Color.TRANSPARENT);
              }
          }
      }

      @Override
      public String getName() {
          return super.getName();
      }
  };
  ImageRequest processorImageRequest = ImageRequestBuilder
          .newBuilderWithSource(Uri.parse("http://avatar.csdn.net/4/E/8/1_y1scp.jpg"))
          .setPostprocessor(redMeshPostProcessor)
          .build();
  DraweeController processorController = Fresco.newDraweeControllerBuilder()
          .setImageRequest(processorImageRequest)
          .setOldController(processImageView.getController())
          .build();
  processImageView.setController(processorController);

这里为图片上绘制了一些小圆点,同时这个属性的设置需要使用ImageRequest来进行配置后处理器。

 ImageRequest的最低请求级别

  1. 检查内存缓存,有如,立刻返回。这个操作是实时的。
  2. 检查未解码的图片缓存,如有,解码并返回。
  3. 检查磁盘缓存,如果有加载,解码,返回。
  4. 下载或者加载本地文件。调整大小和旋转(如有),解码并返回。对于网络图来说,这一套流程下来是最耗时的。

 setLowestPermittedRequestLevel允许设置一个最低请求级别,请求级别和上面对应地有以下几个取值:

  • BITMAP_MEMORY_CACHE
  • ENCODED_MEMORY_CACHE
  • DISK_CACHE
  • FULL_FETCH

 如果你需要立即取到一个图片,或者在相对比较短时间内取到图片,否则就不显示的情况下,这非常有用。

4.渐进式JPEG的设置,动图显示。

 渐进式JPEG表示的是当我们加载一张图片的时候,如果网络比较缓慢,那么图片会从模糊到清晰渐渐呈现,这被称之为渐进式JPEG。具体的使用如下:

       /**
         * 设置渐进式JPEG Config
         * */
        ProgressiveJpegConfig config = new ProgressiveJpegConfig() {
            @Override
            public int getNextScanNumberToDecode(int scanNumber) {
                return 0;
            }

            @Override
            public QualityInfo getQualityInfo(int scanNumber) {
                return null;
            }
        };
        /**
         * 直接控制ImagePipeline Config
         * */
        ImagePipelineConfig imagePipelineConfig = ImagePipelineConfig.newBuilder(context)
                .setProgressiveJpegConfig(config)
                .setDownsampleEnabled(true)
                .build();
        /**
         * 初始化使得Fresco支持渐进式JPEG的加载
         * */
        Fresco.initialize(this,imagePipelineConfig);

初始化的时候需要做这些配置,否则图片是不会呈现渐进式JPEG的。

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse("http://pooyak.com/p/progjpeg/jpegload.cgi"))
        .setProgressiveRenderingEnabled(true) //设置支持渐进式JPEG
        .build();
DraweeController progressiveJPEGController = Fresco.newDraweeControllerBuilder()
        .setImageRequest(request)
        .setOldController(progressiveJpegImageView.getController())
        .build();
progressiveJpegImageView.setController(progressiveJPEGController);

动图显示其实没什么可说的,Controller也不需要做过多的配置。

DraweeController gifController = Fresco.newDraweeControllerBuilder()
             .setUri(Uri.parse("http://img.huofar.com/data/jiankangrenwu/shizi.gif"))
             .setAutoPlayAnimations(true) //使动画自动播放
             .setOldController(gifImageView.getController())
             .build();
gifImageView.setController(gifController);

只需要setAutoPlayAnimations()设置为true就可以在加载后自动进行播放,如果希望手动进行控制,那么就用ControllerListener进行控制或者直接用controller访问animations来完成。

Animatable animatable = mSimpleDraweeView.getController().getAnimatable();
if (animatable != null) {
  animatable.start();
  // later
  animatable.stop();
}

注意:动图设置在高版本的Fresco需要引入gradle,使其支持动画属性。

compile ‘com.facebook.fresco:animated-gif:0.14.0‘

最后放置一个Demo:Demo下载

时间: 2024-10-13 00:20:32

Android之图片加载框架Fresco基本使用(二)的相关文章

Android之图片加载框架Fresco基本使用(一)

PS:Fresco这个框架出的有一阵子了,也是现在非常火的一款图片加载框架.听说内部实现的挺牛逼的,虽然自己还没研究原理.不过先学了一下基本的功能,感受了一下这个框架的强大之处.本篇只说一下在xml中设置属性的相关用法. 0.引入Fresco以及相关注意事项. 1.PlaceHolderImage占位图 2.FailureImage加载失败时显示的图片 3.RetryImage重新加载的图片 4.ProgressBarImage加载时显示的进度图片 5.BackgroundImage背景图 6.

强大的图片加载框架Fresco的使用

前面在卓新科技有限公司实习的时候,在自己的爱吖头条APP中,在图片异步加载的时候和ListView的滑动中,总会出现卡顿,这是因为图片的缓存做的不是足够到位,在项目监理的帮助下,有使用Xutils框架下的图片加载,也有使用ImageLoader来实现,在今天在威哥的微信公众号了gank到了一个当前最强大的图片加载框架——Fresco. Fresco是Facebook发布的一款开源框架,号称是目前最强的Android图片加载库,在内存方面的表现极为优秀,既然有如此信心,对于Fresco的一些介绍,

优雅地实现Android主流图片加载框架封装,可无侵入切换框架

项目开发中,往往会随着需求的改变而切换到其它图片加载框架上去.如果最初代码设计的耦合度太高,那么恭喜你,成功入坑了.至今无法忘却整个项目一行行去复制粘贴被支配的恐惧.:) 那么是否存在一种方式 能够一劳永逸地解决这个痛点呢?下面我们来分析一下图片加载框架面对的现状和解决思路. 问题现状 一个优秀的框架一般在代码设计的时候已经封装很不错了,对于开发者而言框架的使用也是很方便,但是为什么说我们往往还要去做这方面的框架封装呢?原因很简单,实际项目开发中,我们不得不面对着日新月异的需求变化,想要在这个变

Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程

转载地址:http://blog.csdn.net/guolin_blog/article/details/53939176 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 . 在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示: Glide.with(this).load(url).into(i

android Glide图片加载框架的初探

一.Glide图片加载框架的简介 谷歌2014年开发者论坛会上介绍的图片加载框架,它让我们在处理不管是网路下载的图片还是本地的图片,减轻了很多工作量, 二.开发步骤: 1.添加链接库 compile 'com.github.bumptech.glide:glide:3.7.0' 2.代码编写,主要特性 Glide.with(context) .load(imgUrl) .dontAnimate() .skipMemoryCache(true) .diskCacheStrategy(DiskCac

Android自定义图片加载框架

思路: 1. 压缩图片 压缩本地图片: 获得imageview想要显示的大小 -> 设置合适的inSampleSize ->      压缩网络图片: a. 硬盘缓存开启 -> 直接下载存到sd卡,然后采用本地的压缩方案 b. 硬盘缓存关闭 -> 使用BitmapFactory.decodeStream(is, null, opts); 2. 图片加载架构 图片压缩加载完 -> 放入LruCache -> 设置到ImageView上 1.单例,包含一个LruCache用

Android中常见的图片加载框架

图片加载涉及到图片的缓存.图片的处理.图片的显示等.而随着市面上手机设备的硬件水平飞速发展,对图片的显示要求越来越高,稍微处理不好就会造成内存溢出等问题.很多软件厂家的通用做法就是借用第三方的框架进行图片加载. 开源框架的源码还是挺复杂的,但使用较为简单.大部分框架其实都差不多,配置稍微麻烦点,但是使用时一般只需要一行,显示方法一般会提供多个重载方法,支持不同需要.这样会减少很不必要的麻烦.同时,第三方框架的使用较为方便,这大大的减少了工作量.提高了开发效率.本文主要介绍四种常用的图片加载框架,

主流图片加载框架ImageLoader、Glide、Picasso、Fresco性能分析---图片加载速度比较

图片加载这种实现繁琐,可复用性又极强的东西,自然是选择使用图片加载框架来快速实现. 像是Android-Universal-Image-Loader.Glide.Picasso.Fresco之类, 但是这时候的烦恼在于,这么多图片加载框架到底谁最实用? 有说Fresco,因为支持WebP,还是用了NDK来加载图片,减少JavaHeap的使用 有Picasso,简洁高效 有说Glide,Picasso升级,可以加载Gif,在Picasso基础上扩展了很多方法 ImageLoader 使用最广,因为

Fresco从配置到使用(最高效的图片加载框架)

Frescoj说明: facebook开源的针对android应用的图片加载框架,高效和功能齐全. 支持加载网络,本地存储和资源图片: 提供三级缓存(二级memory和一级internal storage): 支持JPEGs,PNGs,GIFs,WEBPs等,还支持Progressive JPEG,优秀的动画支持: 图片圆角,scale,自定义背景,overlays等等: 优秀的内存管理: 2.3(Gingerbread)或以上. --------------------------------