Android 做高斯模糊背景时,过渡的地方要求平滑的解决方案

拿到一个小说详情页的设计稿,顶部要用小说的封面图做高斯模糊。然后在与下半部分界面过渡时,要保持平滑过渡。一般像小说的封面和音乐的专辑封面之类的图片,拿到的图片深色系、浅色系的都会有,以及各种排版的图片都有,要保障所有图片显示都没问题。写篇文章,记录下实现过程。

此处需要平滑过渡
小说详情页设计稿

一开始,尝试用图片直接做高斯模糊。但是会发现边缘处会有明显的分割线。很明显做不到,需求需要的过渡平滑。
最后选择的解决方案如下:

  1. 将封面图用代码,直接裁剪一部分。
    Bitmap coverBitmap = BitmapUtils.cropBitmap(bitmap);
    /**
     * 裁剪中间
     *
     * @param srcBmp 原图
     * @return 裁剪后的图像
     */
    public static Bitmap cropBitmap(Bitmap srcBmp) {
        Bitmap dstBmp;
        if (srcBmp.getWidth() >= srcBmp.getHeight()){

            dstBmp = Bitmap.createBitmap(
                    srcBmp,
                    srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
                    0,
                    srcBmp.getHeight(),
                    srcBmp.getHeight()
            );

        }else{

            dstBmp = Bitmap.createBitmap(
                    srcBmp,
                    0,
                    srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
                    srcBmp.getWidth(),
                    srcBmp.getWidth()
            );
        }
        return dstBmp;
    }
  1. 将一个纯白色的长方形图(用代码生成也可以),拿到的 bitmap 与用封面图裁剪的图,做上下拼接。
whiteRectBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.white_rect)
newBitmap = BitmapUtils.combineImage(coverBitmap, whiteRectBitmap);
    /**
     * 拼接图片
     *
     * @param bitmaps 原图片集
     * @return  拼接后的新图
     */
    public static Bitmap combineImage(Bitmap... bitmaps) {
        boolean isMultiWidth = false;//是否为多宽度图片集
        int width = 0;
        int height = 0;

        //获取图纸宽度
        for (Bitmap bitmap : bitmaps) {
            if (width != bitmap.getWidth()) {
                if (width != 0) {//过滤掉第一次不同
                    isMultiWidth = true;
                }
                width = width < bitmap.getWidth() ? bitmap.getWidth() : width;
            }
        }

        //获取图纸高度
        for (Bitmap bitmap : bitmaps) {
            if (isMultiWidth) {
                height = height + bitmap.getHeight() * width / bitmap.getWidth();
            } else {
                height = height + bitmap.getHeight();
            }
        }

        //创建图纸
        Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        //创建画布,并绑定图纸
        Canvas canvas = new Canvas(newBitmap);
        int tempHeight = 0;
        //画图
        for (int i = 0; i < bitmaps.length; i++) {
            if (isMultiWidth) {
                if (width != bitmaps[i].getWidth()) {
                    int newSizeH = bitmaps[i].getHeight() * width / bitmaps[i].getWidth();
                    Bitmap newSizeBmp = resizeBitmap(bitmaps[i], width, newSizeH);
                    canvas.drawBitmap(newSizeBmp, 0, tempHeight, null);
                    tempHeight = tempHeight + newSizeH;
                    newSizeBmp.recycle();
                } else {
                    canvas.drawBitmap(bitmaps[i], 0, tempHeight, null);
                    tempHeight = tempHeight + bitmaps[i].getHeight();
                }
            } else {
                canvas.drawBitmap(bitmaps[i], 0, tempHeight, null);
                tempHeight = tempHeight + bitmaps[i].getHeight();
            }
            bitmaps[i].recycle();
        }
        return newBitmap;
    }

    public static Bitmap resizeBitmap(Bitmap bitmap, int newWidth, int newHeight) {
        float scaleWidth = ((float) newWidth) / bitmap.getWidth();
        float scaleHeight = ((float) newHeight) / bitmap.getHeight();
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap bmpScale = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return bmpScale;
    }
  1. 将拼接后的图片,进行高斯模糊。
    /**
     * 高斯模糊
     * @param context
     * @param source
     * @param radius
     * @return
     */
    public static Bitmap rsBlur(Context context,Bitmap source,int radius){
        Bitmap inputBmp = source;
        RenderScript renderScript =  RenderScript.create(context);
        // Allocate memory for Renderscript to work with
        final Allocation input = Allocation.createFromBitmap(renderScript,inputBmp);
        final Allocation output = Allocation.createTyped(renderScript,input.getType());
        // Load up an instance of the specific script that we want to use.
        ScriptIntrinsicBlur scriptIntrinsicBlur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        scriptIntrinsicBlur.setInput(input);
        // Set the blur radius
        scriptIntrinsicBlur.setRadius(radius);
        // Start the ScriptIntrinisicBlur
        scriptIntrinsicBlur.forEach(output);
        // Copy the output to the blurred bitmap
        output.copyTo(inputBmp);
        renderScript.destroy();
        return inputBmp;
    }
  1. 最后,显示得到的高斯模糊后的图片,ImageView 可以加 android:alpha="0.3" 的透明度。
    /**
     * 显示顶部高斯模糊图片的背景
     * @param bitmap
     */
    private void showCoverBlur(Bitmap bitmap) {
        Bitmap coverBitmap = BitmapUtils.cropBitmap(bitmap);
        Bitmap whiteRectBitmap = BitmapUtils.drawable2Bitmap(getResources().getDrawable(R.drawable.white_rect));
        Bitmap newBitmap = BitmapUtils.combineImage(coverBitmap, whiteRectBitmap);

        Bitmap blurBitmap = BitmapUtils.rsBlur(BookDetailActivity.this, newBitmap, 25);
        iv_white_rect_blur.setImageBitmap(blurBitmap);
    }


5.记得在onDestory里清空bitmap

    private void recycleBitmap(){
        if (coverBitmap != null && !coverBitmap.isRecycled()) {
            coverBitmap = null;
        }
        if (newBitmap != null && !newBitmap.isRecycled()) {
            newBitmap = null;
        }
        if (whiteRectBitmap != null && !whiteRectBitmap.isRecycled()) {
            whiteRectBitmap = null;
        }
        if (blurBitmap != null && !blurBitmap.isRecycled()) {
            blurBitmap = null;
        }
    }

最后,显示的效果和设计稿想要的效果基本上是一致的。如果你也遇到类似需求,有其他更好的解决方案,欢迎留言交流。

你可能感兴趣的:(Android 做高斯模糊背景时,过渡的地方要求平滑的解决方案)