这几天研究了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
下面的例子将源图片复制为 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. 处理重复的后处理
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,但是这个后处理器是可控制的。
五. 缩放
1. 基本使用
2. focusCrop
如果要使用此缩放模式,首先在 XML 中指定缩放模式:
fresco:actualImageScaleType="focusCrop"
在Java代码中,给你的图片指定居中点:
mSimpleDraweeView .getHierarchy() .setActualImageFocusPoint(PointF pointF);
3. 自定义SacleType
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 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);
七. 渐进式加载图片
你可以设置一个清晰度标准,在未达到这个清晰度之前,会一直显示占位图。
渐进式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);
八. 动画支持
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(); }