Android实现高斯模糊背景对话框

0.需要的效果

<-图一        <-图二

1.实现

先写一个dialog

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;

/**
 * Author: yubin
 * Time:2018/8/1 11:50
 * Tips: 高斯模糊背景对话框
 */
public class VagueDialog extends Dialog {

    private AdapterView.OnItemClickListener mItemClickListener;

    public VagueDialog setOnItemClickListener(AdapterView.OnItemClickListener itemClickListener) {
        mItemClickListener = itemClickListener;
        return this;
    }

    public VagueDialog setOnDismiss(OnDismissListener dismissListener) {
        setOnDismissListener(dismissListener);
        return this;
    }

    /**
     * 全屏的模糊弹窗
     *
     * @param context
     * @param res
     */
    public VagueDialog(@NonNull Activity context, @LayoutRes int res) {
        this(context, context.getWindow().getDecorView(), -1, res);
    }

    /**
     * @param context
     * @param blurredView 模糊之前的View
     * @param vagueHeight 距离底部的高度 -1时全屏模糊
     * @param res         弹出的布局  需包含id blurring_view rl_button rl_button ll_release iv_close
     */
    public VagueDialog(@NonNull Context context, View blurredView, int vagueHeight, @LayoutRes int res) {
        super(context, R.style.circle_vague_dialog_style);
        //获取当前Activity所在的窗体
        try {
            Window dialogWindow = getWindow();
            //设置Dialog从窗体底部弹出
            dialogWindow.setGravity(Gravity.BOTTOM);
            dialogWindow.getDecorView().setPadding(0, 0, 0, 0);
            WindowManager.LayoutParams lp = dialogWindow.getAttributes();
            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            View view = View.inflate(context, res, null);
            //获取View 的id
            int bv = context.getResources().getIdentifier("blurring_view", "id", context.getPackageName());
            BlurringView mBlurringView = view.findViewById(bv);

            //给出了模糊视图并刷新模糊视图。
            if (blurredView != null) {
                mBlurringView.setBlurredView(blurredView, vagueHeight);
                mBlurringView.invalidate();
            }

            view.measure(0, 0);
            if (vagueHeight != -1) {
                lp.height = view.getMeasuredHeight();
            }
            dialogWindow.setAttributes(lp);
            if (vagueHeight == -1) {
                //获取View 的id
                int rl = context.getResources().getIdentifier("rl_button", "id", context.getPackageName());
                RelativeLayout rl_button = view.findViewById(rl);
                rl_button.setAnimation(AnimationUtils.loadAnimation(context, R.anim.pop_enter_anim));
            } else
                dialogWindow.setWindowAnimations(R.style.popupwindow);  //添加动画

            //获取View 的id
            int ll = context.getResources().getIdentifier("ll_release", "id", context.getPackageName());
            LinearLayout ll_release = view.findViewById(ll);
            for (int i = 0; i < ll_release.getChildCount(); i++) {
                final int finalI = i;
                View childAt = ll_release.getChildAt(i);
                childAt.setOnClickListener(view1 -> {
                    mItemClickListener.onItemClick(null, childAt, finalI, finalI);
                    dismiss();
                });
            }
            //获取View 的id
            int iv_close = context.getResources().getIdentifier("iv_close", "id", context.getPackageName());
            ImageView ivClose = view.findViewById(iv_close);
            ivClose.setOnClickListener(view1 -> dismiss());

            setContentView(view);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

需要的BlurringView

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

/**
 * Created by yubin
 * 2018/8/1 0007-下午 3:27
 */
public class BlurringView extends View {

    public BlurringView(Context context) {
        this(context, null);
    }

    public BlurringView(Context context, AttributeSet attrs) {
        super(context, attrs);

        //0 < radius <= 25
        final int defaultBlurRadius = 11;
        // defaultDownsampleFactor > 0
        final int defaultDownsampleFactor = 6;
        //模糊的颜色 默认50%白色
        final int defaultOverlayColor = Color.parseColor("#50FFFFFF");

        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();
    }

    private int mVagueHeight;

    /**
     * @param blurredView 屏幕跟布局
     * @param vagueHeight 距离底部的高度 dp  -1 全屏
     */
    public void setBlurredView(View blurredView, int vagueHeight) {
        mBlurredView = blurredView;
        mVagueHeight = vagueHeight;
    }

    @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);
                if (mVagueHeight != -1) {
                    blur(dp2px(this.getContext(), mVagueHeight));
                } else
                    blur(mVagueHeight);

                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 static int dp2px(Context context, float dpVal) {
        return (int) TypedValue.applyDimension(1, dpVal, context.getResources().getDisplayMetrics());
    }
    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;
    }

    /**
     * 按长方形裁切图片
     *
     * @param bitmap
     * @param imgHeight 剪裁后的高度
     * @return
     */
    private Bitmap imageCropWithRect(Bitmap bitmap, int imgHeight) {
        if (bitmap == null) {
            return null;
        }
        int w = bitmap.getWidth(); // 得到图片的宽,高
        int h = bitmap.getHeight();

        /*int nw, nh, retX, retY;
        if (w > h) {
            nw = h / 2;
            nh = h;
            retX = (w - nw) / 2;
            retY = 0;
        } else {
            nw = w / 2;
            nh = w;
            retX = w / 4;
            retY = (h - w) / 2;
        }*/

        // 下面这句是关键
        Bitmap bmp = Bitmap.createBitmap(bitmap, 0, h - imgHeight, w, imgHeight, null, false);
        if (bitmap != null && !bitmap.equals(bmp) && !bitmap.isRecycled()) {
            bitmap.recycle();
            bitmap = null;
        }
        return bmp;// Bitmap.createBitmap(bitmap, retX, retY, nw, nh, null,
        // false);
    }

    /**
     * android.renderscript.RSIllegalArgumentException: Cannot update allocation from bitmap, sizes mismatch
     *
     * @param h
     */
    protected void blur(int h) {
        try {
            mBlurInput.copyFrom(mBitmapToBlur);
            mBlurScript.setInput(mBlurInput);
            mBlurScript.forEach(mBlurOutput);
            mBlurOutput.copyTo(mBlurredBitmap);
            if (h != -1) {
                mBlurredBitmap = imageCropWithRect(mBlurredBitmap, h);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @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;
}

style中加三个自定义属性

<!--高斯模糊view的属性-->
    <declare-styleable name="PxBlurringView">
        <!--0 < radius <= 25-->
        <attr name="blurRadius" format="integer" />
        <attr name="downsampleFactor" format="integer" />
        <!--模糊的颜色 默认50%白色-->
        <attr name="overlayColor" format="color" />
    </declare-styleable>
    <!--高斯模糊当前-->
    <style name="circle_vague_dialog_style" parent="@android:style/Theme.Dialog">
        <!-- 背景透明 -->
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <!-- 浮于Activity之上 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 边框 -->
        <item name="android:windowFrame">@null</item>
        <!-- Dialog以外的区域模糊效果 -->
        <item name="android:backgroundDimEnabled">false</item>
        <!-- 无标题 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 半透明 -->
        <item name="android:windowIsTranslucent">true</item>
    </style>
    <!-- popupwindow 动画 -->
    <style name="popupwindow">
        <item name="android:windowEnterAnimation">@anim/pop_enter_anim</item>
        <item name="android:windowExitAnimation">@anim/pop_exit_anim</item>
    </style>

anim 文件夹中

pop_enter_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="500"
        android:fromYDelta="100%p"
        android:toYDelta="0" />
    <alpha
        android:duration="500"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

pop_exit_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="500"
        android:fromYDelta="0"
        android:toYDelta="50%p"/>
    <alpha
        android:duration="500"
        android:fromAlpha="1.0"
        android:toAlpha="0.0"/>
</set> 

2.使用

布局示例

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff">

    <com.newcircle.widget.BlurringView
        android:id="@+id/blurring_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        app:blurRadius="25"
        app:downsampleFactor="1"
        app:overlayColor="#B3FFFFFF" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/y397"
        app:layout_constraintBottom_toBottomOf="parent">


        <LinearLayout
            android:id="@+id/ll_release"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@+id/iv_close"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_release_circle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:drawableTop="@drawable/qz_fabu_quanzi"
                android:foreground="?android:attr/selectableItemBackground"
                android:gravity="center"
                android:text="建圈子" />

            <TextView
                android:id="@+id/tv_release_dynamic"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:drawableTop="@drawable/newcircle_release_dynamic"
                android:foreground="?android:attr/selectableItemBackground"
                android:gravity="center"
                android:text="发动态" />

            <TextView
                android:id="@+id/tv_release_active"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:drawableTop="@drawable/newcircle_release_active"
                android:foreground="?android:attr/selectableItemBackground"
                android:gravity="center"
                android:onClick="circleMember"
                android:text="发活动" />

        </LinearLayout>

        <ImageView
            android:id="@+id/iv_close"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:padding="@dimen/x60"
            android:src="@drawable/newcircle_close" />

    </RelativeLayout>
</android.support.constraint.ConstraintLayout>

图一效果

new VagueDialog(mContext, R.layout.dialog_home_publish)
                            .setOnDismiss(dialogInterface -> //do something)
                            .setOnItemClickListener((adapterView, view1, i, l) -> {
                                switch (i) {
                                    case 0:
                                        AAAActivity.start(mContext);
                                        break;
                                    case 1:
                                        BBBActivity.start(mContext);
                                        break;
                                    case 2:
                                        CCCActivity.start(mContext);
                                        break;
                                }
                            }).show();

图二效果

new VagueDialog(mContext, root, 398/2, R.layout.dialog_release)
                .setOnItemClickListener((adapterView, view1, i, l) -> {
                    switch (i) {
                        case 0:
                            AAAActivity.start(mContext);
                            break;
                        case 1:
                            BBBtActivity.start(mContext);
                            break;
                    }
                }).show();
发布了43 篇原创文章 · 获赞 27 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_30878303/article/details/81356260