安卓开发框架(MVP+主流框架+基类+工具类)--- Fresco

本文详细介绍Facebook出品的Fresco图片加载库,涵盖配置、SimpleDraweeView使用、加载图片流程及缓存策略等内容。

 转载自:http://blog.youkuaiyun.com/ljy_programmer/article/details/78273267

学习/参考地址: 
https://www.fresco-cn.org/docs/index.html 
http://blog.youkuaiyun.com/wyb112233/article/details/49637685 
http://blog.youkuaiyun.com/android_ls/article/details/53137867

前言

Fresco是一个出自Facebook的功能强大的图片加载库

优点: 
1)内存自动回收。图片不可见时,会及时自动释放所占用的内存,尽可能地避免OOM 
2)三级缓存机制。两级内存缓存(解码的与未解码的)+一级磁盘缓存,提示加载速度,节省内存占用空间 
3)支持各种加载场景。如动图加载、高斯模糊、渐进式加载、先加载小图再加载大图,以及其它常见的图片加载场景

缺点: 
1)体积较大。较其他主流图片库体积要大一些 
2)侵入性较强。须使用它提供的SimpleDraweeView来代替ImageView加载显示图片

综合来说肯定是利大大大于弊,尤其是当你的应用需要加载很多图片时,更能凸显其价值。


介绍

下面通过 配置、SimpleDraweeView、加载图片、混淆、其他 这几个部分来介绍。

1. 配置

1.1 添加依赖

compile 'com.facebook.fresco:fresco:1.5.0'
compile 'com.facebook.fresco:animated-gif:1.5.0'//加载gif动图需添加此库
compile 'com.facebook.fresco:animated-webp:1.5.0'//加载webp动图需添加此库
compile 'com.facebook.fresco:webpsupport:1.5.0'//支持webp需添加此库
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.5.0'//网络实现层使用okhttp3需添加此库
 
  • 1
  • 2
  • 3
  • 4
  • 5

1.2 设置磁盘缓存

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setMainDiskCacheConfig(DiskCacheConfig.newBuilder(context)
.setBaseDirectoryPath(context.getExternalCacheDir())//设置磁盘缓存的路径
.setBaseDirectoryName(BaseConstants.APP_IMAGE)//设置磁盘缓存文件夹的名称
.setMaxCacheSize(MAX_DISK_CACHE_SIZE)//设置磁盘缓存的大小
.build());
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.3 设置内存缓存

设置已解码的内存缓存(Bitmap缓存)

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setBitmapMemoryCacheParamsSupplier(new Supplier<MemoryCacheParams>() {
       public MemoryCacheParams get() {
           int MAX_HEAP_SIZE = (int) Runtime.getRuntime().maxMemory();
           int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 5;//取手机内存最大值的五分之一作为可用的最大内存数

           MemoryCacheParams bitmapCacheParams = new MemoryCacheParams( //
                   // 可用最大内存数,以字节为单位
                   MAX_MEMORY_CACHE_SIZE,
                   // 内存中允许的最多图片数量
                   Integer.MAX_VALUE,
                   // 内存中准备清理但是尚未删除的总图片所可用的最大内存数,以字节为单位
                   MAX_MEMORY_CACHE_SIZE,
                   // 内存中准备清除的图片最大数量
                   Integer.MAX_VALUE,
                   // 内存中单图片的最大大小
                   Integer.MAX_VALUE);
           return bitmapCacheParams;
       }
   });
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

设置未解码的内存缓存

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setEncodedMemoryCacheParamsSupplier(new Supplier<MemoryCacheParams>() {
       public MemoryCacheParams get() {

           MemoryCacheParams bitmapCacheParams;
           //设置大小,可参考上面已解码的内存缓存
           return bitmapCacheParams;
       }
   });
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1.4 设置内存紧张时的应对措施

MemoryTrimmableRegistry memoryTrimmableRegistry = NoOpMemoryTrimmableRegistry.getInstance();
memoryTrimmableRegistry.registerMemoryTrimmable(new MemoryTrimmable() {
     @Override
     public void trim(MemoryTrimType trimType) {
         final double suggestedTrimRatio = trimType.getSuggestedTrimRatio();

         if (MemoryTrimType.OnCloseToDalvikHeapLimit.getSuggestedTrimRatio() == suggestedTrimRatio 
         || MemoryTrimType.OnSystemLowMemoryWhileAppInBackground.getSuggestedTrimRatio() == suggestedTrimRatio 
         || MemoryTrimType.OnSystemLowMemoryWhileAppInForeground.getSuggestedTrimRatio() == suggestedTrimRatio) { 
                 //清空内存缓存
                 ImagePipelineFactory.getInstance().getImagePipeline().clearMemoryCaches();
         }
     }
 });

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setMemoryTrimmableRegistry(memoryTrimmableRegistry);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

1.5 设置渐进式显示的效果

ProgressiveJpegConfig progressiveJpegConfig = 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);
      }
};
//具体含义可参考 http://wiki.jikexueyuan.com/project/fresco/progressive-jpegs.html

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setProgressiveJpegConfig(progressiveJpegConfig);
//或者使用默认的效果
//imagePipelineConfigBuilder.setProgressiveJpegConfig(new SimpleProgressiveJpegConfig());
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

设置完效果后,还需在下面介绍的ImageRequest中开启渐进式加载。

1.6 允许解码时调整图片大小

允许后,即可在后面介绍的ImageRequest中对结合ResizeOptions对解码后的图片大小进行调整,从而优化了图片所占大小。默认只支持JPEG图,所以要设置该属性来支持png、jpg、webp。

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setDownsampleEnabled(true);
 
  • 1
  • 2

1.7 开启Log

FLog.setMinimumLoggingLevel(FLog.VERBOSE);
Set<RequestListener> requestListeners = new HashSet<>();
requestListeners.add(new RequestLoggingListener());

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setRequestListeners(requestListeners);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.8 初始化

上面的各种配置都是通过ImagePipelineConfig进行的,接着需要进行初始化,在Application中初始化即可

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);

//...进行各种设置

ImagePipelineConfig config = imagePipelineConfigBuilder.build();
Fresco.initialize(context, config);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果想直接使用默认的配置,可以

Fresco.initialize(context);
 
  • 1

2. SimpleDraweeView

Fresco要求使用SimpleDraweeView来替换ImageView进行图片的加载与显示,不少人也是因为这一点而不想使用Fresco。 
下面介绍SimpleDraweeView在xml中的各种属性

//在最外层布局的属性中加入xmlns:fresco="http://schemas.android.com/apk/res-auto"

<com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv"
        android:layout_width="150dp"
        android:layout_height="150dp"
        fresco:actualImageScaleType="centerCrop"
        fresco:fadeDuration="2000"
        fresco:failureImage="@mipmap/ic_launcher"
        fresco:failureImageScaleType="centerCrop"
        fresco:placeholderImage="@mipmap/ic_launcher"
        fresco:placeholderImageScaleType="centerCrop"
        fresco:progressBarAutoRotateInterval="1500"
        fresco:progressBarImage="@drawable/rotate"
        fresco:progressBarImageScaleType="centerCrop"
        fresco:retryImage="@mipmap/ic_launcher"
        fresco:retryImageScaleType="centerCrop"
        fresco:backgroundImage="@mipmap/ic_launcher"
        fresco:overlayImage="@mipmap/ic_launcher"
        fresco:pressedStateOverlayImage="@mipmap/ic_launcher"
        fresco:roundAsCircle="false"
        fresco:roundedCornerRadius="7dp"
        fresco:roundTopLeft="true"
        fresco:roundTopRight="false"
        fresco:roundBottomLeft="false"
        fresco:roundBottomRight="true"
        fresco:roundWithOverlayColor="@color/colorAccent"
        fresco:roundingBorderWidth="2dp"
        fresco:roundingBorderColor="@color/colorPrimary"
        fresco:viewAspectRatio="1"/>

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

上面各个属性的作用说明:(来自http://www.jianshu.com/p/8ff81be83101

属性 作用说明
actualImageScaleType加载完成的图片的缩放样式
fadeDuration由进度条和占位符图片渐变过渡到加载完成的图片所使用的时间间隔
failureImage加载失败所使用的图片
failureImageScaleType加载失败所使用的图片的缩放样式
placeholderImage占位符图片
placeholderImageScaleType占位符图片的缩放样式
progressBarAutoRotateInterval旋转进度条旋转1圈所需要的时间
progressBarImage旋转进度条所使用的图片
progressBarImageScaleType旋转进度条所使用的图片的缩放样式
retryImage重试所使用的图片
retryImageScaleType重试所使用的图片的缩放样式
backgroundImage背景图片
overlayImage覆盖在加载完成后图片上的叠加图片
pressedStateOverlayImage按压状态下的叠加图片
roundAsCircle是否将图片剪切为圆形
roundedCornerRadius圆角图片时候,圆角的半径大小
roundTopLeft左上角是否为圆角
roundTopRight右上角是否为圆角
roundBottomLeft左下角是否为圆角
roundBottomRight右下角是否为圆角
roundWithOverlayColor圆角或圆形图叠加的颜色,只能是颜色
roundingBorderWidth圆角或圆形图边框的宽度
roundingBorderColor圆角或圆形图边框的颜色
viewAspectRatio设置宽高比

各个属性的效果图,可到http://blog.youkuaiyun.com/wyb112233/article/details/49637685查看,我就不重复造轮子了。 
*注意: 
1)android:src属性对于SimpleDraweeView无效,必要的话可用fresco:placeholderImage来设置。 
2)SimpleDraweeView不支持android:layout_width和android:layout_height同时都设为wrap_content。

3. 加载图片

使用Fresco加载图片,大致是按以下流程进行的。 
1. 设置Hierarchay(上面xml中的属性,可在这进行设置) 
2. 构建ImageRequest(加载路径、开启渐进式加载、高斯模糊、调整解码图片大小等,可在这进行设置) 
3. 构建DraweeController(动图加载、失败后点击重新加载等,可在这进行设置) 
4. 进行图片加载

3.1 设置Hierarchay

虽然xml中的属性都能在这一步通过代码进行设置,但一般只在这设置一些统一固定的属性,比如加载占位图、加载失败图等。

Resources res = MyApplication.getInstance().getResources();
Drawable retryImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null);
Drawable failureImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null);
Drawable placeholderImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null);

//对Hierarchy进行设置,如各种状态下显示的图片
public void setHierarchay(GenericDraweeHierarchy hierarchy) {
    if (hierarchy != null) {
        //重新加载显示的图片
        hierarchy.setRetryImage(retryImage); 
        //加载失败显示的图片
        hierarchy.setFailureImage(failureImage, ScalingUtils.ScaleType.CENTER_CROP); 
        //加载完成前显示的占位图
        hierarchy.setPlaceholderImage(placeholderImage, ScalingUtils.ScaleType.CENTER_CROP);
        //设置加载成功后图片的缩放模式
        hierarchy.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP);

        //其他设置请查看具体API。

    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

3.2 构建ImageRequest

/**
 * 构建、获取ImageRequest
 * @param uri 加载路径
 * @param progressiveRenderingEnabled 是否开启渐进式加载
 * @param blurEnable 是否开启高斯模糊
 * @param simpleDraweeView 加载的图片控件
 * @return ImageRequest
 */
public ImageRequest getImageRequest(Uri uri, boolean progressiveRenderingEnabled, boolean blurEnable, SimpleDraweeView simpleDraweeView) {

    int width;
    int height;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        width = simpleDraweeView.getWidth();
        height = simpleDraweeView.getHeight();
    } else {
        width = simpleDraweeView.getMaxWidth();
        height = simpleDraweeView.getMaxHeight();
    }

    //根据请求路径生成ImageRequest的构造者
    ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
    //调整解码图片的大小
    if (width > 0 && height > 0) {
        builder.setResizeOptions(new ResizeOptions(width, height));
    }
    //是否开启渐进式加载,仅支持JPEG图片
    builder.setProgressiveRenderingEnabled(progressiveRenderingEnabled);
    //是否开启高斯模糊效果
    if (blurEnable) {
        builder.setPostprocessor(new BasePostprocessor() {
            @Override
            public String getName() {
                return "blurPostprocessor";
            }

            @Override
            public void process(Bitmap bitmap) {
                //第二个参数值控制模糊程度,越大越模糊
                BitmapBlurHelper.blur(bitmap, 15);
            }
        });
    }
    return builder.build();
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

3.3 构建DraweeController

/**
* 构建、获取Controller
* @param request
* @param oldController
* @return
*/
public DraweeController getController(ImageRequest request, @Nullable DraweeController oldController) {

   PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
   builder.setImageRequest(request);//设置图片请求
   builder.setTapToRetryEnabled(false);//设置是否允许加载失败时点击再次加载
   builder.setAutoPlayAnimations(true);//设置是否允许动画图自动播放
   builder.setOldController(oldController);
   return builder.build();
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.4 进行图片加载

创建一个loadImage方法将上面的Hierarchy、ImageRequest、DraweeController串在一起,供具体的加载场景使用

/**
* 加载图片核心方法
* 
* @param simpleDraweeView              图片加载控件
* @param uri                           图片加载地址
* @param progressiveRenderingEnabled   是否开启渐进式加载
* @param blurEnable                    是否开启高斯模糊效果
*/
public void loadImage(SimpleDraweeView simpleDraweeView, Uri uri, boolean progressiveRenderingEnabled, boolean blurEnable) {
  //设置Hierarchy
   setHierarchay(simpleDraweeView.getHierarchy());
   //构建并获取ImageRequest
   ImageRequest imageRequest = getImageRequest(uri, progressiveRenderingEnabled, blurEnable, simpleDraweeView);
   //构建并获取Controller
   DraweeController draweeController = getController(imageRequest, simpleDraweeView.getController());
   //开始加载
   simpleDraweeView.setController(draweeController);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

具体的加载场景: 
- 加载网络图片,包括gif/webp动图

 public void loadNetImage(SimpleDraweeView simpleDraweeView, String url) {
     Uri uri = Uri.parse(url);
     loadImage(simpleDraweeView, uri, false, false);
 }
 
  • 1
  • 2
  • 3
  • 4
  • 加载本地文件图片
public void loadLocalImage(SimpleDraweeView simpleDraweeView, String fileName) {
    Uri uri = Uri.parse("file://" + fileName);
    loadImage(simpleDraweeView, uri, false, false);
}
 
  • 1
  • 2
  • 3
  • 4
  • 加载res下资源图片
public void loadResourceImage(SimpleDraweeView simpleDraweeView, @DrawableRes int resId) {
    Uri uri = Uri.parse("res:///" + resId);
    loadImage(simpleDraweeView, uri, false, false);
}
 
  • 1
  • 2
  • 3
  • 4
  • 加载ContentProvider下的图片
public void loadContentProviderImage(SimpleDraweeView simpleDraweeView, int resId) {
    Uri uri = Uri.parse("content:///" + resId);
    loadImage(simpleDraweeView, uri, false, false);
}
 
  • 1
  • 2
  • 3
  • 4
  • 加载asset下的图片
public void loadAssetImage(SimpleDraweeView simpleDraweeView, int resId) {
    Uri uri = Uri.parse("asset:///" + resId);
    loadImage(simpleDraweeView, uri, false, false);
}
 
  • 1
  • 2
  • 3
  • 4
  • 加载网络图片,先加载小图,待大图加载完成后再替换掉小图

这个需要修改一下DraweeController的构建,通过setLowResImageRequest来添加小图请求

public DraweeController getSmallToBigController(ImageRequest smallRequest, ImageRequest bigRequest, @Nullable DraweeController oldController) {

    PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
    builder.setLowResImageRequest(smallRequest);//小图的图片请求
    builder.setImageRequest(bigRequest);//大图的图片请求
    builder.setTapToRetryEnabled(false);//设置是否允许加载失败时点击再次加载
    builder.setAutoPlayAnimations(true);//设置是否允许动画图自动播放
    builder.setOldController(oldController);
    return builder.build();
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
public void loadImageSmallToBig(SimpleDraweeView simpleDraweeView, Uri smallUri, Uri bigUri) {
    //设置Hierarchy
    setHierarchay(simpleDraweeView.getHierarchy());
    //构建小图的图片请求
    ImageRequest smallRequest = getImageRequest(smallUri, false, false, simpleDraweeView);
    //构建大图的图片请求
    ImageRequest bigRequest = getImageRequest(bigUri, false, false, simpleDraweeView);
    //构建Controller
    DraweeController draweeController = getSmallToBigController(smallRequest, bigRequest, simpleDraweeView.getController());
    //开始加载
    simpleDraweeView.setController(draweeController);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
//加载网络图片,先加载小图,待大图加载完成后替换
public void loadNetImageSmallToBig(SimpleDraweeView simpleDraweeView, String smallUrl, String bigUrl) {
    Uri smallUri = Uri.parse(smallUrl);
    Uri bigUri = Uri.parse(bigUrl);
    loadImageSmallToBig(simpleDraweeView, smallUri, bigUri);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4. 混淆

在proguard-rules.pro文件中添加以下内容进行混淆配置

#fresco开始
-keep class com.facebook.fresco.** { *; }
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
     @com.facebook.common.internal.DoNotStrip *;
}
-keep class com.facebook.imagepipeline.gif.** { *; }
-keep class com.facebook.imagepipeline.webp.* { *; }
-keepclassmembers class * {
    native <methods>;
}
-dontwarn okio.**
-dontwarn com.squareup.okhttp.**
-dontwarn okhttp3.**
-dontwarn javax.annotation.**
-dontwarn com.android.volley.toolbox.**
-keep class com.facebook.imagepipeline.animated.factory.AnimatedFactoryImpl {
    public AnimatedFactoryImpl(com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory,com.facebook.imagepipeline.core.ExecutorSupplier);
}
#fresco结束
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5. 其他

5.1 缓存策略

Fresco采用三级缓存机制,两级内存缓存+一级磁盘缓存,其中两级内存缓存分为已解码的图片缓存(Bitmap缓存)和未解码的图片缓存。 
下面通过加载流程来了解其缓存策略。 
1. 根据Uri到已解码的图片缓存中查找是否存在对应的Bitmap。如果存在,则返回Bitmap显示; 
如果不存在,则到未解码的图片缓存中查找。 
2. 如果在未解码的图片缓存中存在对应的数据,则解码,返回Bitmap显示并将其加入到已解码的图片缓存中;如果不存在,则到磁盘缓存中查找。 
3. 如果在磁盘缓存中存在对应的数据,则将数据加入到未解码的图片缓存中,然后解码,返回Bitmap显示并将其加入到已解码的图片缓存中;如果不存在,则进行网络请求或者到本地文件加载。 
4. 请求或加载成功后,将数据加入到磁盘缓存和未解码的图片缓存中,然后解码,返回Bitmap显示并将其加入到已解码的图片缓存中。 

简单整了个示意图,帮助理解下: 
缓存策略示意图

5.2 兼容共享动画

android5.0之后加入了共享动画, 
相关的学习地址: 
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0201/2394.html 
http://jcodecraeer.com/a/opensource/2015/0113/2311.html?1422794518

如果直接结合Fresco和共享动画来实现页面的过渡效果,会发现无效或异常。 
Fresco官方也给出了说明,https://www.fresco-cn.org/docs/shared-transitions.html

兼容方法: 
1.重写共享动画转换效果的xml文件,注释掉changeImageTransform,并将该文件放于res/transition文件夹下

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode/>
    <changeBounds/>
    <changeTransform/>
    <changeClipBounds/>
    <!--<changeImageTransform/>-->
    <!-- Fresco图片框架不支持changeImageTransform变换,
    默认情况是这五个变换都使用,所以需要重写xml并注释掉changeImageTransform -->
</transitionSet>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.在style文件中使用上一步重写的xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
        <!-- 允许使用transitions -->
        <item name="android:windowContentTransitions">true</item>
        <!-- 指定shared element transitions -->
        <item name="android:windowSharedElementEnterTransition">
        @transition/share_element_transition</item>
        <item name="android:windowSharedElementExitTransition">
        @transition/share_element_transition</item>
    </style>
</resources>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5.3 浏览大图

“点击小图浏览大图,并且大图支持缩放。” 
这种需求经常能见到,上面提到的SimpleDraweeView并不支持缩放等功能,所以需要重新定制一个控件来显示。 
官方给出了一个ZoomableDraweeView来支持该场景,另外也可以参考PhotoDraweeView

5.4 获取网络请求回来的Bitmap

有时候,我们需要拿到网络请求回来的Bitmap对象,那么我们可以这么做:

//加载图片,在FrescoBitmapCallback里获取返回的Bitmap
public final void loadImageBitmap(String url, FrescoBitmapCallback<Bitmap> callback) {
    if (TextUtils.isEmpty(url)) {
        return;
    }
    try {
        fetch(Uri.parse(url), callback);
    } catch (Exception e) {
        //oom风险.
        e.printStackTrace();
        callback.onFailure(Uri.parse(url), e);
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
private void fetch(final Uri uri, final FrescoBitmapCallback<Bitmap> callback) throws Exception {

    ImageRequestBuilder requestBuilder = ImageRequestBuilder.newBuilderWithSource(uri);
    ImageRequest imageRequest = requestBuilder.build();

    DataSource<CloseableReference<CloseableImage>> dataSource = 
    ImagePipelineFactory.getInstance().getImagePipeline().fetchDecodedImage(imageRequest, null);

    dataSource.subscribe(new BaseBitmapDataSubscriber() {
        @Override
        public void onNewResultImpl(@Nullable final Bitmap bitmap) {
            if (callback == null) return;
            if (bitmap != null && !bitmap.isRecycled()) {
                //后台线程中加载
                Executors.newSingleThreadExecutor().submit(new Callable<Bitmap>() {
                    @Override
                    public Bitmap call() throws Exception {
                        final Bitmap resultBitmap = bitmap.copy(bitmap.getConfig(), bitmap.isMutable());
                        if (resultBitmap != null && !resultBitmap.isRecycled()) {
                            //回调UI线程中去
                            new Handler(Looper.getMainLooper()).post(new Runnable() {
                                @Override
                                public void run() {
                                    callback.onSuccess(uri, resultBitmap);
                                }
                            });
                        }
                        return resultBitmap;
                    }
                });
            }
        }

        @Override
        public void onCancellation(DataSource<CloseableReference<CloseableImage>> dataSource) {
            super.onCancellation(dataSource);
            if (callback == null) return;
            callback.onCancel(uri);
        }

        @Override
        public void onFailureImpl(DataSource dataSource) {
            if (callback == null) return;
            Throwable throwable = null;
            if (dataSource != null) {
                throwable = dataSource.getFailureCause();
            }
            callback.onFailure(uri, throwable);
        }

    }, UiThreadImmediateExecutorService.getInstance());
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
public interface FrescoBitmapCallback<T> {

   void onSuccess(Uri uri, T result);

    void onFailure(Uri uri, Throwable throwable);

    void onCancel(Uri uri);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

使用

demo中已将前面介绍的配置、加载图片的方法等都封装到FrescoUtil类中。大家可以根据自己的需求到FrescoUitl中进行修改定制。 
1.在Application中初始化

//初始化Fresco
FrescoUtil.getInstance().initializeFresco(this);
 
  • 1
  • 2

2.在需要加载图片的地方调用相关方法

//加载网络图片
FrescoUtil.getInstance().loadNetImage(simpleDraweeView, imageUrl);
//加载res下的图片
FrescoUtil.getInstance().loadResourceImage(simpleDraweeView, resId);
....
 
  • 1
  • 2
  • 3
  • 4
  • 5

详细代码请看demo,地址https://github.com/LJYcoder/DevBase 
demo的内容流程,请看《安卓开发框架(MVP+主流框架+基类+工具类)— 开篇》



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值