1、简介
Fresco是Facebook最新推出的一款用于Android应用中展示图片的强大图片库,可以从网络、本地存储和本地资源中加载图片。相对于ImageLoader,拥有更快的图片下载速度以及可以加载和显示gif图等诸多优势,是个很好的图片框架。
2、特点
1)内存管理
在5.0以下系统,Fresco将图片放到一个特别的内存区域。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。
内存分配采用:系统匿名共享内存
2)渐进式呈现图片
渐进式图片格式先呈现大致的图片轮廓,然后随着图片下载的继续, 呈现逐渐清晰的图片,这对于移动设备,尤其是慢网络有极大的利好,可带来更好的用户体验。
3)支持加载Gif图,支持WebP格式。
4)图像的呈现
(1)自定义居中焦点(对人脸等图片显示非常有帮助)。
(2)圆角图,当然圆圈也行。
(3)下载失败之后,点击重新下载。
(4)自定义占位图,自定义overlay, 或者进度条。
(5)指定用户按压时的overlay。
5)图像的加载
(1)为同一个图片指定不同的远程路径,或者使用已经存在本地缓存中的图片。
(2)先显示一个低解析度的图片,等高清图下载完之后再显示高清图。
(3)加载完成回调通知。
(4)对于本地图,如有EXIF缩略图,在大图加载完成之前,可先显示缩略图。
(5)缩放或者旋转图片。
(6)处理已下载的图片。
3、下载地址
https://github.com/facebook/fresco
官方使用网址:
http://fresco-cn.org/docs/index.html
4、支持的URI
远程图片
http://, https://
本地文件
file://
Content provider
content://
asset目录下的资源
asset://
res目录下的资源
res://
Uri中指定图片数据
data:mime/type;base64,
5、常用API
android:layout_width="20dp"// 不支持wrap_content, 如果要设置宽高比, 需要在Java代码中指定setAspectRatio(float ratio);
android:layout_height="20dp" // 不支持wrap_content
fresco:placeholderImage="@color/wait_color"// 下载成功之前显示的图片
fresco:placeholderImageScaleType="fitCenter"// 设置图片缩放. 通常使用focusCrop,该属性值会通过算法把人头像放在中间
fresco:failureImage="@drawable/error"// 加载失败的时候显示的图片
fresco:failureImageScaleType=“centerInside"// 设置图片缩放
fresco:retryImage="@drawable/retrying"// 加载失败,提示用户点击重新加载的图片(会覆盖failureImage的图片)
fresco:roundAsCircle="true"// 设置圆形方式显示图片
// 圆角设置
fresco:roundedCornerRadius="1dp" fresco:roundTopLeft="true" fresco:roundTopRight="false" fresco:roundBottomLeft="false" fresco:roundBottomRight="true" fresco:roundWithOverlayColor="@color/corner_color" fresco:roundingBorderWidth="2dp" fresco:roundingBorderColor="@color/border_color"
6、使用步骤
(1)添加依赖
dependencies { // 在 API < 14 上的机器支持 WebP 时,需要添加 compile 'com.facebook.fresco:animated-base-support:0.14.1' // 支持 GIF 动图,需要添加 compile 'com.facebook.fresco:animated-gif:0.14.1' // 支持 WebP (静态图+动图),需要添加 compile 'com.facebook.fresco:animated-webp:0.14.1' compile 'com.facebook.fresco:webpsupport:0.14.1' // 仅支持 WebP 静态图,需要添加 compile 'com.facebook.fresco:webpsupport:0.14.1' compile 'com.facebook.fresco:fresco:0.14.1' }
(2)在application中初始化Fresco
Fresco.initialize(this);
(3)配置网络权限
<uses-permission android:name="android.permission.INTERNET"/>
(4)在xml布局文件中,加入命名空间
<!-- 其他元素--> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:fresco="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" android:layout_width="match_parent">
(5)在xml中引入SimpleDraweeView
<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/my_image_view" android:layout_width="130dp" android:layout_height="130dp" fresco:placeholderImage="@drawable/my_drawable" />
(6)在Java代码中开始加载图片
Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/logo.png"); SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view); draweeView.setImageURI(uri);
注意:
如果项目中使用了OkHttp需要进行替换
For OkHttp2:
compile "com.facebook.fresco:imagepipeline-okhttp:0.12.0+"
For OkHttp3:
compile "com.facebook.fresco:imagepipeline-okhttp3:0.12.0+"
7、例子
1)带进度条的图片 (先显示进度条,在显示图片)
(1)布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:fresco="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_fresco_spimg" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/sdv_fresco_spimg" android:layout_width="130dp" android:layout_gravity="center" android:layout_height="130dp" fresco:placeholderImage="@drawable/atguigu_logo" /> </LinearLayout>
(2)代码
@Bind(R.id.sdv_fresco_spimg) SimpleDraweeView sdvFrescoSpimg; private void initData() { // 设置样式 GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(getResources()); GenericDraweeHierarchy hierarchy = builder.setProgressBarImage(new ProgressBarDrawable()).build(); sdvFrescoSpimg.setHierarchy(hierarchy); // 加载图片的地址 Uri uri = Uri.parse("http://img4.duitang.com/uploads/item/201211/24/20121124175330_ruKEK.jpeg"); // 加载图片 sdvFrescoSpimg.setImageURI(uri); }
2)图片的不同裁剪
public class FrescoCropActivity extends Activity { @Bind(R.id.sdv_fresco_crop) SimpleDraweeView sdvFrescoCrop; private GenericDraweeHierarchyBuilder builder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fresco_crop); ButterKnife.bind(this); initData(); } private void initData() { builder = new GenericDraweeHierarchyBuilder(getResources()); } // 居中,无缩放 @OnClick(R.id.bt_fresco_center) void bt_fresco_center_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.CENTER).build(); // 图片显示 imageDisplay(hierarchy); } private void imageDisplay(GenericDraweeHierarchy hierarchy) { sdvFrescoCrop.setHierarchy(hierarchy); // 加载图片 Uri uri = Uri.parse("http://img4q.duitang.com/uploads/item/201305/20/20130520115416_VrUUR.jpeg"); sdvFrescoCrop.setImageURI(uri); } // 保持宽高比缩小或放大,使得两边都大于或等于显示边界。居中显示 @OnClick(R.id.bt_fresco_centercrop) void bt_fresco_centercrop_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP).build(); // 图片显示 imageDisplay(hierarchy); } // 同centerCrop, 但居中点不是中点,而是指定的某个点,这里我设置为图片的左上角那点 @OnClick(R.id.bt_fresco_focuscrop) void bt_fresco_focuscrop_click(View view){ // 样式设置 PointF point = new PointF(0,0); GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.FOCUS_CROP) .setActualImageFocusPoint(point).build(); // 图片显示 imageDisplay(hierarchy); } // 使两边都在显示边界内,居中显示。如果图尺寸大于显示边界,则保持长宽比缩小图片 @OnClick(R.id.bt_fresco_centerinside) void bt_fresco_centerinside_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_INSIDE).build(); // 图片显示 imageDisplay(hierarchy); } // 保持宽高比,缩小或者放大,使得图片完全显示在显示边界内。居中显示 @OnClick(R.id.bt_fresco_fitcenter) void bt_fresco_fitcenter_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER).build(); // 图片显示 imageDisplay(hierarchy); } // 保持宽高比,缩小或者放大,使得图片完全显示在显示边界内,不居中,和显示边界左上对齐 @OnClick(R.id.bt_fresco_fitstart) void bt_fresco_fitstart_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_START).build(); // 图片显示 imageDisplay(hierarchy); } // 保持宽高比,缩小或者放大,使得图片完全显示在显示边界内,不居中,和显示边界右下对齐 @OnClick(R.id.bt_fresco_fitend) void bt_fresco_fitend_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_END).build(); // 图片显示 imageDisplay(hierarchy); } // 不保持宽高比,填充满显示边界 @OnClick(R.id.bt_fresco_fitxy) void bt_fresco_fitxy_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_XY).build(); // 图片显示 imageDisplay(hierarchy); } // 如要使用title mode显示, 需要设置为none @OnClick(R.id.bt_fresco_none) void bt_fresco_none_click(View view){ // 样式设置 GenericDraweeHierarchy hierarchy = builder.setActualImageScaleType(null).build(); // 图片显示 imageDisplay(hierarchy); } }
3)圆形和圆角图片
原图
(1)设置圆形图片
(2)设置圆角图片
public class FrescoCircleAndCornerActivity extends Activity { @Bind(R.id.sdv_fresco_circleandcorner) SimpleDraweeView sdvFrescoCircleandcorner; private Uri uri; private GenericDraweeHierarchyBuilder builder; private RoundingParams parames; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fresco_circle_and_corner); ButterKnife.bind(this); initData(); } private void initData() { uri = Uri.parse("http://img4q.duitang.com/uploads/item/201304/27/20130427043538_wAfHC.jpeg"); builder = new GenericDraweeHierarchyBuilder(getResources()); } // 设置圆形图片 @OnClick(R.id.bt_fresco_circle) void bt_fresco_circle_click(View view){ parames = RoundingParams.asCircle(); GenericDraweeHierarchy hierarchy = builder.setRoundingParams(parames).build(); sdvFrescoCircleandcorner.setHierarchy(hierarchy); sdvFrescoCircleandcorner.setImageURI(uri); } // 设置圆角图片 @OnClick(R.id.bt_fresco_corner) void bt_fresco_corner_click(View view){ parames = RoundingParams.fromCornersRadius(50f); parames.setOverlayColor(getResources().getColor(android.R.color.holo_red_light));//覆盖层 parames.setBorder(getResources().getColor(android.R.color.holo_blue_light), 5);//边框 GenericDraweeHierarchy hierarchy = builder.setRoundingParams(parames).build(); sdvFrescoCircleandcorner.setHierarchy(hierarchy); // 加载图片 sdvFrescoCircleandcorner.setImageURI(uri); } }
4)渐进式展示图片(从不清晰到清晰)
// 加载质量配置 ProgressiveJpegConfig jpegConfig = new ProgressiveJpegConfig() { @Override public int getNextScanNumberToDecode(int scanNumber) { return scanNumber + 2; } @Override public QualityInfo getQualityInfo(int scanNumber) { boolean isGoodEnough = (scanNumber >= 5); return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false); } }; ImagePipelineConfig.newBuilder(this).setProgressiveJpegConfig(jpegConfig).build(); // 获取图片URL Uri uri = Uri.parse("http://cdn.duitang.com/uploads/item/201303/12/20130312021353_45Qix.jpeg"); // 获取图片请求 ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri).setProgressiveRenderingEnabled(true).build(); DraweeController draweeController = Fresco.newDraweeControllerBuilder() .setImageRequest(request) .setTapToRetryEnabled(true) .setOldController(sdvFrescoJpeg.getController())//使用oldController可以节省不必要的内存分配 .build(); // 1设置加载的控制 sdvFrescoJpeg.setController(draweeController);
5)Gif动画图片
(1)
(2)添加依赖
compile 'com.facebook.fresco:animated-gif:0.14.1'
(3)代码
@Bind(R.id.sdv_fresco_gif) SimpleDraweeView sdvFrescoGif; // 请求gif图片 @OnClick(R.id.bt_fresco_askImg) void bt_fresco_askImg_click(View view){ Uri uri = Uri.parse("http://www.sznews.com/humor/attachement/gif/site3/20140902/4487fcd7fc66156f51db5d.gif"); DraweeController controller = Fresco.newDraweeControllerBuilder() .setUri(uri) .setAutoPlayAnimations(false) .setOldController(sdvFrescoGif.getController()) .build(); sdvFrescoGif.setController(controller); } // 动画停止 @OnClick(R.id.bt_fresco_stopAnim) void bt_fresco_stopAnim_click(View view){ Animatable animatable = sdvFrescoGif.getController().getAnimatable(); if(animatable != null && animatable.isRunning()) { animatable.stop(); } } // 动画开始 @OnClick(R.id.bt_fresco_startAnim) void bt_fresco_startAnim_click(View view){ Animatable animatable = sdvFrescoGif.getController().getAnimatable(); if(animatable != null && !animatable.isRunning()) { animatable.start(); } }
6)多图请求及图片复用
// 先显示低分辨率的图,然后是高分辨率的图 @OnClick(R.id.bt_fresco_multiImg) void bt_fresco_multiImg_click(View view){ // 图片地址 Uri lowUri = Uri.parse("http://img1.gamedog.cn/2012/03/11/19-120311133617-50.jpg"); Uri highUri = Uri.parse("http://img5.duitang.com/uploads/item/201312/03/20131203153823_Y4y8F.jpeg"); // 控制加载图片 DraweeController controller = Fresco.newDraweeControllerBuilder() .setLowResImageRequest(ImageRequest.fromUri(lowUri)) .setImageRequest(ImageRequest.fromUri(highUri)) .build(); // 加载图片 sdvFrescoMulti.setController(controller); } // 本地缩略图预览 @OnClick(R.id.bt_fresco_thumbnailImg) void bt_fresco_thumbnailImg_click(View view){ // 图片地址 Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory() +"/meinv1.jpg")); // 加载图片的请求 ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setLocalThumbnailPreviewsEnabled(true) .build(); // 控制图片的加载 DraweeController controller = Fresco.newDraweeControllerBuilder() .setImageRequest(request) .build(); // 加载图片 sdvFrescoMulti.setController(controller); } // 本地图片复用 @OnClick(R.id.bt_fresco_multiplexImg) void bt_fresco_multiplexImg_click(View view){ //本地图片的复用 //在请求之前,还会去内存中请求一次图片,没有才会先去本地,最后去网络uri //本地准备复用图片的uri 如果本地这个图片不存在,会自动去加载下一个uri // 请求加载图片 Uri uri1 = Uri.fromFile(new File(Environment.getExternalStorageDirectory()+"/meinv.jpg")); //图片的网络uri Uri uri2 = Uri.parse("http://img5.duitang.com/uploads/item/201312/03/20131203153823_Y4y8F.jpeg"); ImageRequest request1 = ImageRequest.fromUri(uri1); ImageRequest request2 = ImageRequest.fromUri(uri2); ImageRequest[] requests = {request1, request2}; // 控制加载图片 DraweeController controller = Fresco.newDraweeControllerBuilder() .setFirstAvailableImageRequests(requests) .setOldController(sdvFrescoMulti.getController()) .build(); // 加载图片 sdvFrescoMulti.setController(controller); }
7)图片加载监听

1 public class FrescoListenerActivity extends Activity { 2 3 @Bind(R.id.tv_title) 4 TextView tvTitle; 5 @Bind(R.id.sdv_fresco_listener) 6 SimpleDraweeView sdvFrescoListener; 7 @Bind(R.id.tv_fresco_listener) 8 TextView tvFrescoListener; 9 @Bind(R.id.tv_fresco_listener2) 10 TextView tvFrescoListener2; 11 private ControllerListener controllerListener = new BaseControllerListener<ImageInfo>(){ 12 // 加载图片完毕 13 @Override 14 public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) { 15 super.onFinalImageSet(id, imageInfo, animatable); 16 17 if (imageInfo == null) { 18 return; 19 } 20 21 // 获取图片的质量 22 QualityInfo qualityInfo = imageInfo.getQualityInfo(); 23 24 tvFrescoListener.setText("Final image received! " + 25 " Size: " + imageInfo.getWidth() 26 + "x" + imageInfo.getHeight() 27 + " Quality level: " + qualityInfo.getQuality() 28 + " good enough: " + qualityInfo.isOfGoodEnoughQuality() 29 + " full quality: " + qualityInfo.isOfFullQuality()); 30 } 31 32 // 渐进式加载图片回调 33 @Override 34 public void onIntermediateImageSet(String id, ImageInfo imageInfo) { 35 super.onIntermediateImageSet(id, imageInfo); 36 37 tvFrescoListener2.setText("IntermediateImageSet image receiced"); 38 } 39 40 // 加载图片失败 41 @Override 42 public void onFailure(String id, Throwable throwable) { 43 super.onFailure(id, throwable); 44 45 46 tvFrescoListener.setText("Error loading" + id); 47 } 48 }; 49 50 @Override 51 protected void onCreate(Bundle savedInstanceState) { 52 super.onCreate(savedInstanceState); 53 54 setContentView(R.layout.activity_fresco_listener); 55 ButterKnife.bind(this); 56 57 initData(); 58 } 59 60 private void initData() { 61 tvTitle.setText("图片加载监听"); 62 } 63 64 @OnClick(R.id.bt_fresco_listener) 65 void bt_fresco_listener(View view){ 66 67 // 加载图片质量配置 68 ProgressiveJpegConfig jpegConfig = new ProgressiveJpegConfig() { 69 @Override 70 public int getNextScanNumberToDecode(int scanNumber) { 71 return scanNumber + 2; 72 } 73 74 @Override 75 public QualityInfo getQualityInfo(int scanNumber) { 76 boolean isGoodEnough = (scanNumber >= 5); 77 78 return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false); 79 } 80 }; 81 82 ImagePipelineConfig.newBuilder(this).setProgressiveJpegConfig(jpegConfig).build(); 83 84 // 图片地址 85 Uri uri = Uri.parse("http://h.hiphotos.baidu.com/zhidao/pic/item/58ee3d6d55fbb2fbac4f2af24f4a20a44723dcee.jpg"); 86 87 // 图片请求 88 ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) 89 .setProgressiveRenderingEnabled(true) 90 .build(); 91 92 // 图片加载的控制 93 DraweeController controller = Fresco.newDraweeControllerBuilder() 94 .setOldController(sdvFrescoListener.getController()) 95 .setImageRequest(request) 96 .setControllerListener(controllerListener) 97 .build(); 98 99 // 加载图片 100 sdvFrescoListener.setController(controller); 101 } 102 }
8)图片缩放和旋转
a)缩放
// 图片地址 Uri uri = Uri.parse("http://c.hiphotos.baidu.com/image/pic/item/962bd40735fae6cd21a519680db30f2442a70fa1.jpg"); // 图片的请求 ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setResizeOptions(new ResizeOptions(50,50)) .build(); // 控制图片的加载 PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder() .setOldController(sdvFrescoResize.getController()) .setImageRequest(request) .build(); // 加载图片 sdvFrescoResize.setController(controller);
b)旋转
Uri uri = Uri.parse("http://c.hiphotos.baidu.com/image/pic/item/962bd40735fae6cd21a519680db30f2442a70fa1.jpg"); ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setAutoRotateEnabled(true) .build(); // 控制图片的加载 DraweeController controller = Fresco.newDraweeControllerBuilder() .setOldController(sdvFrescoResize.getController()) .setImageRequest(request) .build(); // 加载图片 sdvFrescoResize.setController(controller);
9)修改图片--为图片添加网格
// 图片地址 Uri uri = Uri.parse("http://c.hiphotos.baidu.com/image/pic/item/962bd40735fae6cd21a519680db30f2442a70fa1.jpg"); // 修改图片 Postprocessor postProcessor = new BasePostprocessor() { @Override public String getName() { return "postProcessor"; } @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); } } } }; // 创建图片请求 ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setPostprocessor(postProcessor) .build(); // 控制加载 PipelineDraweeController controller = (PipelineDraweeController)Fresco.newDraweeControllerBuilder() .setOldController(sdvFrescoModify.getController()) .setImageRequest(request) .build(); // 加载图片 sdvFrescoModify.setController(controller);
10)动态展示图片
(1)布局
<LinearLayout android:id="@+id/ll_fresco" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:orientation="vertical"> </LinearLayout>
(2)代码
// 图片的地址 Uri uri = Uri.parse("http://img4q.duitang.com/uploads/item/201304/27/20130427043538_wAfHC.jpeg"); // 图片的请求 ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .build(); // 加载图片的控制 PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder() .setOldController(simpleDraweeView.getController()) .setImageRequest(request) .build(); // 加载图片 simpleDraweeView.setController(controller); // 添加View到线性布局中 llFresco.addView(simpleDraweeView);
8、注意事项
1)问题处理
1)重复的边界
2)图片没有加载
3)文件不可用
4)OOM - 无法分配图片空间
5)Bitmap太大导致无法绘制
6)通过Logcat来判断原因
7)启动日志
8)查看日志
2)一些陷阱
1)不要使用 ScrollViews
2)不要向下转换
3)不要使用getTopLevelDrawable
4)不要复用 DraweeHierarchies
5)不要在多个DraweeHierarchy中使用同一个Drawable
6)不要直接控制 hierarchy
7)不要直接给 DraweeView 设置图片
8)使用 DraweeView 时,请不要使用任何 ImageView 的属性
3)为什么不支持wrap_content
4)共享元素动画
9、参考网站
http://blog.csdn.net/u011771755/article/details/47608191
https://github.com/NateRobinson/FrescoStudyDemo