android 自定义毛玻璃布局样式实现

自定义毛玻璃布局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>

代码自行理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Z_Try

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值