Android實現高斯模糊背景對話框

0.需要的效果

<-圖一        <-圖二

1.實現

先寫一個dialog

import android.app.Dialog;
import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.RotateAnimation;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.newcircle.R;

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

    private AdapterView.OnItemClickListener mItemClickListener;

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

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

    /**
     * @param context
     * @param blurredView 模糊之前的View 一般頁面的根佈局
     * @param vagueHeight 距離底部的高度 -1時全屏模糊
     * @param res         彈出的佈局
     */
    public CircleVagueDialog(@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);

            BlurringView mBlurringView = view.findViewById(R.id.blurring_view);
//        View blurredView = view.findViewById(R.id.blurring_view);

            //給出了模糊視圖並刷新模糊視圖。
            mBlurringView.setBlurredView(blurredView, vagueHeight);
            mBlurringView.invalidate();

            view.measure(0, 0);
            if (vagueHeight != -1) {
                lp.height = view.getMeasuredHeight();
            }
            dialogWindow.setAttributes(lp);
            dialogWindow.setWindowAnimations(R.style.popupwindow);  //添加動畫

            LinearLayout ll_release = view.findViewById(R.id.ll_release);
            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();
                });
            }
            ImageView ivClose = view.findViewById(R.id.iv_close);
            ivClose.setAnimation(new RotateAnimation(270, 0, ivClose.getWidth() / 2, ivClose.getHeight() / 2));
            ivClose.setOnClickListener(view1 -> dismiss());

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

}

需要的BlurringView

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.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.util.AttributeSet;
import android.view.View;

import com.newcircle.R;
import com.smartcity.common.commonprovider.utils.DensityUtils;

/**
 * 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);

        final Resources res = getResources();
        final int defaultBlurRadius = 11;
        final int defaultDownsampleFactor = 6;
        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(DensityUtils.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 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中加三個自定義屬性

<declare-styleable name="PxBlurringView">
        <attr name="blurRadius" format="integer"/>
        <attr name="downsampleFactor" format="integer"/>
        <attr name="overlayColor" format="color"/>
    </declare-styleable>

 

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 CircleVagueDialog(mContext, findViewById(R.id.root), -1, 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 CircleVagueDialog(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();

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章