Android 图片相关整理

01.网络请求图片资源

1.1 直接通过http请求网络图片通过流转化成Bitmap

  • 经过测试,请求8张图片,耗时毫秒值174。如果是服务器响应速度一般,耗时需要2秒【正式接口】。

    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
    39
    40
    41
    42
    43
    /**
    * 请求网络图片转化成bitmap
    * @param url url
    * @return 将url图片转化成bitmap对象
    */
    private static long time = 0;
    public static Bitmap returnBitMap(String url) {
    long l1 = System.currentTimeMillis();
    URL myFileUrl = null;
    Bitmap bitmap = null;
    HttpURLConnection conn = null;
    InputStream is = null;
    try {
    myFileUrl = new URL(url);
    } catch (MalformedURLException e) {
    e.printStackTrace();
    }
    try {
    conn = (HttpURLConnection) myFileUrl.openConnection();
    conn.setConnectTimeout(10000);
    conn.setReadTimeout(5000);
    conn.setDoInput(true);
    conn.connect();
    is = conn.getInputStream();
    bitmap = BitmapFactory.decodeStream(is);
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    try {
    if (is != null) {
    is.close();
    conn.disconnect();
    }
    } catch (IOException e) {
    e.printStackTrace();
    }
    long l2 = System.currentTimeMillis();
    time = (l2-l1) + time;
    LogUtils.e("毫秒值"+time);
    //保存
    }
    return bitmap;
    }

1.2 使用第三方库glide将网络图片转化为Bitmap

  • 代码如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 请求网络图片转化成bitmap
    */
    private static long times = 0;
    public static void glideBitmap(Context context,String url){
    final long l1 = System.currentTimeMillis();
    Glide.with(context)
    .load(url)
    .asBitmap()
    //设置缓存
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(new SimpleTarget<Bitmap>() {
    @Override
    public void onResourceReady(Bitmap resource,
    GlideAnimation<? super Bitmap> glideAnimation) {
    long l2 = System.currentTimeMillis();
    times = (l2-l1) + times;
    LogUtils.e("毫秒值"+times);
    //请求8张图片,耗时毫秒值98
    }
    });
    }

02.保存bitmap图片

  • 保存bitmap图片到本地文件夹,具体操作如下所示

    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
    /**
    * 保存图片至自定义路径,刷新相册
    */
    public static void saveImageToFile(Context context, Bitmap bmp) {
    // 首先保存图片,这个路径可以自定义
    File appDir = new File(Environment.getExternalStorageDirectory(), "yc");
    // 测试由此抽象路径名表示的文件或目录是否存在
    if (!appDir.exists()) {
    //如果不存在,则创建由此抽象路径名命名的目录
    //noinspection ResultOfMethodCallIgnored
    appDir.mkdir();
    }
    // 然后自定义图片的文件名称
    String fileName = System.currentTimeMillis() + ".jpg";
    // 创建file对象
    File file = new File(appDir, fileName);
    try {
    FileOutputStream fos = new FileOutputStream(file);
    bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
    fos.flush();
    fos.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    // 其次把文件插入到系统图库
    try {
    MediaStore.Images.Media.insertImage(context.getContentResolver(),
    file.getAbsolutePath(), fileName, null);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    }
    // 最后通知图库更新
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    intent.setData(Uri.parse("file://" + file.getAbsoluteFile()));
    context.sendBroadcast(intent);
    }

03.实现带有圆角的图片

3.1 使用glide处理图片圆角的逻辑

  • 如下所示

    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
    /**
    * 加载带有圆角的矩形图片 用glide处理
    *
    * @param path 路径
    * @param round 圆角半径
    * @param resId 加载失败时的图片
    * @param target 控件
    */
    public static void loadImgByPicassoWithRound(final Context activity, String path,
    final int round, int resId, final ImageView target) {
    if (path != null && path.length() > 0) {
    Glide.with(activity)
    .load(path)
    .asBitmap()
    .placeholder(resId)
    .error(resId)
    //设置缓存
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(new BitmapImageViewTarget(target) {
    @Override
    protected void setResource(Bitmap resource) {
    super.setResource(resource);
    RoundedBitmapDrawable circularBitmapDrawable = RoundedBitmapDrawableFactory
    .create(activity.getResources(), resource);
    //设置绘制位图时要应用的角半径
    circularBitmapDrawable.setCornerRadius(round);
    target.setImageDrawable(circularBitmapDrawable);
    }
    });
    }
    }

4.2 自定义带有圆角的ImageView

  • 使用BitmapShader实现圆形、圆角图片

04.如何高斯模糊

4.1 高斯模糊实现原理

  • 前沿【摘自网络】:在Android平台上进行模糊渲染是一个相当耗CPU也相当耗时的操作,一旦处理不好,卡顿是在所难免的。考虑到效率,渲染一张图片最好的方法是使用OpenGL,其次是使用C++/C,使用Java代码是最慢的。但是Android推出RenderScript之后,我们就有了新的选择,测试表明,使用RenderScript的渲染效率和使用C/C++不相上下,但是使用RenderScript却比使用JNI简单地多!
  • 原理步骤如下所示:
    • a.压缩图片,可以质量压缩,也可以宽高压缩
    • b.创建RenderScript内核对象
    • c.创建一个模糊效果的RenderScript的工具对象
    • d.设置相关参数,具体看代码……
  • 实现思路
    • 先将图片进行最大程度的模糊处理,再将原图放置在模糊后的图片上面,通过不断改变原图的透明度(Alpha值)来实现动态模糊效果。

4.2 高斯模糊实现的代码

  • 设置高斯模糊代码

    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
    /**
    * 设置模糊背景
    */
    private void setBlurBackground(int pos) {
    //获取轮播图索引pos处的图片
    Integer integer = pagerAdapter.getBitmapHashMap().get(pos);
    Resources res = this.getResources();
    Bitmap bitmap= BitmapFactory.decodeResource(res, integer);
    //压缩图片
    final Bitmap image = BitmapUtils.compressImage(bitmap);
    if (bitmap != null) {
    if (mBlurRunnable != null) {
    mIvBlurBackground.removeCallbacks(mBlurRunnable);
    }
    mBlurRunnable = new Runnable() {
    @Override
    public void run() {
    //压缩图片,宽高缩放
    Bitmap blurBitmap = BlurBitmapUtils.getBlurBitmap(
    mIvBlurBackground.getContext(), image, 15);
    ViewSwitchUtils.startSwitchBackgroundAnim(mIvBlurBackground, blurBitmap);
    }
    };
    mIvBlurBackground.postDelayed(mBlurRunnable, 100);
    }
    }
  • RenderScript图片高斯模糊

    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
    39
    40
    41
    42
    /**
    * RenderScript图片高斯模糊
    */
    public class BlurBitmapUtils {

    /**
    * 建议模糊度(在0.0到25.0之间)
    */
    private static final int SCALED_WIDTH = 100;
    private static final int SCALED_HEIGHT = 100;

    /**
    * 得到模糊后的bitmap
    * @param context 上下文
    * @param bitmap bitmap
    * @param radius 半径
    * @return
    */
    public static Bitmap getBlurBitmap(Context context, Bitmap bitmap, int radius) {
    // 将缩小后的图片做为预渲染的图片。
    Bitmap inputBitmap = Bitmap.createScaledBitmap(bitmap, SCALED_WIDTH, SCALED_HEIGHT, false);
    // 创建一张渲染后的输出图片。
    Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
    // 创建RenderScript内核对象
    RenderScript rs = RenderScript.create(context);
    // 创建一个模糊效果的RenderScript的工具对象
    ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
    // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间。
    // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去。
    Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
    Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
    // 设置渲染的模糊程度, 25f是最大模糊度
    blurScript.setRadius(radius);
    // 设置blurScript对象的输入内存
    blurScript.setInput(tmpIn);
    // 将输出数据保存到输出内存中
    blurScript.forEach(tmpOut);
    // 将数据填充到Allocation中
    tmpOut.copyTo(outputBitmap);
    return outputBitmap;
    }
    }
  • 设置高斯模糊背景View动画过渡效果

    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
    /**
    * 图片背景切换动画帮助类,设置View动画
    */
    public class ViewSwitchUtils {

    static void startSwitchBackgroundAnim(ImageView view, Bitmap bitmap) {
    Drawable oldDrawable = view.getDrawable();
    Drawable oldBitmapDrawable ;
    TransitionDrawable oldTransitionDrawable = null;
    if (oldDrawable instanceof TransitionDrawable) {
    oldTransitionDrawable = (TransitionDrawable) oldDrawable;
    oldBitmapDrawable = oldTransitionDrawable.findDrawableByLayerId(oldTransitionDrawable.getId(1));
    } else if (oldDrawable instanceof BitmapDrawable) {
    oldBitmapDrawable = oldDrawable;
    } else {
    oldBitmapDrawable = new ColorDrawable(0xffc2c2c2);
    }

    if (oldTransitionDrawable == null) {
    oldTransitionDrawable = new TransitionDrawable(new Drawable[]{oldBitmapDrawable, new BitmapDrawable(bitmap)});
    oldTransitionDrawable.setId(0, 0);
    oldTransitionDrawable.setId(1, 1);
    oldTransitionDrawable.setCrossFadeEnabled(true);
    view.setImageDrawable(oldTransitionDrawable);
    } else {
    oldTransitionDrawable.setDrawableByLayerId(oldTransitionDrawable.getId(0), oldBitmapDrawable);
    oldTransitionDrawable.setDrawableByLayerId(oldTransitionDrawable.getId(1), new BitmapDrawable(bitmap));
    }
    oldTransitionDrawable.startTransition(1000);
    }
    }

4.3 高斯模糊可能会造成的崩溃

  • 崩溃日志
    • 开发回收bitmap引发Canvas: trying to use a recycled bitmap错误处理
  • 抛该异常的原因分析
    • 如果代码已经不再需要使用Bitmap对象了,就可以释放了。释放内存以后,就不能再使用该Bitmap对象了,如果再次使用,就会抛出异常。所以一定要保证不再使用的时候释放。
  • 解决该问题的办法
    • 使用缓存