一篇不错的Fresco文章
来源:互联网 发布:win7如何更改mac地址 编辑:程序博客网 时间:2024/04/26 17:22
版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com
今天只是入门级别的,改天会出一个深度一点的文章介绍用法。
题外话:最近消沉了有快三个月了,这几天都不知道自己在干嘛,这是春节之后的第一篇博文,从今天开始让一切都不一样:
Code Behavior, one can.t be less.
Fresco是一个Facebook开源的Android图片加载库,性能真的让我无话可说,而且满足了我对图片加载的一切幻想,所以我必须为它写一篇文章,当然更多的是自己的总结与记录。
Fresco开源地址:https://github.com/facebook/fresco
Fresco文档地址:https://www.fresco-cn.org
前端时间我写了一个Android相册库Album:https://github.com/yanzhenjie/album,当时我在做测试的时候发现,用Picasso或者Glide时,当列表达到上千条时,滑动起来就会卡,但是换成Fresco
后一点都不卡了,而且Fresco
做到的几个内置效果让我欣喜若狂,所以我把我的使用总结记录下来:
依赖Fresco
// 一般依赖:compile 'com.facebook.fresco:fresco:0.14.1'// 如果需要支持gif,再添加:compile 'com.facebook.fresco:animated-gif:0.12.0'
- 1
- 2
- 3
- 4
- 5
初始化
建议在App启动就初始化,所以建议写在Application#onCreate()
中,记得在manifest.xml
注册Application
。
一般初始化:
public class App extends Application { @Override public void onCreate() { super.onCreate(); Fresco.initialize(this); } ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
高级初始化-配置缓存文件夹
// 高级初始化:Fresco.initialize(this, ImagePipelineConfig.newBuilder(App.this) .setMainDiskCacheConfig( DiskCacheConfig.newBuilder(this) .setBaseDirectoryPath(new File("SD卡路径")) // 注意Android运行时权限。 .build() ) .build());
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
上面的高级初始化当然不止这么一点点,这里举出一个敏感的例子,就是配置缓存SD卡路径,这里涉及到Android6.0运行时权限,我也给一个解决方案,我使用的权限管理库是AndPermission
(https://github.com/yanzhenjie/AndPermission):
首先在Application
中判断是否有SD卡权限,如果有则初始化到SD卡,如果没有则采用默认配置:
public class App extends Application { private static App app; @Override public void onCreate() { super.onCreate(); app = this; // 如果有SD卡权限则直接初始化到SD卡。 if (AndPermission.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) initFresco(); else { // 没有权限,暂时使用默认配置。 Fresco.initialize(this); } } /** * 高级初始话Fresco。 */ public void initFresco() { // 高级初始化: Fresco.initialize(this, ImagePipelineConfig.newBuilder(App.this) .setMainDiskCacheConfig( DiskCacheConfig.newBuilder(this) .setBaseDirectoryPath(new File("SD卡的路径...")) .build() ) .build() ); } public static App get() { return app; }}
- 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
然后在SplashActivity
申请SD卡权限,已被下次进入App
时初始化Fresco
时拥有SD卡权限:
public class SplashActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 申请权限。 AndPermission.with(this) .requestCode(100) .permission(Manifest.permission.READ_CALENDAR) .send(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { AndPermission.onRequestPermissionsResult(requestCode, permissions, grantResults, listener); } /** * 权限监听。 */ private PermissionListener listener = new PermissionListener() { @Override public void onSucceed(int requestCode, List<String> grantPermissions) { if(requestCode == 100) // 启动app: startActivity(new Intent(SplashActivity.this, MainActivity.class)); } @Override public void onFailed(int requestCode, List<String> deniedPermissions) { if(requestCode == 100) // 用户不授权,则退出app: finish(); } };}
- 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
高级初始化-配置网络层为OkHttp
Fresco
默认使用HttpURLConnection
作为网络层,当然也可以配置OkHttp作为它的网络层,配置OkHttp为它的网络层需要依赖下面的库:
compile "com.facebook.fresco:imagepipeline-okhttp3:0.12.0+"
- 1
然后在Application中初始化的时候注意:
/** * 初始话Fresco。 */public void initFresco() { // 你的OkHttpClient根据你的设计来,建议是单例: OkHttpClient okHttpClient = new OkHttpClient(); Fresco.initialize(this, OkHttpImagePipelineConfigFactory.newBuilder(App.this, okHttpClient) .setMainDiskCacheConfig( DiskCacheConfig.newBuilder(this) .setBaseDirectoryPath(new File("SD卡的路径...")) .build() ) .build() );}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
这里只需要注意原来的ImagePipelineConfig
换成了OkHttpImagePipelineConfigFactory
,并且需要一个OkHttpClient
的对象。
加载网络图片、url、assets、res、本地File图片
先给出支持的URI格式列表(列表来自fresco-cn.org):
SimpleDraweeView
实际开发中,如果没有特殊需求,我们一般使用SimpleDraweeView
来占位,传统的图片加载框架一般是使用ImageView
,例如:
<ImageView.../>
- 1
- 2
在使用Fresco
时我们一般使用SimpleDraweeView
,它也是继承ImageView
的:
<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/iv_head_background" android:layout_width="match_parent" android:layout_height="@dimen/dp_200"/>
- 1
- 2
- 3
- 4
把它当成我们平常使用的ImageView
即可,不过我们要注意Fresco
不支持wrap_content
(具体原因看这里),需要使用match_parent
或者显示指定view宽高,有些同学看到这个就很头疼了,但是这真的不是什么问题,下面给出解决方案:
一、由服务器返回URL时返回图片的宽高信息
平常我们服务器这样返回图片url的:
{ "name":"严振杰", "head":"http://www.yanzhenjie.com/images/main/yzj_head.png"}
- 1
- 2
- 3
- 4
使用Fresco我们可以把一个图片url当成一个对象包裹起来:
{ "name":"严振杰", "head": { "url":"http://www.yanzhenjie.com/images/main/yzj_head.png", "width":"500", "height":"500" }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
或者在URL后面跟一个宽高的参数:
{ "name":"严振杰", "head":"http://www.yanzhenjie.com/images/main/yzj_head.png?width=500&height=500"}
- 1
- 2
- 3
- 4
二、根据设计师的给的尺寸,预先设置图片宽高
设计师设计UI的时候肯定会用一个屏幕作为标准,比如iOS的750*1340
,或者Android的720*1280
,我们可以根据设计图和手机实际宽高计算出View在手机中应有的宽高,见下面的代码。
最后:我们在解析出来宽高后,我们可以动态的设置SimpleDraweeView
的宽高:
/** * 设置view大小。 * * @param view View。 * @param width 指定宽。 * @param width 指定高。 */public static void requestLayout(View view, int width, int height) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); if (layoutParams == null) { layoutParams = new ViewGroup.LayoutParams(width, height); view.setLayoutParams(layoutParams); } else { view.getLayoutParams().width = width; view.getLayoutParams().height = height; view.requestLayout(); }}/** * 根据设计图宽高,计算出View在该屏幕上的实际宽高。 * * @param width 设计图中View宽。 * @param height 设计图中View高。 */public static void calcRealSizeByDesign(View view, int width, int height) { int realWidth, realHeight; realWidth = 设备屏幕宽度 * width / 设计图屏幕宽度; realHeight = measure[0] * height / width; requestLayout(view, realWidth, realHeight);}
- 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/https远程图片
/** * 显示http或者https远程图片。 * * @param draweeView imageView。 * @param url 连接地址。 */public static void showUrl(SimpleDraweeView draweeView, String url) { try { draweeView.setImageURI(Uri.parse(url)); } catch (Exception e) { e.printStackTrace(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
二、显示本地图片
这里就有个坑了,先看一下下面我写了两个方法,一个需要传入View的实际宽高,一个不需要。上面已经说了,SimpleDraweeView
需要在xml中、java中指定它的宽高,或者是使用match_parent
。
这里需要注意,1. 如果view指定的宽高不是match_parent
则直接调用第二个不需要传入宽高的发那个发,如果为SimpleDraweeView
写的宽高是match_parent
时,加载图片需要告诉Fresco
你的View在屏幕上的实际宽高是多少,否则是不能加载出来的。
比如,你的SimpleDraweeView
是全屏的,那么你就填入屏幕的宽高,如果不是全屏,就利用上面讲的方法测量出View的实际宽高后传入。
/** * 显示一个本地图片。 * * @param draweeView imageView。 * @param path 路径。 * @param width 实际宽。 * @param height 实际高度。 */public static void showFile(SimpleDraweeView draweeView, String path, int width, int height) { try { Uri uri = Uri.parse("file://" + path); ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setResizeOptions(new ResizeOptions(width, height)) .build(); AbstractDraweeController controller = Fresco.newDraweeControllerBuilder() .setOldController(draweeView.getController()) .setImageRequest(request) .build(); draweeView.setController(controller); } catch (Exception e) { e.printStackTrace(); }}/** * 显示本地图片。 * * @param draweeView imageView。 * @param path 路径。 */public static void showFile(SimpleDraweeView draweeView, String path) { try { Uri uri = Uri.parse("file://" + path); draweeView.setImageURI(uri); } catch (Exception e) { e.printStackTrace(); }}
- 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
三、显示res中图片
这里要注意,我们在为res中的图片生成Uri的时候:
Uri uri = Uri.parse("res://包名(任何字符串或者留空)/" + R.drawable.ic_launcher);
- 1
所以我们一般留空,因此我们的代码看起来是下面的样子:
/** * 显示一个Res中的图片。 * * @param draweeView ImageView。 * @param resId 资源ID。 */public static void showRes(SimpleDraweeView draweeView, @DrawableRes int resId) { try { // 你没看错,这里是三个///。 draweeView.setImageURI(Uri.parse("res:///" + resId)); } catch (Exception e) { e.printStackTrace(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
四、显示ContentProvider图片
/** * 显示content provider图片。 * * @param draweeView image view。 * @param path 路径。 */public static void showContentProvider(SimpleDraweeView draweeView, String path) { try { draweeView.setImageURI(Uri.parse("content://" + path)); } catch (Exception e) { e.printStackTrace(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
五、显示assets中的图片
/** * 显示Assets中的图片。 * * @param draweeView ImageView. * @param path 路径。 */public static void showAsset(SimpleDraweeView draweeView, String path) { try { draweeView.setImageURI(Uri.parse("asset://" + path)); } catch (Exception e) { e.printStackTrace(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
你以为到这里就完了吗?并没有,继续看。
一些默认属性的设置
<com.facebook.drawee.view.SimpleDraweeView android:layout_width="20dp" android:layout_height="20dp" fresco:fadeDuration="300" // 淡出时间,毫秒。 fresco:actualImageScaleType="focusCrop" // 等同于android:scaleType。 fresco:placeholderImage="@color/wait_color" // 加载中…时显示的图。 fresco:placeholderImageScaleType="fitCenter" // 加载中…显示图的缩放模式。 fresco:failureImage="@drawable/error" // 加载失败时显示的图。 fresco:failureImageScaleType="centerInside" // 加载失败时显示图的缩放模式。 fresco:retryImage="@drawable/retrying" // 重试时显示图。 fresco:retryImageScaleType="centerCrop" // 重试时显示图的缩放模式。 fresco:progressBarImage="@drawable/progress_bar" // 进度条显示图。 fresco:progressBarImageScaleType="centerInside" // 进度条时显示图的缩放模式。 fresco:progressBarAutoRotateInterval="1000" // 进度条旋转时间间隔。 fresco:backgroundImage="@color/blue" // 背景图,不会被View遮挡。 fresco:roundAsCircle="false" // 是否是圆形图片。 fresco:roundedCornerRadius="1dp" // 四角圆角度数,如果是圆形图片,这个属性被忽略。 fresco:roundTopLeft="true" // 左上角是否圆角。 fresco:roundTopRight="false" // 右上角是否圆角。 fresco:roundBottomLeft="false" // 左下角是否圆角。 fresco:roundBottomRight="true" // 左下角是否圆角。 fresco:roundingBorderWidth="2dp" // 描边的宽度。 fresco:roundingBorderColor="@color/border_color" 描边的颜色。/>
- 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
xml中可以配置,在Java代码中也是可以配置的,我这里列出一部分API:
SimpleDraweeView simpleDraweeView = new SimpleDraweeView(context);simpleDraweeView.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(context.getResources()) .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER) .setPlaceholderImage(R.drawable.fresco_failed) .setPlaceholderImageScaleType(ScalingUtils.ScaleType.FIT_XY) .setFailureImage(R.drawable.fresco_failed) .setPressedStateOverlay(ResCompat.getDrawable(R.drawable.transparent_half_1)) .setFailureImageScaleType(ScalingUtils.ScaleType.FIT_XY) .build();simpleDraweeView.setHierarchy(hierarchy);// load image from ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
一些特殊效果
第一个,先来一个圆形图片带白色的边:
综合上面的属性这里就不多解释了:
<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/iv_user_fund_user_head" android:layout_width="80dp" android:layout_height="80dp" fresco:roundAsCircle="true" // 圆形图片。 fresco:roundingBorderColor="@color/white" // 白色描边。 fresco:roundingBorderWidth="2dp"/> // 描边宽度。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
第二个,Fresco高斯模糊:
/** * 以高斯模糊显示。 * * @param draweeView View。 * @param url url. * @param iterations 迭代次数,越大越魔化。 * @param blurRadius 模糊图半径,必须大于0,越大越模糊。 */public static void showUrlBlur(SimpleDraweeView draweeView, String url, int iterations, int blurRadius) { try { Uri uri = Uri.parse(url); ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setPostprocessor(new IterativeBoxBlurPostProcessor(iterations, blurRadius)) .build(); AbstractDraweeController controller = Fresco.newDraweeControllerBuilder() .setOldController(draweeView.getController()) .setImageRequest(request) .build(); draweeView.setController(controller); } catch (Exception e) { e.printStackTrace(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
效果就这样子,其它的关于进度条子类的自己去试试吧,我就不演示占篇幅了。
拿到缓存的Bitmap
有几次在群里讨论关于Fresco的问题,很多同学吐槽Fresco没有直接拿到Bitmap
的方法,其实直接拿到Bitmap
相当于从SD卡读取一个文件,如果图片过大,就会耗时,造成App卡(假)死,所以我们要采用异步的方式:
/** * 加载图片成bitmap。 * * @param imageUrl 图片地址。 */public static void loadToBitmap(String imageUrl, BaseBitmapDataSubscriber mDataSubscriber) { ImageRequest imageRequest = ImageRequestBuilder .newBuilderWithSource(Uri.parse(imageUrl)) .setProgressiveRenderingEnabled(true) .build(); ImagePipeline imagePipeline = Fresco.getImagePipeline(); DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage (imageRequest, App.get()); dataSource.subscribe(mDataSubscriber, CallerThreadExecutor.getInstance());}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
用起来也很简单:
loadToBitmap(imageUrl, new BaseBitmapDataSubscriber() { @Override public void onNewResultImpl(@Nullable Bitmap bitmap) { // 读取成功。 } @Override public void onFailureImpl(DataSource dataSource) { // 读取失败。 }});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
今天的技术篇就到这里吧,大概是1.30分了,睡觉。
版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com
<link rel="stylesheet" href="http://csdnimg.cn/release/phoenix/production/markdown_views-d4dade9c33.css"> </div>
- 一篇不错的Fresco文章
- 一篇不错的文章
- 一篇不错的文章
- 一篇不错的文章
- 看到一篇不错的文章
- 转载一篇不错的文章
- 一篇不错的励志文章
- GEF 一篇不错的文章
- 一篇不错的面试文章
- 一篇不错的shiro文章
- 转的一篇不错的文章。
- 内存对齐的一篇不错的文章
- AOP介绍的一篇不错的文章
- 一篇介绍mina的不错的文章
- 一篇不错的了解Backbone的文章
- 一篇不错的git fork的文章。
- 《慈父家训》-一篇不错的文章
- Log4j的一篇文章,还不错
- Maven——简介、安装(win10x64)
- poj2104 K-th Number (主席树无修改)
- Servlet运行原理及404、500、405异常原因和解决方法总结
- 不值一提的计算机基础教程-3-处理器
- spring 基础上
- 一篇不错的Fresco文章
- 醉话没有测试(QA)的测试
- 「12306奇葩验证码」反例背后的产品观
- 移动端界面中的版式设计原理
- 产品经理不得不知的APP数据分析&报表设计基础
- 八数码第六境界——双向BFS+康托展开判重+回溯记录路径+逆序数判无解
- 户外私密Party| 在大峡谷中聊点平时不能聊的产品干货(报名结束)
- 《利用Python进行数据分析》第5章 pandas的数据汇总与处理缺失数据
- java8之list集合中取出某一属性的方法