美文网首页Android
Android 图片相关整理

Android 图片相关整理

作者: 杨充211 | 来源:发表于2018-07-05 18:49 被阅读57次

    目录介绍

    • 0.思考问题及解决方案
    • 1.加载图片的压缩处理技术
    • 2.网络url图片转换Bitmap保存到本地
      • 2.1 直接通过http请求网络图片通过流转化成Bitmap
      • 2.2 使用第三方库glide将网络图片转化为Bitmap
    • 3.保存bitmap图片到本地文件夹
    • 4.实现带有圆角的图片
      • 4.1 使用glide处理图片圆角的逻辑
      • 4.2 自定义带有圆角的ImageView
    • 5.毫无满仓轮播图背景做高斯模糊
      • 5.1 高斯模糊实现原理
      • 5.2 高斯模糊实现的代码
      • 5.3 高斯模糊可能会造成的崩溃
      • 5.4 高斯模糊参考案例

    关于链接

    0.思考问题及解决方案

    • 0.1.0 图片压缩的技术是什么,原理如何理解?
    • 0.1.1 为什么保存图片,切割图片圆角需要将图片转化成bitmap?
    • 0.1.2 对于从网络下载图片,可以采用什么方式?为什么glide相比从网络直接请求更加高效?
    • 0.1.3 图片背景滑动高斯模糊的原理是什么,是否影响性能?
    • 0.1.4 bitmap如何避免被回收?如果回收了,怎么避免出现使用bitmap崩溃
    • 0.1.5 为什么设置高斯模糊需要放在子线程中处理,不这样做会有什么影响?

    1.加载图片的压缩处理技术

    1.1 压缩技术步骤

    • 1.1.1 科学计算图片所需的采样比例
    • 1.1.2 设置图片加载的渲染模式为Config.RGB_565,能降低一半内存
    • 1.1.3 对bitmap进行质量压缩

    1.2 代码如下所示

    /**
     * 根据路径获得突破并压缩返回bitmap用于显示
     * @return          Bitmap
     */
    public static Bitmap getSmallBitmap(String filePath, int newWidth, int newHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        //设置只解析图片的边界参数,即宽高
        options.inJustDecodeBounds = true;
        //options.inSampleSize = 2;
        BitmapFactory.decodeFile(filePath, options);
        // Calculate inSampleSize
        //科学计算图片所需的采样比例
        options.inSampleSize = calculateInSampleSize(options, newWidth, newHeight);
        //设置图片加载的渲染模式为Config.RGB_565,能降低一半内存,但是会影响图片质量
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        // Decode bitmap with inSampleSize set
        //关闭标记,解析真实的图片
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
        //质量压缩
        Bitmap newBitmap = compressImage(bitmap, 500);
        if (bitmap != null){
            bitmap.recycle();
        }
        return newBitmap;
    }
    
    
    /**
     * 计算图片的缩放值
     */
    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            // Calculate ratios of height and width to requested height and
            // width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            // Choose the smallest ratio as inSampleSize value, this will
            // guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }
    
    /**
     * 质量压缩
     * @param image
     * @param maxSize
     */
    private static Bitmap compressImage(Bitmap image, int maxSize){
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // scale
        int options = 80;
        // Store the bitmap into output stream(no compress)
        image.compress(Bitmap.CompressFormat.JPEG, options, os);
        // Compress by loop
        while ( os.toByteArray().length / 1024 > maxSize) {
            // Clean up os
            os.reset();
            // interval 10
            options -= 10;
            image.compress(Bitmap.CompressFormat.JPEG, options, os);
        }
        Bitmap bitmap = null;
        byte[] b = os.toByteArray();
        if (b.length != 0) {
            bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
        }
        return bitmap;
    }
    

    2.网络url图片转换Bitmap保存到本地

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

    • 2.1.1 直接通过网络请求将网络图片转化成bitmap
      • 经过测试,请求8张图片,耗时毫秒值174
      • 如果是服务器响应速度一般,耗时需要2秒【正式接口】
    /**
     * 请求网络图片转化成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;
    }
    

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

    /**
     * 请求网络图片转化成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
                    }
                });
    }
    

    3.保存bitmap图片到本地文件夹

    /**
     * 保存图片至自定义路径,刷新相册
     */
    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);
    }
    

    4.实现带有圆角的图片

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

    /**
     * 加载带有圆角的矩形图片  用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

    5.毫无满仓轮播图背景做高斯模糊

    5.1 高斯模糊实现原理

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

    5.2 高斯模糊实现的代码

    • 5.2.1 设置高斯模糊代码
    /**
     * 设置模糊背景
     */
    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);
        }
    }
    
    • 5.2.2 RenderScript图片高斯模糊
    /**
     * 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;
        }
    
    }
    
    • 5.2.3 设置高斯模糊背景View动画过渡效果
    /**
     * 图片背景切换动画帮助类,设置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);
        }
    }
    

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

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

    5.4 高斯模糊参考案例

    关于我的博客

    相关文章

      网友评论

        本文标题:Android 图片相关整理

        本文链接:https://www.haomeiwen.com/subject/wmxmuftx.html