先来看看效果如何:
最近对Android又有了一些探索,自从看到iOS上的高斯模糊做得如此奈斯就萌生了在安卓上模仿一下的想法,简单想了一下可以 用这样的思路来做:
- 众所周知FrameLayout是能叠加在已经有的View上面的,所以在写UI时用一个FrameLayout 来承载一个ImageView,作为对话框后面的背景,假设这个ImageView的ID为main_dialog_bg。
- 获取当前屏幕的截图进行模糊处理,并让main_dialog_bg这个ImageView来显示它,但同时要设置它的透明度为全透明,而且setVisibility(View.GONE); 。
- 用户点击弹窗按钮时 执行第2步,然后main_dialog_bg.setVisibility(View.VISIBLE); ,用循环让模糊的背景从全透明转为不透明,再弹出窗口
- 用户取消 弹窗时用循环来实现背景透明度从不透明慢慢变成全透明,最后把背景可见性设为GONE
下面是做出来的 一个Demo,效果很棒
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
<!-- 这里的RelativeLayout用于主界面的布局,为了方便布局这里直接用了一张图片作为主界面 -->
<RelativeLayout
android:layout_below="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@mipmap/main_view" />
</RelativeLayout>
<!-- 这里的FrameLayout用于承载对话框的背景 -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_dialog_bg"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</RelativeLayout>
非常重要的地方就是状态栏要设为透明并且在java代码里面写一句
toolbar.setFitsSystemWindows(true);
styles.xml:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<!-- 注意!需要将状态栏透明打开 -->
<item name="android:windowTranslucentStatus">true</item>
</style>
</resources>
MainActivity.java:
package dialog.blur.demo;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
private ImageView dialogBg;
private int originalW;
private int originalH;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//创建activity先把对话框背景图设为不可见
dialogBg.setImageAlpha(0);
dialogBg.setVisibility(View.GONE);
}
@SuppressLint("HandlerLeak")
private void initView() {
Toolbar toolbar = findViewById(R.id.toolbar);
dialogBg = findViewById(R.id.iv_dialog_bg);
setSupportActionBar(toolbar);
toolbar.setFitsSystemWindows(true);//这条语句和状态栏透明必不可少
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
dialogBg.setVisibility(View.GONE);
}
}
};
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.pop:
handleBlur();//处理背景图
dialog();//弹出对话框并实现背景图的展现
break;
case R.id.exit:
finish();
break;
default:
}
return true;
}
private Bitmap captureScreen(Activity activity) {
activity.getWindow().getDecorView().destroyDrawingCache(); //先清理屏幕绘制缓存(重要)
activity.getWindow().getDecorView().setDrawingCacheEnabled(true);
Bitmap bmp = activity.getWindow().getDecorView().getDrawingCache();
//获取原图尺寸
originalW = bmp.getWidth();
originalH = bmp.getHeight();
//对原图进行缩小,提高下一步高斯模糊的效率
bmp = Bitmap.createScaledBitmap(bmp, originalW / 4, originalH / 4, false);
return bmp;
}
private void setScreenBgLight(Dialog dialog) {
Window window = dialog.getWindow();
WindowManager.LayoutParams lp;
if (window != null) {
lp = window.getAttributes();
lp.dimAmount = 0.2f;
window.setAttributes(lp);
}
}
private void dialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("弹窗");
builder.setMessage("这是一个对话框,实现了背景虚化效果。");
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface p1) {
//点击对话框外侧区域取消弹窗的监听
hideBlur(); //背景淡出
}
});
builder.setPositiveButton("好的", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface p1, int p2) {
//为确定按钮(右)添加点击事件
hideBlur(); //背景淡出
}
});
Dialog dialog = builder.show();
setScreenBgLight(dialog);//背景明暗度调整
}
private void handleBlur() {
Bitmap bp = captureScreen(MainActivity.this);
bp = blur(bp); //对屏幕截图模糊处理
//将模糊处理后的图恢复到原图尺寸并显示出来
bp = Bitmap.createScaledBitmap(bp, originalW, originalH, false);
dialogBg.setImageBitmap(bp);
dialogBg.setVisibility(View.VISIBLE);
//防止UI线程阻塞,在子线程中让背景实现淡入效果
asyncRefresh(true);
}
private void asyncRefresh(boolean in) {
//淡出淡入效果的实现
if(in) { //淡入效果
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 256; i += 5) {
refreshUI(i);//在UI线程刷新视图
try {
Thread.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
} else { //淡出效果
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 255; i >= 0; i -= 5) {
refreshUI(i);//在UI线程刷新视图
try {
Thread.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当淡出效果完毕后发送消息给mHandler把对话框背景设为不可见
mHandler.sendEmptyMessage(0);
}
}).start();
}
}
private void refreshUI(final int i) {
runOnUiThread(new Runnable() {
@Override
public void run() {
dialogBg.setImageAlpha(i);
}
});
}
private void hideBlur() {
//把对话框背景隐藏
asyncRefresh(false);
System.gc();
}
private Bitmap blur(Bitmap bitmap) {
//使用RenderScript对图片进行高斯模糊处理
Bitmap output = Bitmap.createBitmap(bitmap); // 创建输出图片
RenderScript rs = RenderScript.create(this); // 构建一个RenderScript对象
ScriptIntrinsicBlur gaussianBlue = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); //
// 创建高斯模糊脚本
Allocation allIn = Allocation.createFromBitmap(rs, bitmap); // 开辟输入内存
Allocation allOut = Allocation.createFromBitmap(rs, output); // 开辟输出内存
float radius = 10f; //设置模糊半径
gaussianBlue.setRadius(radius); // 设置模糊半径,范围0f<radius<=25f
gaussianBlue.setInput(allIn); // 设置输入内存
gaussianBlue.forEach(allOut); // 模糊编码,并将内存填入输出内存
allOut.copyTo(output); // 将输出内存编码为Bitmap,图片大小必须注意
rs.destroy();
//rs.releaseAllContexts(); // 关闭RenderScript对象,API>=23则使用rs.releaseAllContexts()
return output;
}
}
其他的代码,比如res/menu/toolbar.xml和res/values/color.xml, 就没有必要贴出来了