android 图像加载 第三方开源 之 - Fresco (2)

这几天研究了Fresco的用法,如果仅仅是加载图片的话,访问第一篇即可,本篇整理了其更多的知识点。

一.  Fresco的架构的组成

1.  DraweeView     用来对图片的显示,就是一个ImageView,可在XML中直接使用,可设置属性

       --  SimpleDraweeView

2.  DraweeHierarchy   --  对图片属性的设置   上篇也讲到其使用方法    访问第一篇即可

       --  GenericDraweeHierarchy         通过GenericDraweeHierarchyBuilder.build获得

3.  DraweeController    --   对图片更多的控制,比如设置点击重试是否开启,设置图片下载的监听,设置URI

       --   PipelineDraweeController       通过DraweeControllerBuilder.build() 获得  使用方式如下:

Uri uri = Uri.parse(url);
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri).build();
PipelineDraweeController draweeController = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
    .setImageRequest(request).setAutoPlayAnimations(true).setTapToRetryEnabled(true).build();
mDraweeView.setController(draweeController);

 4.  Listeners

       --   ControllerListener    可监听图片的下载 使用方式如下:

ControllerListener controllerListener = new BaseControllerListener<ImageInfo>(){
    @Override
    public void onSubmit(String id, Object callerContext) {
        super.onSubmit(id, callerContext);
    }

    @Override
    public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {
        super.onFinalImageSet(id, imageInfo, animatable);
        Log.d("stormxz", "onFinalImageSet  成功");
    }

    @Override
    public void onIntermediateImageSet(String id, ImageInfo imageInfo) {
        super.onIntermediateImageSet(id, imageInfo);
    }

    @Override
    public void onIntermediateImageFailed(String id, Throwable throwable) {
        super.onIntermediateImageFailed(id, throwable);
    }

    @Override
    public void onFailure(String id, Throwable throwable) {
        super.onFailure(id, throwable);
        Log.d("stormxz", "onFailure  失败");
    }

    @Override
    public void onRelease(String id) {
        super.onRelease(id);
    }
};

    DraweeController draweeController = (DraweeController) Fresco.newDraweeControllerBuilder()
        .setUri(Uri.parse("http://img3.redocn.com/tupian/20150312/haixinghezhenzhubeikeshiliangbeijing_3937174.jpg")) //需要加载图片的uri
        .setTapToRetryEnabled(true)   //设置点击重试是否开启
        .setControllerListener(controllerListener)  //设置监听
        .setOldController(simpleDraweeView.getController()).build();
    simpleDraweeView.setController(draweeController);
所有的图片加载,onFinalImageSet 或者 onFailure 都会被触发。前者在成功时,后者在失败时。


二.  支持的URI

特别注意:Fresco 不支持 相对路径的URI. 所有的 URI 都必须是绝对路径,并且带上该 URI 的 scheme。



注意,只有图片资源才能使用在Image pipeline中,比如(PNG)。其他资源类型,比如字符串,或者XML Drawable在Image pipeline中没有意义。所以加载的资源不支持这些类型。
像ShapeDrawable这样声明在XML中的drawable可能引起困惑。注意到这毕竟不是图片。如果想把这样的drawable作为图像显示,那么把这个drawable设置为占位图,然后把URI设置为null。

上面官网的话,简单点说就是,1. URI中需传入真实图片的路径   2. 想直接使用Drawable,使用SimpleDraweeView中占位符图片属placeholderImage


三.  ImageRequest

1. ImageDecodeOptions    图片处理操作

ImageDecodeOptions decodeOptions = ImageDecodeOptions.newBuilder()
        .setBitmapConfig()             //配置bitmapConfig
        .setUseLastFrameForPreview()  //是否使用最后一帧作为显示
        .setCustomImageDecoder()
        .setDecodePreviewFrame()
        .build();

2.  ImageRequest 的使用

ImageRequest request = ImageRequestBuilder
        .newBuilderWithSource(uri)         //从那边访问
        .setImageDecodeOptions(decodeOptions)   //图片操作
        .setAutoRotateEnabled(true)        //是否支持旋转
        .setLocalThumbnailPreviewsEnabled(true)   //设置缩略图
        .setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)     //设置最低请求级别,与Image pipeine相关
        .setProgressiveRenderingEnabled(false)     //是否支持渐进式加载
        .setResizeOptions(new ResizeOptions(width, height))     //缩放,旋转
        .setPostprocessor()        //后处理器
        .build();

最后将ImageRequest设置到DraweeController中

ps: 必选项,uri,其他为可选项


四. 后处理器PostProcessor 

当网络或本地访问图片后,可对图片Bitmap进行修改操作,这是就要用到PostProcessor

1.  参数为一个时

采用官网demo 给图片加上红色网格

Postprocessor redMeshPostprocessor = new BasePostprocessor() {
    @Override
    public String getName() {
        return "redMeshPostprocessor";
    }

    @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.RED);
            }
        }
    }
};

将对象redMeshPostProcessor 设置到ImageRuest中

效果如下:

                                            


ps: 图片在进入后处理器(postprocessor)的图片是原图的一个完整拷贝,原来的图片不受修改的影响。在5.0以前的机器上,拷贝后的图片也在native内存中。
      在开始一个图片显示时,即使是反复显示同一个图片,在每次进行显示时,都需要指定后处理器。对于同一个图片,每次显示可以使用不同的后处理器。
      后处理器现在不支持 动图加载 。

2.  参数为2个时

public void process(Bitmap destBitmap, Bitmap sourceBitmap) {
    for (int x = 0; x < destBitmap.getWidth(); x++) {
        for (int y = 0; y < destBitmap.getHeight(); y++) {
            destBitmap.setPixel(destBitmap.getWidth() - x, y, sourceBitmap.getPixel(x, y));
        }
    }
}
ps:源图片和目标图片具有相同的大小。
     不要修改源图片。在未来的版本中这会抛出一个异常。
     不要保存对任何一个图片的引用。它们的内存会由 image pipeline 进行管理,目标图片会在 Drawww 或 DataSource 中正常地销毁。

3.  其他process

如果处理后的图片大小需要和原图片不同,我们有第三个 process 方法。你可以使用 PlatformBitmapFactory 类以指定的大小安全地创建一张图片,在 Java Heap 之外。
下面的例子将源图片复制为 1 / 4 大小
@Override
public CloseableReference<Bitmap> process(
        Bitmap sourceBitmap,
        PlatformBitmapFactory bitmapFactory) {
    CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap(
            sourceBitmap.getWidth() / 2,
            sourceBitmap.getHeight() / 2);
    try {
        Bitmap destBitmap = bitmapRef.get();
        for (int x = 0; x < destBitmap.getWidth(); x+=2) {
            for (int y = 0; y < destBitmap.getHeight(); y+=2) {
                destBitmap.setPixel(x, y, sourceBitmap.getPixel(x, y));
            }
        }
        return CloseableReference.cloneOrNull(bitmapRef);
    } finally {
        CloseableReference.closeSafely(bitmapRef);
    }
}
ps: 
你必须遵循 closeable references 的规则。
不要使用 Android 中 Bitmap.createBitmap() 方法,它会在 Java 堆内存中产生一个 bitmap 对象。

4.  处理重复的后处理  

通过继承BaseepeatedPostprocessor ,其中有update()方法可以再次触发process处理
        public class MeshPostprocessor extends BaseRepeatedPostprocessor {
            private int mColor = Color.TRANSPARENT;

            public void setColor(int color) {
                mColor = color;
                update();
            }

            @Override
            public String getName() {
                return "meshPostprocessor";
            }

            @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, mColor);
                    }
                }
            }
        }

        MeshPostprocessor meshPostprocessor = new MeshPostprocessor();

        meshPostprocessor.setColor(Color.RED);
        meshPostprocessor.setColor(Color.BLUE);
so 每个 image request, 应该只有一个Postprocessor,但是这个后处理器是可控制的。

注意:不要写多于一个process,否则会出现意想不到的结果

五.  缩放

1. 基本使用

实际图片,占位图,重试图和失败图都可以在 xml 中进行设置,用 fresco:actualImageScaleType 这样的属性。你也可以使用 GenericDraweeHierarchyBuilder 类在代码中进行设置。 即使显示效果已经构建完成,实际图片的缩放类型仍然可以通过 GenericDraweeHierarchy 类在运行中进行修改。 不要使用 android:scaleType 属性,也不要使用 setScaleType() 方法,它们对 Drawees 无效。


2.  focusCrop 

可指定某个点进行居中

如果要使用此缩放模式,首先在 XML 中指定缩放模式:
fresco:actualImageScaleType="focusCrop"

在Java代码中,给你的图片指定居中点:
mSimpleDraweeView .getHierarchy() .setActualImageFocusPoint(PointF pointF);

3. 自定义SacleType

有时候现有的 ScaleType 不符合你的需求,我们允许你通过实现 ScalingUtils.ScaleType 来拓展它,这个接口里面只有一个方法:getTransform,它会基于以下参数来计算转换矩阵:
parent bounds (View 坐标系中对图片的限制区域)
child size (要放置的图片高宽)
focus point (图片坐标系中的聚焦点位置)
当然,你的类里面应该包含了你需要额外信息。

官网例子
假设 View 应用了一些 padding, parentBounds 为 (100, 150, 500, 450), 图片大小为(420,210)。那么我们知道 View 的宽度度为 500 - 100 = 400, 高度为 450 - 150 = 300。那么如果我们不做任何处理,图片在 View 坐标系中就会被画在(0, 0, 420, 210)。但是 ScaleTypeDrawable 会使用 parentBounds 来进行限制,那么画布就会被 (100, 150, 500, 450) 这块矩阵裁切,那么最后图片显示区域就是 (100, 150, 420, 210)。
为了避免这种情况,我们可以变换 (parentBounds.left, parentBounds.top) (在这个例子中是(100, 150))。现在图片比 View 还宽,现在无论是将图片放置在 (100, 150, 500, 360) 还是 (0, 0, 400, 210),我们都会损失右侧20像素的宽度。
那么我们可以将它缩小一点(400/420的缩放比例),让它能够放置在 View 给它分配的区域中(缩放后变成了400, 200)。那么现在宽度刚刚好,但是高度却不是了。
我们需要进一步处理,我们可以算一下高度还剩余的空间 300 - 200 = 100。我们可以将他们平均分配在上下两侧,这样图片就被居中了,真棒!你可以通过实现 FIT_CENTER 来让它做到这点。那么来看代码吧:
  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;
  }
}

六.  多图请求和复用

场景一:假设你要显示一张高分辨率的图,但是这张图下载比较耗时。与其一直显示占位图,你可能想要先下载一个较小的缩略图。
这时,你可以设置两个图片的URI,一个是低分辨率的缩略图,一个是高分辨率的图。
Uri lowResUri, highResUri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
        .setLowResImageRequest(ImageRequest.fromUri(lowResUri))
        .setImageRequest(ImageRequest.fromUri(highResUri))
        .setOldController(mSimpleDraweeView.getController())
        .build();
mSimpleDraweeView.setController(controller);
ps:  动图无法在低分辨率那一层显示。

场景二:但是假设同一张图片有多个 URI 的情况。比如,你可能上传过一张拍摄的照片。原始图片太大而不能上传,所以图片首先经过了压缩。在这种情况下,首先尝试获取本地压缩后的图片 URI,如果失败的话,尝试获取本地原始图片 URI,如果还是失败的话,尝试获取上传到网络的图片 URI。直接下载我们本地可能已经有了的图片不是一件光彩的事。
Uri uri1, uri2;
ImageRequest request = ImageRequest.fromUri(uri1);
ImageRequest request2 = ImageRequest.fromUri(uri2);
ImageRequest[] requests = { request1, request2 };

DraweeController controller = Fresco.newDraweeControllerBuilder()
        .setFirstAvailableImageRequests(requests)
        .setOldController(mSimpleDraweeView.getController())
        .build();
mSimpleDraweeView.setController(controller);

七. 渐进式加载图片

意思是图会从模糊到清晰的过程
Fresco 支持渐进式的网络JPEG图。在开始加载之后,图会从模糊到清晰渐渐呈现。
你可以设置一个清晰度标准,在未达到这个清晰度之前,会一直显示占位图。
渐进式JPEG图仅仅支持网络图。本地图片会一次解码完成,所以没必要渐进式加载。你还需要知道的是,并不是所有的JPEG图片都是渐进式编码的,所以对于这类图片,不可能做到渐进式加载。
Uri uri;
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
        .setProgressiveRenderingEnabled(true)
        .build();
DraweeController controller = Fresco.newDraweeControllerBuilder()
        .setImageRequest(request)
        .setOldController(mSimpleDraweeView.getController())
        .build();
mSimpleDraweeView.setController(controller);

八. 动画支持

针对GIF 以及webp格式的图片
如果你希望图片下载完之后自动播放,同时,当View从屏幕移除时,停止播放,只需要在 image request 中简单设置,如下:
Uri uri;
    DraweeController controller = Fresco.newDraweeControllerBuilder()
            .setUri(uri)
            .setAutoPlayAnimations(true)
            . // 其他设置(如果有的话)
.build();
    mSimpleDraweeView.setController(controller);

手动配置
ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
    @Override
    public void onFinalImageSet(
            String id,
            @Nullable ImageInfo imageInfo,
            @Nullable Animatable anim) {
        if (anim != null) {
            // 其他控制逻辑
            anim.start();
        }
    }
};

Uri uri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
        .setUri(uri)
        .setControllerListener(controllerListener)
        // 其他设置(如果有的话)
        .build();
mSimpleDraweeView.setController(controller);
控制如下:
Animatable animatable = mSimpleDraweeView.getController().getAnimatable();
if (animatable != null) {
    animatable.start();
    // later
    animatable.stop();
}

ps:  动画现在还不支持 postprocessors 。
 
  
 
 

猜你喜欢

转载自blog.csdn.net/weixin_39158738/article/details/78069220