Fresco实践总结-高斯模糊、圆形圆角、URL、File、Assets、Resource

标签: Fresco Fresco高斯模糊 Fresco加载本地 Fresco加载图片 Fresco特效
6087人阅读 评论(7) 收藏 举报
分类:

版权声明:转载必须注明本文转自严振杰的博客: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'

初始化

建议在App启动就初始化,所以建议写在Application#onCreate()中,记得在manifest.xml注册Application

一般初始化:

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Fresco.initialize(this);
    }
    ...

高级初始化-配置缓存文件夹

// 高级初始化:
Fresco.initialize(this, ImagePipelineConfig.newBuilder(App.this)
    .setMainDiskCacheConfig(
        DiskCacheConfig.newBuilder(this)
            .setBaseDirectoryPath(new File("SD卡路径")) // 注意Android运行时权限。
            .build()
    )
    .build()
);

上面的高级初始化当然不止这么一点点,这里举出一个敏感的例子,就是配置缓存SD卡路径,这里涉及到Android6.0运行时权限,我也给一个解决方案,我使用的权限管理库是AndPermissionhttps://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;
    }
}

然后在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();
        }
    };
}

高级初始化-配置网络层为OkHttp

Fresco默认使用HttpURLConnection作为网络层,当然也可以配置OkHttp作为它的网络层,配置OkHttp为它的网络层需要依赖下面的库:

compile "com.facebook.fresco:imagepipeline-okhttp3:0.12.0+"

然后在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()
    );
}

这里只需要注意原来的ImagePipelineConfig换成了OkHttpImagePipelineConfigFactory,并且需要一个OkHttpClient的对象。

加载网络图片、url、assets、res、本地File图片

先给出支持的URI格式列表(列表来自fresco-cn.org):

Type Scheme Sample
http远程图片 http://或者https:// HttpURLConnection或者OkHttp
本地文件 file:// FileInputStream
Content provider content:// ContentResolver
res目录下的资源 res:// Resources.openRawResource
asset目录下的资源 asset:// AssetManager
Uri中指定图片数据 data:mime/type;base64, 数据类型必须符合rfc2397规定 (仅支持 UTF-8)

SimpleDraweeView

实际开发中,如果没有特殊需求,我们一般使用SimpleDraweeView来占位,传统的图片加载框架一般是使用ImageView,例如:

<ImageView
.../>

在使用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"/>

把它当成我们平常使用的ImageView即可,不过我们要注意Fresco不支持wrap_content(具体原因看这里),需要使用match_parent或者显示指定view宽高,有些同学看到这个就很头疼了,但是这真的不是什么问题,下面给出解决方案:

一、由服务器返回URL时返回图片的宽高信息
平常我们服务器这样返回图片url的:

{
    "name":"严振杰",
    "head":"http://www.yanzhenjie.com/images/main/yzj_head.png"
}

使用Fresco我们可以把一个图片url当成一个对象包裹起来:

{
    "name":"严振杰",
    "head": 
        {
            "url":"http://www.yanzhenjie.com/images/main/yzj_head.png",
            "width":"500",
            "height":"500"
        }
}

或者在URL后面跟一个宽高的参数:

{
    "name":"严振杰",
    "head":"http://www.yanzhenjie.com/images/main/yzj_head.png?width=500&height=500"
}

二、根据设计师的给的尺寸,预先设置图片宽高
设计师设计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);
}

基础配置和注意的地方讲完了,那么下面就是重头戏了,如何加载图片。

一、加载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();
    }
}

二、显示本地图片

这里就有个坑了,先看一下下面我写了两个方法,一个需要传入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();
    }
}

三、显示res中图片

这里要注意,我们在为res中的图片生成Uri的时候:

Uri uri = Uri.parse("res://包名(任何字符串或者留空)/" + R.drawable.ic_launcher);

所以我们一般留空,因此我们的代码看起来是下面的样子:

/**
 * 显示一个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();
    }
}

四、显示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();
    }
}

五、显示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();
    }
}

你以为到这里就完了吗?并没有,继续看。

一些默认属性的设置

<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" 描边的颜色。
/>

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 ...

一些特殊效果

第一个,先来一个圆形图片带白色的边:

圆形带有描边

综合上面的属性这里就不多解释了:

<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"/> // 描边宽度。

第二个,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();
    }
}

效果就这样子,其它的关于进度条子类的自己去试试吧,我就不演示占篇幅了。

拿到缓存的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());
}

用起来也很简单:

loadToBitmap(imageUrl, new BaseBitmapDataSubscriber() {
    @Override
    public void onNewResultImpl(@Nullable Bitmap bitmap) {
        // 读取成功。
    }

    @Override
    public void onFailureImpl(DataSource dataSource) {
        // 读取失败。
    }
});

今天的技术篇就到这里吧,大概是1.30分了,睡觉。


版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

查看评论

Android图片加载神器之Fresco,基于各种使用场景的讲解

转载请标明出处:http://blog.csdn.net/android_ls/article/details/53137867Fresco是Facebook开源Android平台上一个强大的图片加载...
  • android_ls
  • android_ls
  • 2016-11-12 18:13:06
  • 20274

Fresco的解析和使用

1.概念介绍 Fresco是Facebook开源Android平台上一个强大的图片加载库,也是迄今为止Android平台上最强大的图片加载库。 优点:相对于其他开源的第三方图片加载库,Fres...
  • jxf_access
  • jxf_access
  • 2018-01-18 19:14:00
  • 154

Fresco 5.0以上内存持续增长问题优化

fresco是android一款比较好的图片处理框架,特别是在5.0以下,效果很佳。在5.0以下系统,Fresco将图片放到一个特别的内存区域ashmem中。这块内存我们通过android studi...
  • tsdfk1455
  • tsdfk1455
  • 2017-03-24 13:29:28
  • 4281

android fresco 详解

Android图片加载神器之Fresco-加载图片基础[详细图解Fresco的使用] 标签: androidFrescoSimpleDraw加载图片 2015-10-19 08:13 11...
  • rongbinjava
  • rongbinjava
  • 2016-07-06 10:08:36
  • 2261

Fresco的使用<一>

引入Frescodependencies { // 添加依赖 compile 'com.facebook.fresco:fresco:0.12.0' }开始使用 Fresco// 进行全局初始...
  • Konfyt_Android
  • Konfyt_Android
  • 2016-09-16 19:34:26
  • 1239

Fresco介绍:Android的一个新图片库

翻译自:https://code.facebook.com/posts/366199913563917 快速有效的展示图片对Facebook Android客户端非常重要。可是该团队多年来在有效...
  • lufqnuli
  • lufqnuli
  • 2016-05-30 16:43:39
  • 3597

Android Fresco图片框架内部实现原理探索

流行的网络框架 目前流行的网络图片框架:Picasso、Universal Image Loader、Volley的(ImageLoader、NetworkImageView)、Glide和Fr...
  • zhengjiqingxue
  • zhengjiqingxue
  • 2016-08-04 15:31:10
  • 1241

Fresco原理分析

对于Fresco大家在网上搜索看到的大多数是这篇翻译的文章,英语好的同学,可以直接看原版,因为我觉得翻译的不是很好( http://www.jcodecraeer.com/a/anzhuokaifa/...
  • fu_xiuyuan
  • fu_xiuyuan
  • 2015-07-12 09:39:13
  • 5277

android 初识Fresco

Fresco是facebook推出的一款强大的图片加载的框架,这个框架出来一段时间了,前一段时间使用过了ImageLoader的框架,生命在于折腾,今天就来折腾一下Fresco这个强大的框架    ...
  • ElinaVampire
  • ElinaVampire
  • 2015-07-27 09:32:25
  • 5537

Fresco进行图片的下载,高斯模糊

使用fresco进行图片的预下载,下载转存,以及对图片进行高斯模糊 activity:package com.fanyafeng.frescopicload.activity;import andr...
  • qq_23195583
  • qq_23195583
  • 2016-12-12 15:06:03
  • 1852
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 169万+
    积分: 6702
    排名: 4404
    推荐
    欢迎关注我的公众号
    博客专栏