Transparent blurry view which blurs layout underneath

后端 未结 5 508
[愿得一人]
[愿得一人] 2020-11-30 18:27

I\'ve got a Linearlayout which I\'ve made transparent, and now I\'m looking for a way to give it a Blur effect, so what\'s ever underneath it gets blurry. Just like the Wind

5条回答
  •  我在风中等你
    2020-11-30 19:24

    This was on my mind for some time, and I just implemented it thanks to your question.

    To be able to do this, we need to draw the layout that is beneath our blur layout into a bitmap. Than by using a blurring algorithm, we need to blur that bitmap and finally draw blurred bitmap as our blur layout's background.

    Luckily android has cached drawing mechanism, so first part is easy. We can simply enable cached drawing for our beneath layout and use getDrawingCache() to acquire the bitmap from it.

    Now we need a fast blurring algorithm. I used this https://stackoverflow.com/a/10028267/3133545

    Here it is.

    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.ColorFilter;
    import android.graphics.Paint;
    import android.graphics.PixelFormat;
    import android.graphics.drawable.Drawable;
    import android.util.Log;
    import android.view.View;
    
    import java.lang.ref.WeakReference;
    import java.util.InputMismatchException;
    
    /**
     * A drawable that draws the target view as blurred using fast blur
     * 

    *

    * TODO:we might use setBounds() to draw only part a of the target view *

    * Created by 10uR on 24.5.2014. */ public class BlurDrawable extends Drawable { private WeakReference targetRef; private Bitmap blurred; private Paint paint; private int radius; public BlurDrawable(View target) { this(target, 10); } public BlurDrawable(View target, int radius) { this.targetRef = new WeakReference(target); setRadius(radius); target.setDrawingCacheEnabled(true); target.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_AUTO); paint = new Paint(); paint.setAntiAlias(true); paint.setFilterBitmap(true); } @Override public void draw(Canvas canvas) { if (blurred == null) { View target = targetRef.get(); if (target != null) { Bitmap bitmap = target.getDrawingCache(true); if (bitmap == null) return; blurred = fastBlur(bitmap, radius); } } if (blurred != null && !blurred.isRecycled()) canvas.drawBitmap(blurred, 0, 0, paint); } /** * Set the bluring radius that will be applied to target view's bitmap * * @param radius should be 0-100 */ public void setRadius(int radius) { if (radius < 0 || radius > 100) throw new InputMismatchException("Radius must be 0 <= radius <= 100 !"); this.radius = radius; if (blurred != null) { blurred.recycle(); blurred = null; } invalidateSelf(); } public int getRadius() { return radius; } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter cf) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } /** * from https://stackoverflow.com/a/10028267/3133545 *

    *

    *

    * Stack Blur v1.0 from * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html *

    * Java Author: Mario Klingemann * http://incubator.quasimondo.com * created Feburary 29, 2004 * Android port : Yahel Bouaziz * http://www.kayenko.com * ported april 5th, 2012 *

    * This is a compromise between Gaussian Blur and Box blur * It creates much better looking blurs than Box Blur, but is * 7x faster than my Gaussian Blur implementation. *

    * I called it Stack Blur because this describes best how this * filter works internally: it creates a kind of moving stack * of colors whilst scanning through the image. Thereby it * just has to add one new block of color to the right side * of the stack and remove the leftmost color. The remaining * colors on the topmost layer of the stack are either added on * or reduced by one, depending on if they are on the right or * on the left side of the stack. *

    * If you are using this algorithm in your code please add * the following line: *

    * Stack Blur Algorithm by Mario Klingemann */ private static Bitmap fastBlur(Bitmap sentBitmap, int radius) { Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); if (radius < 1) { return (null); } int w = bitmap.getWidth(); int h = bitmap.getHeight(); int[] pix = new int[w * h]; Log.e("pix", w + " " + h + " " + pix.length); bitmap.getPixels(pix, 0, w, 0, 0, w, h); int wm = w - 1; int hm = h - 1; int wh = w * h; int div = radius + radius + 1; int r[] = new int[wh]; int g[] = new int[wh]; int b[] = new int[wh]; int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; int vmin[] = new int[Math.max(w, h)]; int divsum = (div + 1) >> 1; divsum *= divsum; int dv[] = new int[256 * divsum]; for (i = 0; i < 256 * divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int[][] stack = new int[div][3]; int stackpointer; int stackstart; int[] sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum; int rinsum, ginsum, binsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = pix[yi + Math.min(wm, Math.max(i, 0))]; sir = stack[i + radius]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rbs = r1 - Math.abs(i); rsum += sir[0] * rbs; gsum += sir[1] * rbs; bsum += sir[2] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (y == 0) { vmin[x] = Math.min(x + radius + 1, wm); } p = pix[yw + vmin[x]]; sir[0] = (p & 0xff0000) >> 16; sir[1] = (p & 0x00ff00) >> 8; sir[2] = (p & 0x0000ff); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; yp = -radius * w; for (i = -radius; i <= radius; i++) { yi = Math.max(0, yp) + x; sir = stack[i + radius]; sir[0] = r[yi]; sir[1] = g[yi]; sir[2] = b[yi]; rbs = r1 - Math.abs(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; if (x == 0) { vmin[y] = Math.min(y + r1, hm) * w; } p = x + vmin[y]; sir[0] = r[p]; sir[1] = g[p]; sir[2] = b[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; yi += w; } } bitmap.setPixels(pix, 0, w, 0, 0, w, h); return (bitmap); } }

    Usage :

    View beneathView = //the view that beneath blur view
    View blurView= //blur View
    
    BlurDrawable blurDrawable = new BlurDrawable(beneathView, radius);
    
    blurView.setBackgroundDrawable(blurDrawable);
    

    And how my test application looked like:

    test application

    I decided not to use this tho, because it is too hacky and not looking as cool as i thought it would be in first place.

提交回复
热议问题