欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-22 17:11:52
ps:最近看到很多人都开始写年终总结了,时间过得飞快,又到年底了,又老了一岁。 学习内容: 1.进度条 2.缩放 3.controllerbuilder,con...

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,它会基于以下参数来计算转换矩阵,简单解释一下官方给出的例子。

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

官方的例子其实就是这样,给了一个实际的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下载

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。