package hanks.com.guideview; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.support.v8.renderscript.Allocation; import android.support.v8.renderscript.Element; import android.support.v8.renderscript.RenderScript; import android.support.v8.renderscript.ScriptIntrinsicBlur; import android.util.AttributeSet; import android.view.View; /** * A custom view for presenting a dynamically blurred version of another view's content. * <p> * Use {@link #setBlurredView(View)} to set up the reference to the view to be blurred. * After that, call {@link #invalidate()} to trigger blurring whenever necessary. */ public class BlurringView extends View { public BlurringView(Context context) { this(context, null); } public BlurringView(Context context, AttributeSet attrs) { super(context, attrs); final Resources res = getResources(); final int defaultBlurRadius = res.getInteger(R.integer.default_blur_radius); final int defaultDownsampleFactor = res.getInteger(R.integer.default_downsample_factor); final int defaultOverlayColor = res.getColor(R.color.default_overlay_color); initializeRenderScript(context); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PxBlurringView); setBlurRadius(a.getInt(R.styleable.PxBlurringView_blurRadius, defaultBlurRadius)); setDownsampleFactor(a.getInt(R.styleable.PxBlurringView_downsampleFactor, defaultDownsampleFactor)); setOverlayColor(a.getColor(R.styleable.PxBlurringView_overlayColor, defaultOverlayColor)); a.recycle(); } public void setBlurredView(View blurredView) { mBlurredView = blurredView; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mBlurredView != null) { if (prepare()) { // If the background of the blurred view is a color drawable, we use it to clear // the blurring canvas, which ensures that edges of the child views are blurred // as well; otherwise we clear the blurring canvas with a transparent color. if (mBlurredView.getBackground() != null && mBlurredView.getBackground() instanceof ColorDrawable) { mBitmapToBlur.eraseColor(((ColorDrawable) mBlurredView.getBackground()).getColor()); } else { mBitmapToBlur.eraseColor(Color.TRANSPARENT); } mBlurredView.draw(mBlurringCanvas); blur(); canvas.save(); canvas.translate(mBlurredView.getX() - getX(), mBlurredView.getY() - getY()); canvas.scale(mDownsampleFactor, mDownsampleFactor); canvas.drawBitmap(mBlurredBitmap, 0, 0, null); canvas.restore(); } canvas.drawColor(mOverlayColor); } } public void setBlurRadius(int radius) { mBlurScript.setRadius(radius); } public void setDownsampleFactor(int factor) { if (factor <= 0) { throw new IllegalArgumentException("Downsample factor must be greater than 0."); } if (mDownsampleFactor != factor) { mDownsampleFactor = factor; mDownsampleFactorChanged = true; } } public void setOverlayColor(int color) { mOverlayColor = color; } private void initializeRenderScript(Context context) { mRenderScript = RenderScript.create(context); mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript)); } protected boolean prepare() { final int width = mBlurredView.getWidth(); final int height = mBlurredView.getHeight(); if (mBlurringCanvas == null || mDownsampleFactorChanged || mBlurredViewWidth != width || mBlurredViewHeight != height) { mDownsampleFactorChanged = false; mBlurredViewWidth = width; mBlurredViewHeight = height; int scaledWidth = width / mDownsampleFactor; int scaledHeight = height / mDownsampleFactor; // The following manipulation is to avoid some RenderScript artifacts at the edge. scaledWidth = scaledWidth - scaledWidth % 4 + 4; scaledHeight = scaledHeight - scaledHeight % 4 + 4; if (mBlurredBitmap == null || mBlurredBitmap.getWidth() != scaledWidth || mBlurredBitmap.getHeight() != scaledHeight) { mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); if (mBitmapToBlur == null) { return false; } mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888); if (mBlurredBitmap == null) { return false; } } mBlurringCanvas = new Canvas(mBitmapToBlur); mBlurringCanvas.scale(1f / mDownsampleFactor, 1f / mDownsampleFactor); mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType()); } return true; } protected void blur() { mBlurInput.copyFrom(mBitmapToBlur); mBlurScript.setInput(mBlurInput); mBlurScript.forEach(mBlurOutput); mBlurOutput.copyTo(mBlurredBitmap); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mRenderScript != null) { mRenderScript.destroy(); } } private int mDownsampleFactor; private int mOverlayColor; private View mBlurredView; private int mBlurredViewWidth, mBlurredViewHeight; private boolean mDownsampleFactorChanged; private Bitmap mBitmapToBlur, mBlurredBitmap; private Canvas mBlurringCanvas; private RenderScript mRenderScript; private ScriptIntrinsicBlur mBlurScript; private Allocation mBlurInput, mBlurOutput; }