自定义毛玻璃布局BlurLayout
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class BlurLayout extends FrameLayout {
public final static float SURFACE_VIEW_BITMAP_SCALE = 0.4f;
public final static float BLUR_RADIUS = 10;
private View activityView;
private SurfaceView surfaceView;
private SurfaceRender surfaceRender;
private Bitmap surfaceViewBitmap;
private final Canvas surfaceViewCanvas = new Canvas();
private final Matrix surfaceMatrix = new Matrix();
private boolean canvasDrawLock;
private boolean preDrawLock;
public BlurLayout(@NonNull Context context) {
this(context, null);
}
public BlurLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BlurLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
FrameLayout decorView = (FrameLayout) ((Activity) context).getWindow().getDecorView();
activityView = decorView.findViewById(android.R.id.content);
surfaceView = new SurfaceView(context);
surfaceRender = new DoubleCacheSurfaceRender(context);
surfaceView.getHolder().addCallback(surfaceRender);
getViewTreeObserver().addOnPreDrawListener(preDrawListener);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
surfaceView.setLayoutParams(lp);
addView(surfaceView, 0);
}
private final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
Log.i("BlurLayout", "onPreDraw");
if (preDrawLock) {
preDrawLock = false;
} else {
prepareBitmapAndPost();
}
return true;
}
};
@Override
protected void dispatchDraw(Canvas c) {
if (canvasDrawLock) {
return;
}
prepareBitmapAndPost();
super.dispatchDraw(c);
}
private void prepareBitmapAndPost() {
long begin = System.currentTimeMillis();
int surfaceWidth = surfaceView.getWidth();
int surfaceHeight = surfaceView.getHeight();
if (surfaceWidth == 0 || surfaceHeight == 0) {
return;
}
int scaledSurfaceWidth = (int) (surfaceWidth * SURFACE_VIEW_BITMAP_SCALE);
int scaledSurfaceHeight = (int) (surfaceHeight * SURFACE_VIEW_BITMAP_SCALE);
if (surfaceViewBitmap == null || scaledSurfaceWidth != surfaceViewBitmap.getWidth()
|| scaledSurfaceHeight != surfaceViewBitmap.getHeight()) {
surfaceViewBitmap = Bitmap.createBitmap(scaledSurfaceWidth, scaledSurfaceHeight, Bitmap.Config.ARGB_8888);
surfaceViewCanvas.setBitmap(surfaceViewBitmap);
}
if (!canvasDrawLock) {
canvasDrawLock = true;
surfaceMatrix.reset();
surfaceMatrix.preScale(SURFACE_VIEW_BITMAP_SCALE, SURFACE_VIEW_BITMAP_SCALE);
surfaceMatrix.postTranslate(-(getLeft() + getTranslationX()) * SURFACE_VIEW_BITMAP_SCALE,
-(getTop() + getTranslationY()) * SURFACE_VIEW_BITMAP_SCALE);
surfaceViewCanvas.setMatrix(surfaceMatrix);
surfaceViewCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
activityView.draw(surfaceViewCanvas);
canvasDrawLock = false;
}
Log.i("BlurLayout", (System.currentTimeMillis() - begin) + " prepared surfaceViewBitmap");
surfaceRender.updateBackground(surfaceViewBitmap);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (surfaceRender != null) {
surfaceRender.release();
}
if (preDrawListener != null) {
getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
}
Log.i("BlurLayout", "onDetachedFromWindow");
}
public void setCoverColor(int color) {
if (surfaceRender != null) {
surfaceRender.setCoverColor(color);
}
}
}
相对应的配置属性
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
public class BlurRenderScript {
private RenderScript rs;
private ScriptIntrinsicBlur blurScript;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public Bitmap blurBitmap(Context context, Bitmap inputBitmap, float blurRadius) {
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
if (rs == null || blurScript == null) {
rs = RenderScript.create(context);
blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
}
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
blurScript.setRadius(blurRadius);
}
blurScript.setInput(tmpIn);
blurScript.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);
return outputBitmap;
}
}
相对应配置属性
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceHolder;
import androidx.annotation.NonNull;
import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
public class DoubleCacheSurfaceRender extends SurfaceRender {
private final Deque<Bitmap> bufferQueue = new LinkedList<>();
private final ReentrantLock queueLock = new ReentrantLock();
private final Object renderThreadLock = new Object();
private Thread renderThread;
private boolean stopThread = false;
private boolean isRenderIdle = true;
public DoubleCacheSurfaceRender(Context context) {
this.context = context;
init();
}
private void init() {
HandlerThread blurThread = new HandlerThread("BlurThread");
blurThread.setPriority(Thread.MAX_PRIORITY);
blurThread.start();
blurHandler = new Handler(blurThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
long begin = System.currentTimeMillis();
Bitmap background = (Bitmap) msg.obj;
if (background == null) {
return;
}
Bitmap copy = Bitmap.createBitmap(background);
Bitmap blurBackground = blurRenderScript.blurBitmap(context, copy, BlurLayout.BLUR_RADIUS);
Log.i("BlurLayout", "cost:" + (System.currentTimeMillis() - begin) + " blur bitmap");
queueLock.lock();
if (bufferQueue.size() > 1) {
bufferQueue.removeFirst().recycle();
}
bufferQueue.offer(blurBackground);
Log.i("BlurLayout", "bufferQueue size:" + bufferQueue.size());
queueLock.unlock();
if (isRenderIdle) {
synchronized (renderThreadLock) {
isRenderIdle = false;
renderThreadLock.notify();
}
}
copy.recycle();
}
};
}
@Override
public void surfaceCreated(@NonNull final SurfaceHolder holder) {
super.surfaceCreated(holder);
}
@Override
void drawFrame(final SurfaceHolder holder) {
renderThread = new Thread(new Runnable() {
final Paint paint = new Paint();
@Override
public void run() {
while (true) {
if (stopThread) {
return;
}
if (bufferQueue.size() == 0) {
try {
synchronized (renderThreadLock) {
isRenderIdle = true;
Log.i("BlurLayout", "renderThreadLock.wait");
renderThreadLock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long begin = System.currentTimeMillis();
queueLock.lock();
Bitmap blurBackground = bufferQueue.poll();
queueLock.unlock();
if (blurBackground == null) {
continue;
}
Canvas canvas = lockCanvas();
if (canvas != null) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawBitmap(blurBackground, null, dst, paint);
if (coverColor != 0) {
canvas.drawColor(coverColor);
}
}
unlockCanvasAndPost(canvas);
blurBackground.recycle();
Log.i("BlurLayout", (System.currentTimeMillis() - begin) + " draw surfaceView ");
}
}
});
renderThread.setPriority(Thread.MAX_PRIORITY);
renderThread.start();
}
@Override
void release() {
if (blurHandler != null) {
blurHandler.getLooper().quit();
stopThread = true;
}
}
}
相对应配置属性
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.view.SurfaceHolder;
import androidx.annotation.NonNull;
public abstract class SurfaceRender implements SurfaceHolder.Callback {
Context context;
Handler blurHandler;
BlurRenderScript blurRenderScript = new BlurRenderScript();
int surfaceWidth;
int surfaceHeight;
Rect dst;
SurfaceHolder holder;
int coverColor;
abstract void drawFrame(SurfaceHolder holder);
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
this.holder = holder;
drawFrame(holder);
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
this.surfaceWidth = width;
this.surfaceHeight = height;
dst = new Rect(0, 0, surfaceWidth, surfaceHeight);
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
}
Canvas lockCanvas() {
Canvas canvas;
if (holder == null) {
return null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas = holder.lockHardwareCanvas();
} else {
canvas = holder.lockCanvas();
}
return canvas;
}
void unlockCanvasAndPost(Canvas canvas) {
try {
holder.unlockCanvasAndPost(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
void updateBackground(Bitmap background) {
if (blurHandler != null) {
Message msg = Message.obtain();
msg.obj = background;
blurHandler.sendMessage(msg);
}
}
public void setCoverColor(int color) {
this.coverColor = color;
}
abstract void release();
}
布局添加控件
<xxxxx.BlurLayout
android:id="@+id/rlayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_95">
<androidx.appcompat.widget.LinearLayoutCompat
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.appcompat.widget.LinearLayoutCompat>
</xxxxx.BlurLayout>
代码自行理解