安卓常用框架及依赖库APP开发经验总结

android 图片加载库(4)- Fresco

2018-02-10  本文已影响119人  前行的乌龟

上一篇说完 Glide 之后,我们现在来说下 Fresco,从使用感受上来看,使用 Fresco 比 Glide 要简便不少,但是 Fresco 需要使用单独的图片控件,系统原生的就不行了,代码侵入性太强。从性能上来说 Fresco 很强大,尤其内存性能强大,但是 Fresco 包太 5M,Glide 的包才 900K

Fresco 支持很多功能:

Fresco 的不足:

学习资料


Fresco 从15年出现开始,已经有很多优秀的学习资料了,跟着这些学习资料走就是最好的选择,当然不要忘了看完入门再去看看光放文档,中文的:Fresco官方中文文档

入门3部曲:

深入学习:

简单使用


上面我们看完入门文档后,这里我还是要记录下 Fresco 常用的使用和各种设置的。

引入依赖
implementation 'com.facebook.fresco:fresco:1.8.1'

若想支持 webp,gif 还需要添加相应的支持库

compile 'com.facebook.fresco:fresco:1.5.0'
//加载gif动图需添加此库
compile 'com.facebook.fresco:animated-gif:1.5.0'
//加载webp动图需添加此库
compile 'com.facebook.fresco:animated-webp:1.5.0'
//支持webp需添加此库
compile 'com.facebook.fresco:webpsupport:1.5.0'
//网络实现层使用okhttp3需添加此库
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.5.0'
全局初始化
 Fresco.initialize(this);
加载图片
   image.setImageURI( "xxx" );
重试代码
  DraweeController controller = Fresco.newDraweeControllerBuilder()
                //加载的图片URI地址   
                .setUri("xxx")
                //设置点击重试是否开启
                .setTapToRetryEnabled(true)
                //设置旧的Controller
                .setOldController(image.getController())
                //构建
                .build();
        image.setController(controller);
xml 使用

这里简单写一下,全部属性去看下面的属性列表

 <com.facebook.drawee.view.SimpleDraweeView
            android:id="@+id/image_net"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="10dp"
            app:actualImageScaleType="focusCrop"
            app:fadeDuration="300"
            app:failureImage="@drawable/icon_error"
            app:layout_constraintDimensionRatio="H,1:1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:placeholderImage="@drawable/icon_place"
            app:placeholderImageScaleType="centerCrop"
            app:progressBarAutoRotateInterval="5000"
            app:progressBarImage="@drawable/icon_process"
            app:retryImage="@drawable/icon_retry"
            app:roundingBorderWidth="3dp"
            app:roundingBorderColor="@color/colorPrimary"
            app:roundAsCircle="true"/>
全部属性列表
XML属性 意义
fadeDuration 淡入淡出动画持续时间(单位:毫秒ms)
actualImageScaleType 实际图像的缩放类型
placeholderImage 占位图
placeholderImageScaleType 占位图的缩放类型
progressBarImage 进度图
progressBarImageScaleType 进度图的缩放类型
progressBarAutoRotateInterval 进度图自动旋转间隔时间(单位:毫秒ms)
failureImage 失败图
failureImageScaleType 失败图的缩放类型
retryImage 重试图
retryImageScaleType 重试图的缩放类型
backgroundImage 背景图
overlayImage 叠加图
pressedStateOverlayImage 按压状态下所显示的叠加图
roundAsCircle 设置为圆形图
roundedCornerRadius 圆角半径
roundTopLeft 左上角是否为圆角
roundTopRight 右上角是否为圆角
roundBottomLeft 左下角是否为圆角
roundBottomRight 右下角是否为圆角
roundingBorderWidth 圆形或者圆角图边框的宽度
roundingBorderColor 圆形或者圆角图边框的颜色
roundWithOverlayColor 圆形或者圆角图底下的叠加颜色(只能设置颜色)
viewAspectRatio 控件宽高比

缩放类型 focusCrop = centerCrop,这个是 Fresco 特有的,可以指定居中点的位置

缓存策略


Fresco 的图片缓存是非常值得我们去好好探寻一番的,Fresco 在 5.0 以下的内存性能是要比 Glide 强很多的,但是 5.0 之后就趋同了,都是用 BitmapFactory.Options.inBitmap 这个优化方案了,这毕竟是 google 官方的优化嘛,5.0 之前 google 做的太烂了,大家各自有不同的优化可以理解,但是到 5.0 之后 Google 的 inBitmap 这个 Bitmap 在堆内存的缓存池的优化做的还是不错的,所以大家又回到 google 的方案了。

Fresco 的网络缓存,磁盘缓存每什么说的,重点在 Bitmap 的内存缓存上

这2大框架,相比较起来,理论上还是 Fresco 要节省一些内存的,图片没 decode 解码就不是bitmap 对象,内存占用小很多,但是劣势是 CPU 工作量大一些,得益于 Fresco 优秀的线程池设计,这点不是问题,Fresco 可是维护了3个干不同类型活的线程池,具体的可以去看官方文档。但是实际上 Fresco 不支持根据 view 的大小动态调节图片尺寸,在实际使用中要是不优化这块的话 Fresco 的内存占用比 Glide 要多一点点的。这块优化内容下面我会说的,不要急。

下面是我在学习 Fresco 内存优化中的一些收获,内容比较杂,大家看看,也许能消除大家一些疑惑。

android 中内存,优化的一些知识看这里:

根据 android 的内存特性( android 对于 bitmap 的优化 ),Fresco 的内存管理是分上下2部分的,分割点就在于 5.0 系统对于 bitmap 的优化

官方文档的描述:


Snip20180303_4.png

所以呢,Fresco 的内存缓存管理做的还是不错的,View 在离开显示区域时会触动回收bitmap 资源的,这点对比 Glide 监听页面生命周期回收资源的方式,我觉得要好上不少,这种方式才是紧密贴合 5.0 之后 Google 对 bitmap 的优化思路的,java heap 堆内存层面的高度复用。

线程池


Fresco 的线程池是必须要说一下的,下面内容来自官方文档:

Image pipeline 默认有3个线程池:

  • 3个线程用于网络下载
  • 2个线程用于磁盘操作: 本地文件的读取,磁盘缓存操作。
  • 2个线程用于CPU相关的操作: 解码,转换,以及后处理等后台操作。

渐进式显示


什么是渐进式显示,之前我也不知道,但是看到效果后就知道了,原来一直在我们身边


顺序式
渐进式

顺序式我们不说,这里我们来说说渐进式,Fresco 支持渐进式,渐进式除了需要 Fresco 库的支持外,最总要的图片是要按照渐进式编码做的,这点网上有很多,大伙搜索一下,我看到的是 渐进式需要 SRGB 编码。

我们可以在 Fresco 初始化时操作的 ImagePipelineConfig 类中,设置自定义的渐进式解码类,也可以使用默认的 SimpleProgressiveJpegConfig

ProgressiveJpegConfig pjpegConfig = new ProgressiveJpegConfig() {
  @Override
  public int getNextScanNumberToDecode(int scanNumber) {
    return scanNumber + 2;
  }    

  public QualityInfo getQualityInfo(int scanNumber) {
    boolean isGoodEnough = (scanNumber >= 5);
    return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false);
  }
}

ImagePipelineConfig config = ImagePipelineConfig.newBuilder()
    .setProgressiveJpegConfig(pjpeg)
    .build();

文档提示我们,必须显式地在加载时设置,才允许渐进式JPEG图片加载

Uri uri;
ImageRequest request = ImageRequestBuilder
    .newBuilderWithSource(uri)
    .setProgressiveRenderingEnabled(true)
    .build();
PipelineDraweeController controller = Fresco.newControllerBuilder()
    .setImageRequest(requests)
    .setOldController(mSimpleDraweeView.getController())
    .build();

mSimpleDraweeView.setController(controller);

资料来源:

渐进式编码资料:

Fresco API 深入使用


自古以来,看一个开源库,无非以下几步:

只有知道了 Fresco 的功能代码结构,我们才好继续深入的使用 Fresco 这个库,一个优秀的库必然是有很多可以深入使用研究的功能和操作的。这里我分析不了源码,没那么高水平,通过官方文档,简单写下功能分层的几个核心类。自己的水平还是差很多啊,要不然至少框架结构的 UML 类图至少是能画出来的。

Fresco 核心功能对象:

全局配置

以下列出了所有可配置的选项,当然了一般我们是不会配置这么多选项的


ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
    .setBitmapMemoryCacheParamsSupplier(bitmapCacheParamsSupplier)
    .setCacheKeyFactory(cacheKeyFactory)
    .setDownsampleEnabled(true)
    .setEncodedMemoryCacheParamsSupplier(encodedCacheParamsSupplier)
    .setExecutorSupplier(executorSupplier)
    .setImageCacheStatsTracker(imageCacheStatsTracker)
    .setMainDiskCacheConfig(mainDiskCacheConfig)
    .setMemoryTrimmableRegistry(memoryTrimmableRegistry)
    .setNetworkFetchProducer(networkFetchProducer)
    .setPoolFactory(poolFactory)
    .setProgressiveJpegConfig(progressiveJpegConfig)
    .setRequestListeners(requestListeners)
    .setSmallImageDiskCacheConfig(smallImageDiskCacheConfig)
    .build();
Fresco.initialize(context, config);

DraweeHierarchy

同一个 DraweeHierarchy 不能设置给多个 view, 也就是说 DraweeHierarchy 对象是我们不要去复用他

 GenericDraweeHierarchy draweeHierarchy = new GenericDraweeHierarchyBuilder(getResources())
                .setFadeDuration(300)
                .setPlaceholderImage(R.drawable.icon_place)
                .setFailureImage(R.drawable.icon_error)
                .setRetryImage(R.drawable.icon_retry)
                .build();

mSimpleDraweeView.setHierarchy(draweeHierarchy );

DraweeController

这个不多说了,API 很简单了,每什么特别需要说的点

ImageRequest imageRequest = ImageRequest.fromUri(uri);
imageRequest.setAutoRotateEnabled(true)

DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setUri(uri)
    .setImageRequest(request)
    .setAutoPlayAnimations(true)
    .setTapToRetryEnabled(true)
    .setOldController(mSimpleDraweeView.getController())
    .setControllerListener(listener)
    .build();

mSimpleDraweeView.setController(controller);

缩略图

Fresco 的缩略图支持比 Glide 要差不少,本质是请求2个图片地址

Uri lowResUri, highResUri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
    .setLowResImageRequest(ImageRequest.fromUri(lowResUri))
    .setImageRequest(ImageRequest.fromUri(highResUri))
    .setOldController(mSimpleDraweeView.getController())
    .build();
mSimpleDraweeView.setController(controller);

ImagePipeline 管道的直接使用

ImagePipeline 管道是 Fresco 的 核心,从数据的加载,换粗你的管理,到图片的变化处理都是由 ImagePipeline 来管理的。Fresco 支持我们直接获取 ImagePipeline 对象,那么我们就可以使用 ImagePipeline 来做很多事了。

  1. 判断一个资源是否在缓存中
// 获取管道对象
ImagePipeline imagePipeline = Fresco.getImagePipeline();
Uri uri = Uri.parse("");
// 判断是否在内存中有缓存
boolean inBitmapMemoryCache = imagePipeline.isInBitmapMemoryCache(uri);
可以判断内存,磁盘缓存

可以看到,不光内存缓存,我们还可以判断磁盘是不是缓存过了,而且可以接受多种类型的数据 - Uri ,ImageRequest ,而且考虑到访问磁盘是 IO 操作,提供了同步和异步方法。

  1. 删除某个 URI 的缓存
ImagePipeline imagePipeline = Fresco.getImagePipeline();
Uri uri;
imagePipeline.evictFromMemoryCache(uri);
imagePipeline.evictFromDiskCache(uri);

// combines above two lines
imagePipeline.evictFromCache(uri);

当然你要是清楚全部缓存也是可以的,但是官方不推荐这么做

ImagePipeline imagePipeline = Fresco.getImagePipeline();
imagePipeline.clearMemoryCaches();
imagePipeline.clearDiskCaches();

// combines above two lines
imagePipeline.clearCaches();
  1. 预加载

预加载这东西具体怎么用,在哪用就得看各位看官的需求了,我举个例子,漫画 app 这样的需要显示特大图片的,应该预加载下一页的内容。

预加载到磁盘缓存:

imagePipeline.prefetchToDiskCache(imageRequest, callerContext);

预加载到内存缓存:

imagePipeline.prefetchToBitmapCache(imageRequest, callerContext);

取消预加载:

DataSource<Void> prefetchDataSource = imagePipeline.prefetchTo...;
prefetchDataSource.close();
  1. 获取 bitmap 缓存对象
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, this);
        dataSource.subscribe(new BaseBitmapDataSubscriber() {

            @Override
            protected void onNewResultImpl(@Nullable Bitmap bitmap) {
                // 处理显示
            }

            @Override
            protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
                // 失败
            }
        }, UiThreadImmediateExecutorService.getInstance());

Fresco 并不能直接返回 Bitmap 对象,只能给我们一个数据源,让我们去注册一个观察者去处理 Bitmap 对象,Bitmap 对象的生命周期不能超出这个方法,这个方法结束后这个 Bitmap 对象就会被回收掉。

dataSource.subscribe 方法的第二个参数是 Fresco 的进程调度器:

UiThreadImmediateExecutorService.getInstance()
CallerThreadExecutor.getInstance();
  1. 获取 File 缓存对象
 Boolean result = imagePipeline.isInDiskCache(imageRequest).getResult();
        if (result) {
            CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(imageRequest, this);
            BinaryResource resource = ImagePipelineFactory.getInstance().getMainFileCache().getResource(cacheKey);
            File file = ((FileBinaryResource) resource).getFile();
        }
0.jpeg

Fresco 非侵入式设计


Fresco 要求我们使用独有的 SimpleDraweeView ,对于我们的工程来说,替换 imageview 为 SimpleDraweeView 是一个大工程。使用一个新的库,但是会大量修改原有的代码这就是侵入行,不能使用 imageview 对我们来说,很多时候都是非常不便的,SimpleDraweeView 毕竟不是 imageview 这种 google 原生的view 组件,一些 android 系统对原生图片组件的优化和功能,SimpleDraweeView 是无法支持的。那么有什么办法解决吗,目前还没有太好的方案,我看了很多文章,现在有3种思路:

Fresco 图片自适应显示


Fresco 的 SimpleDraweeView 不能使用 warpContent ,这是很让人沮丧的一点,在显示大图,长图时这是一个麻烦。目前有2种处理手法:

这2中方法还是主推第二种,服务器返回给我们目标图片的宽高,我们计算之后再去设置 imageview 的宽高,这样我们在 imageview 设置图片之前走一次 layout 比 imageview 设置图片之后再去走一次 layout ,在性能上要好不少,这是没有 bitmap 来参与,布局,绘制要快很多。这点大家使用淘宝时应该有体会,有的商品详情里面图很大,很长,这时淘宝会加载一个和目标图片登场的 item 进来。

手段一:加载图片获取宽高后,去修改 view 高度

给 simpleDraweeView 的 DraweeController 添加一个 ControllerListener 监听器。原文地址:Fresco进阶 图片错位 宽高自适应

/** 
    * 通过imageWidth 的宽度,自动适应高度 
    * * @param simpleDraweeView view 
    * * @param imagePath  Uri 
    * * @param imageWidth width 
    */  
   public static void setControllerListener(final SimpleDraweeView simpleDraweeView, String imagePath, final int imageWidth) {  
       final ViewGroup.LayoutParams layoutParams = simpleDraweeView.getLayoutParams();  
       ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {  
           @Override  
           public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable anim) {  
               if (imageInfo == null) {  
                   return;  
               }  
               int height = imageInfo.getHeight();  
               int width = imageInfo.getWidth();  
               layoutParams.width = imageWidth;  
               layoutParams.height = (int) ((float) (imageWidth * height) / (float) width);  
               simpleDraweeView.setLayoutParams(layoutParams);  
           }  
  
           @Override  
           public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {  
               Log.d("TAG", "Intermediate image received");  
           }  
  
           @Override  
           public void onFailure(String id, Throwable throwable) {  
               throwable.printStackTrace();  
           }  
       };  
       DraweeController controller = Fresco.newDraweeControllerBuilder().setControllerListener(controllerListener).setUri(Uri.parse(imagePath)).build();  
       simpleDraweeView.setController(controller);  
   }  

手段二:从服务器获取图片宽高,再修改 view 高度

Uri uri = "file:///mnt/sdcard/MyApp/myfile.jpg";
int width = 50, height = 50;
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
    .setResizeOptions(new ResizeOptions(width, height))
    .build();
PipelineDraweeController controller = Fresco.newDraweeControllerBuilder()
    .setOldController(mDraweeView.getController())
    .setImageRequest(request)
    .build();
mSimpleDraweeView.setController(controller);

推荐这种手段,现在各大图片云可以支持返回图片宽高,所以这种做法现在成本也不是很高了,而且还是 Fresco 官方推荐的做法。官方文档:wrap_content的限制

Fresco 官方文档截图

优化加载大图


这部分内容是承接上面 Fresco 图片自适应显示 来说的,上面的部分侧重如何确定加载大图时 view 的宽高,这部分侧重我们如何在加载大图时如何优化显示性能,减少内容消耗,这2部分其实是一块内容

首先最好的办法是我们先加载小兔,缩录图,在显示图片详情的单独页面时再去显示原始的大图。七牛云挺不错,在图片请求中可以带上需要的宽和高

但是要注意,不管服务器能不能返回缩略图,所存储的原图都不应该太大,有时图片太大,甚至都无法下载下来(报504之类的错误)

那么若是只能拿到原图或大图,fresco怎么优化显示,Fresco 默认提供了3种缩小图片的策略:

综上,要缩小内存占用,以及减少cpu计算量,减少卡顿,应该是Downsampling结合Resizing来使用.其中Downsampling是在Fresco初始化时开启,而Resizing则是通过构建ImageRequest时通过制定宽高来实现,所以可以定制每一张或每一类图片的宽高. 示例代码如下:


初始化 Fresco
设置图片加载请求数据

还有一种思路,就是学 Glide 呗,每次请求我们按照 view 的 size rezise 呗

public void setImageSrc(final SimpleDraweeView draweeView, Uri uri, int width, int height) {
    ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
    if(width > 0 && height > 0){
        builder.setResizeOptions(new ResizeOptions(width, height));
    }
    ImageRequest request = builder.build();
    PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
            .setImageRequest(request)
            .setTapToRetryEnabled(true)
            .setOldController(draweeView.getController())
            .build();
    draweeView.setController(controller);

高斯模糊


高斯模糊是app里设置一些背景效果 常用到的手段.在fresco中,可以通过postprocessor来实现,也可以自己拿到bitmap后将bitmap模糊化后设置到ImageView或SimpleDraweeView(这个不建议,会消除掉SimpleDraweeView的层级结构,变成单纯的ImageView)上.
推荐开源库: BlurPostprocessor

列表滚动时的优化


在列表视图滚动时,不加载图片,等滚动停止后再开始加载图片,提升列表视图的滚动流畅度。

    // 需要暂停网络请求时调用
    public static void pause(){
        Fresco.getImagePipeline().pause();
    }

    // 需要恢复网络请求时调用
    public static void resume(){
        Fresco.getImagePipeline().resume();
    }
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    super.onScrollStateChanged(recyclerView, newState);
    switch (newState) {
        case RecyclerView.SCROLL_STATE_IDLE://停止滑动
            if (Fresco.getImagePipeline().isPaused())
                Fresco.getImagePipeline().resume();
            break;
        case RecyclerView.SCROLL_STATE_DRAGGING:
            if (preScrollState == RecyclerView.SCROLL_STATE_SETTLING) {
                //触摸滑动不需要加载
                Fresco.getImagePipeline().pause();
            } else {
                //触摸滑动需要加载
                if (Fresco.getImagePipeline().isPaused())
                    Fresco.getImagePipeline().resume();
            }
            break;
        case RecyclerView.SCROLL_STATE_SETTLING://惯性滑动
            Fresco.getImagePipeline().pause();
            break;
    }
    preScrollState = newState;
}

自定义进度图

Fresco 支持自定义进度图,我们需要自己实现一个 Progress 的 Drawable ,核心就是 onLevelChange 这个方法,下面贴一个自定义的进度图

import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;

import com.facebook.drawee.drawable.DrawableUtils;

public class ImageLoadingDrawable extends Drawable {

    private Paint mRingBackgroundPaint;
    private int mRingBackgroundColor;
    // 画圆环的画笔
    private Paint mRingPaint;
    // 圆环颜色
    private int mRingColor;
    // 半径
    private float mRadius;
    // 圆环半径
    private float mRingRadius;
    // 圆环宽度
    private float mStrokeWidth;
    // 圆心x坐标
    private int mXCenter;
    // 圆心y坐标
    private int mYCenter;
    // 总进度
    private int mTotalProgress = 10000;
    // 当前进度
    private int mProgress;

    public ImageLoadingDrawable(){
        initAttrs();
    }

    private void initAttrs() {
        mRadius = 160;
        mStrokeWidth = 4;
        mRingBackgroundColor = 0xFFadadad;
        mRingColor = 0xFF0EB6D2;
        mRingRadius = mRadius + mStrokeWidth / 2;
        initVariable();
    }

    private void initVariable() {
        mRingBackgroundPaint = new Paint();
        mRingBackgroundPaint.setAntiAlias(true);
        mRingBackgroundPaint.setColor(mRingBackgroundColor);
        mRingBackgroundPaint.setStyle(Paint.Style.STROKE);
        mRingBackgroundPaint.setStrokeWidth(mStrokeWidth);

        mRingPaint = new Paint();
        mRingPaint.setAntiAlias(true);
        mRingPaint.setColor(mRingColor);
        mRingPaint.setStyle(Paint.Style.STROKE);
        mRingPaint.setStrokeWidth(mStrokeWidth);
    }

    @Override
    public void draw(Canvas canvas) {
        drawBar(canvas,mTotalProgress,mRingBackgroundPaint);
        drawBar(canvas,mProgress,mRingPaint);
    }

    private void drawBar(Canvas canvas, int level, Paint paint) {
        if (level > 0 ) {
            Rect bound= getBounds();
            mXCenter = bound.centerX();
            mYCenter = bound.centerY();
            RectF oval = new RectF();
            oval.left = (mXCenter - mRingRadius);
            oval.top = (mYCenter - mRingRadius);
            oval.right = mRingRadius * 2 + (mXCenter - mRingRadius);
            oval.bottom = mRingRadius * 2 + (mYCenter - mRingRadius);
            canvas.drawArc(oval, -90, ((float) level / mTotalProgress) * 360, false, paint); //
        }
    }

    @Override
    protected boolean onLevelChange(int level) {
        mProgress = level;
        if(level > 0 && level < 10000) {
            invalidateSelf();
            return true;
        }else {
            return false;
        }
    }

    @Override
    public void setAlpha(int alpha) {
        mRingPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        mRingPaint.setColorFilter(cf);
    }

    @Override
    public int getOpacity() {
        return DrawableUtils.getOpacityFromColor(this.mRingPaint.getColor());
    }
}

使用的话就是设置进 DraweetHierarchy 图层管理器中

image.getHierarchy().setProgressBarImage(new ImageLoadingDrawable());

Fresco 开源扩展库


PhotoDraweeView 手势库说明


这个库有一点得说一下,demo 里的 PhotoDraweeView 宽高给 match 之后就可以了,但我写的时候,发现必须在图片加载之后把图片的宽高更新近去 PhotoDraweeView 才能正常支持手势

       String uri = "res:///" + R.drawable.panda1;

        PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
        builder.setUri(uri);
        builder.setOldController(photoDraweeView.getController());
        builder.setControllerListener(new BaseControllerListener<ImageInfo>() {
            @Override
            public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable animatable) {
                photoDraweeView.update(imageInfo.getWidth(), imageInfo.getHeight());
            }
        });

        photoDraweeView.setController(builder.build());

后来看别人的实现,发现 PhotoDraweeView 有专门设置资源的方法,使用 Fresco 的 control 不好用了。

  photoDraweeView.setPhotoUri(Uri.parse(uri));

subsampling-scale-image-view 超长图加载库


这个库我每试出来库描述的那种按页加载的情况,不过看了 heap 堆内存的使用量,的确是要少不少的,加载大图的确比较合适。我测试的时候,1080*11776 的图是一起出来的,我下滑的时候不会再去请求数据了,只是使用 子编码优化超大图的内存占用量,大概可以优化到原图的60%。这个控件有一点不好的是只能加载 file 文件,需要我们自己去监听一下下载完成后,手动获取 disk 磁盘缓存

public File getFile(Uri uri, ImagePipeline imagePipeline) {
        ImageRequest imageRequest = ImageRequest.fromUri(uri);
        CacheKey encodedCacheKey = imagePipeline.getCacheKeyFactory().getEncodedCacheKey(imageRequest, this);

        BinaryResource resource = ImagePipelineFactory.getInstance()
                .getMainFileCache().getResource(encodedCacheKey);
        return ((FileBinaryResource) resource).getFile();
    }

        ImagePipeline pipeline = Fresco.getImagePipeline();

        if (pipeline.isInDiskCacheSync(uri)) {
            photoDraweeView.setImage(ImageSource.uri(getFile(uri, pipeline).getAbsolutePath()));
        } else {
            ImageRequest imageRequest = ImageRequest.fromUri(uri);
            DataSource<CloseableReference<CloseableImage>> dataSource = pipeline.fetchDecodedImage(imageRequest, this);
            dataSource.subscribe(new BaseBitmapDataSubscriber() {
                @Override
                protected void onNewResultImpl(@Nullable Bitmap bitmap) {
                    if (pipeline.isInDiskCacheSync(uri)) {
                        photoDraweeView.setImage(ImageSource.uri(getFile(uri, pipeline).getAbsolutePath()));
                    }
                }

                @Override
                protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {

                }
            }, UiThreadImmediateExecutorService.getInstance());
        }
上一篇下一篇

猜你喜欢

热点阅读