Android中毛玻璃效果主要有两种实现方式。
1.使用JAVA算法FastBlur实现
方法1 先将图片缩小,然后放大图片,再设置为控件背景以达到更模糊的效果,同时也提升模糊算法的处理效率。
2.使用Android自带类RenderScript 实现
方法2 模糊半径只能设置1-25。
对比下来同样的模糊半径 ,方法1 的模糊效果更好,且方法1 的模糊半径可以设置范围更大。示例代码如下:
1.activity中调用
private void testImageBlur() {
Log.i(TAG, "testImageBlur() 11");
Bitmap bitmapOrigin1 = getTestImage();
//1.显示原图
mImageOrigin.setImageBitmap(bitmapOrigin1);
Log.i(TAG, "testImageBlur() 22 bitmapOrigin1.isRecycled:" + bitmapOrigin1.isRecycled());
final float RADIUS = 20;
Bitmap bitmapOrigin2 = getTestImage();
Bitmap bitmapFastBlur = BitmapUtil.blurFastBlur(this, bitmapOrigin2, 20);
//2.显示使用FastBlur处理后 高斯模糊图片
mImageFastBlurResult.setImageBitmap(bitmapFastBlur);
Bitmap bitmapOrigin3 = getTestImage();
Bitmap bitmapRenderScriptBlur = BitmapUtil.blurRenderScript(this, bitmapOrigin3, 25);
//3.显示 RenderScript 处理后的高斯模糊图片
mImageRenderScriptResult.setImageBitmap(bitmapRenderScriptBlur);
Log.i(TAG, "testImageBlur() 33 bitmapOrigin3.isRecycled:" + bitmapOrigin3.isRecycled()
+ " bitmapOrigin1.isRecycled():" + bitmapOrigin1.isRecycled());
}
2.BitmapUtil.java 类
public class BitmapUtil {
private static final String TAG = "BitmapUtil";
public static BitmapDrawable getConfirmDialogBg(Context context) {
return getScreenBlurBg(context, 30.0f, 1676, 160, 834, 1094);
}
/**
* 使用 获取全屏高斯模糊的图片 BitmapDrawable
*
* @param activity
* @return
*/
public static BitmapDrawable getScreenBlurBg(Activity activity) {
WeakReference<Bitmap> screenBitmap = new WeakReference(FastBlurUtil.takeScreenShot(activity));
Log.i(TAG, "getScreenBlurBg 00 screenBitmap:" + screenBitmap);
if (null == screenBitmap) {
return null;
}
Log.i(TAG, "getScreenBlurBg 11 screenBitmap:" + screenBitmap);
long startMs = System.currentTimeMillis();
float radius = 10.0F;
Bitmap bitmapSmall = small(screenBitmap.get());
Bitmap bitmapBlur = FastBlurUtil.fastBlur(bitmapSmall, radius);
WeakReference<Bitmap> overlay = new WeakReference(FastBlurUtil.getDimBitmap(bitmapBlur, 0.2F));
Log.i(TAG, "getScreenBlurBg 22 =====blur time:" + (System.currentTimeMillis() - startMs));
try {
if (screenBitmap.get() != null && !screenBitmap.get().isRecycled()) {
screenBitmap.get().recycle();
}
if (null != bitmapSmall && !bitmapSmall.isRecycled()) {
bitmapSmall.recycle();
}
if (null != bitmapBlur && !bitmapBlur.isRecycled()) {
bitmapBlur.recycle();
}
} catch (Exception e) {
e.printStackTrace();
}
BitmapDrawable rst = new BitmapDrawable(activity.getResources(), overlay.get());
return rst;
}
/**
* 获取屏幕指定区域高斯模糊的图片 BitmapDrawable
*
* @param context
* @param blurRadius 模糊半径
* @param xOffset 指定区域的左上角顶点X坐标偏移
* @param yOffset 指定区域的左上角顶点Y坐标偏移
* @param width 指定区域的宽度
* @param height 指定区域的高度
* @return
*/
public static BitmapDrawable getScreenBlurBg(Context context, float blurRadius, int xOffset, int yOffset, int width, int height) {
Bitmap screenBitmap = FastBlurUtil.screenshot(context);
if (null == screenBitmap) {
return null;
}
Bitmap b1 = crop(screenBitmap, xOffset, yOffset, width, height);
Bitmap overlay = FastBlurUtil.fastBlur(bitmapMergeWithColor(b1), blurRadius);
// Bitmap overlay = FastBlurUtility.getDimBitmap(FastBlurUtility.fastBlur(small(bitmapMerge(b1, b2)), radius), 0.2F);
BitmapDrawable rst = new BitmapDrawable(context.getResources(), getRoundedCornerBitmap(overlay));
return rst;
}
/**
* 在给定的bitmap中剪裁指定区域
*
* @param source
* @param xOffset 指定区域的左上角顶点X坐标偏移
* @param yOffset 指定区域的左上角顶点Y坐标偏移
* @param width 指定区域的宽度
* @param height 指定区域的高度
* @return
*/
public static Bitmap crop(Bitmap source, int xOffset, int yOffset, int width, int height) {
return Bitmap.createBitmap(source, xOffset, yOffset, width, height);
}
/**
* 缩小bitmap,可以使用此方法先将图片缩小,再设置为控件背景以达到更模糊的效果
*
* @param bitmap
* @return
*/
private static Bitmap small(Bitmap bitmap) {
Matrix matrix = new Matrix();
matrix.postScale(0.25F, 0.25F);
Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizeBmp;
}
/**
* 图片圆角化
*
* @param bitmap
* @return
*/
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
final float roundPx = 24f;
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xFFFFFFFF;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
/**
* 将bitmap与指定颜色混合
*
* @param b1
* @return
*/
private static Bitmap bitmapMergeWithColor(Bitmap b1) {
if (!b1.isMutable()) {
b1 = b1.copy(Bitmap.Config.ARGB_8888, true);
}
Canvas canvas = new Canvas(b1);
canvas.drawARGB(79, 255, 255, 255);
canvas.save();
canvas.restore();
return b1;
}
/**
* 使用 fastBlur 接口实现高斯模糊效果
*
* @param context
* @param orginBitmap 需要做模糊效果的原始 bitmap
* @param radius 模糊半径
* @return 模糊后的bitmap
*/
public static Bitmap blurFastBlur(Context context, Bitmap orginBitmap, float radius) {
Log.i(TAG, "getScreenBlurBg 00 orginBitmap:" + orginBitmap);
if (null == orginBitmap) {
return null;
}
Log.i(TAG, "getScreenBlurBg 11 orginBitmap:" + orginBitmap);
long startMs = System.currentTimeMillis();
//先将图片缩小,再设置为控件背景以达到更模糊的效果,同时也提升模糊算法的处理效率
Bitmap bitmapSmall = small(orginBitmap);
Bitmap bitmapBlur = FastBlurUtil.fastBlur(bitmapSmall, radius);
WeakReference<Bitmap> overlay = new WeakReference(FastBlurUtil.getDimBitmap(bitmapBlur, 0.2F));
//WeakReference<Bitmap> overlay = new WeakReference(bitmapBlur);
Log.i(TAG, "getScreenBlurBg 22 =====blur time:" + (System.currentTimeMillis() - startMs));
try {
if (orginBitmap != null && !orginBitmap.isRecycled()) {
orginBitmap.recycle();
}
if (null != bitmapSmall && !bitmapSmall.isRecycled()) {
bitmapSmall.recycle();
}
if (null != bitmapBlur && !bitmapBlur.isRecycled()) {
bitmapBlur.recycle();
}
} catch (Exception e) {
e.printStackTrace();
}
return overlay.get();
}
/**
* 使用Android自带 RenderScript 接口实现高斯模糊效果
*
* @param context
* @param smallBitmap 需要做模糊效果的bitmap
* @param radius 模糊半径
* @return 模糊后的bitmap
*/
public static Bitmap blurRenderScript(Context context, Bitmap smallBitmap, float radius) {
// Create a new bitmap that is a copy of the original bitmap
Bitmap bitmap = smallBitmap.copy(Bitmap.Config.ARGB_8888, true);
// Initialize RenderScript
RenderScript rs = RenderScript.create(context);
// Create an empty allocation that will hold the original bitmap
Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);
// Create an empty allocation that will hold the blurred bitmap
Allocation output = Allocation.createTyped(rs, input.getType());
// Load the script in the Allocation
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setInput(input);
// Set the blur radius
//float radius = 20f; // 20 works well for me
// Start the Script Intrinsic Blur
script.setRadius(radius);
script.forEach(output);
// Copy the script result into the blurred bitmap
output.copyTo(bitmap);
// Recycle the original bitmap
smallBitmap.recycle();
// After all of this, we can return the now-blurred bitmap
return bitmap;
}
}
3.FastBlurUtil.java 类
public class FastBlurUtil {
public FastBlurUtil() {
}
public static Bitmap getBlurBackgroundDrawer(Context context) {
Bitmap bmp = screenshot(context);
return startBlurBackground(bmp);
}
public static Bitmap screenshot(Context context) {
int[] dim = new int[]{ScreenUtils.getScreenWidth(context), ScreenUtils.getScreenHeight(context)};
String surfaceClassName = "";
if (VERSION.SDK_INT <= 17) {
surfaceClassName = "android.view.Surface";
} else {
surfaceClassName = "android.view.SurfaceControl";
}
try {
Class<?> c = Class.forName(surfaceClassName);
Method method = c.getMethod("screenshot", Rect.class, Integer.TYPE, Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
Bitmap bitmap = (Bitmap)method.invoke((Object)null, new Rect(0, 0, dim[0], dim[1]), dim[0], dim[1], 0);
if(null == bitmap){
return null;
}
bitmap = bitmap.copy(Config.ARGB_8888, true);
return bitmap;
} catch (NoSuchMethodException | InvocationTargetException | ClassNotFoundException | IllegalAccessException var6) {
var6.printStackTrace();
return null;
}
}
public static Bitmap takeScreenShot(Activity activity) {
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap b1 = view.getDrawingCache();
int width = activity.getResources().getDisplayMetrics().widthPixels;
int height = activity.getResources().getDisplayMetrics().heightPixels + getNavigationBarOffset(activity);
Bitmap bmp = Bitmap.createBitmap(b1, 0, 0, width, height);
view.destroyDrawingCache();
return bmp;
}
@RequiresApi(
api = 26
)
private static Bitmap startBlurBackground(Bitmap bkg) {
long startMs = System.currentTimeMillis();
float radius = 10.0F;
Bitmap overlay = getDimBitmap(fastBlur(small(bkg), radius), 0.2F);
Log.i("FastBlurUtility", "=====blur time:" + (System.currentTimeMillis() - startMs));
return overlay;
}
private static Bitmap big(Bitmap bitmap) {
Matrix matrix = new Matrix();
matrix.postScale(4.0F, 4.0F);
Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizeBmp;
}
private static Bitmap small(Bitmap bitmap) {
Matrix matrix = new Matrix();
matrix.postScale(0.25F, 0.25F);
Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizeBmp;
}
private static int getStatusBarHeight(Activity activity) {
int result = 0;
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = activity.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
private static int getNavigationBarOffset(Activity activity) {
int result = 0;
Resources resources = activity.getResources();
if (VERSION.SDK_INT >= 21) {
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId);
}
}
return result;
}
/**
* 每个像素设置模糊效果
* @param bitmap 原图
* @param radiusf 模糊半径
* @return 模糊处理后的效果图
*/
public static Bitmap fastBlur(Bitmap bitmap, float radiusf) {
if (bitmap == null) {
return null;
} else {
int radius = (int)radiusf;
if (radius < 1) {
return null;
} else {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int[] r = new int[wh];
int[] g = new int[wh];
int[] b = new int[wh];
int[] vmin = new int[Math.max(w, h)];
int divsum = div + 1 >> 1;
divsum *= divsum;
int[] dv = new int[256 * divsum];
int i;
for(i = 0; i < 256 * divsum; ++i) {
dv[i] = i / divsum;
}
int yi = 0;
int yw = 0;
int[][] stack = new int[div][3];
int r1 = radius + 1;
int rsum;
int gsum;
int bsum;
int x;
int y;
int p;
int stackpointer;
int stackstart;
int[] sir;
int rbs;
int routsum;
int goutsum;
int boutsum;
int rinsum;
int ginsum;
int binsum;
for(y = 0; y < h; ++y) {
bsum = 0;
gsum = 0;
rsum = 0;
boutsum = 0;
goutsum = 0;
routsum = 0;
binsum = 0;
ginsum = 0;
rinsum = 0;
for(i = -radius; i <= radius; ++i) {
p = pix[yi + Math.min(wm, Math.max(i, 0))];
sir = stack[i + radius];
sir[0] = (p & 16711680) >> 16;
sir[1] = (p & '\uff00') >> 8;
sir[2] = p & 255;
rbs = r1 - Math.abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for(x = 0; x < w; ++x) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = Math.min(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 16711680) >> 16;
sir[1] = (p & '\uff00') >> 8;
sir[2] = p & 255;
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
++yi;
}
yw += w;
}
for(x = 0; x < w; ++x) {
bsum = 0;
gsum = 0;
rsum = 0;
boutsum = 0;
goutsum = 0;
routsum = 0;
binsum = 0;
ginsum = 0;
rinsum = 0;
int yp = -radius * w;
for(i = -radius; i <= radius; ++i) {
yi = Math.max(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - Math.abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for(y = 0; y < h; ++y) {
pix[yi] = -16777216 & pix[yi] | dv[rsum] << 16 | dv[gsum] << 8 | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = Math.min(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return bitmap;
}
}
}
@RequiresApi(
api = 26
)
public static Bitmap getDimBitmap(Bitmap background, float dimAmount) {
if (background == null) {
return null;
} else {
int bgWidth = background.getWidth();
int bgHeight = background.getHeight();
Bitmap newbmp = Bitmap.createBitmap(bgWidth, bgHeight, Config.ARGB_8888);
Canvas cv = new Canvas(newbmp);
cv.drawBitmap(background, 0.0F, 0.0F, (Paint)null);
cv.drawColor(Color.argb(dimAmount, 0.0F, 0.0F, 0.0F));
cv.save();
cv.restore();
return newbmp;
}
}
}