背景
毛玻璃,开发中又爱又恨的一个话题,玩法层出不穷,有动态的,也有静态的。有的是实时模糊,有些只需要模糊一次,本文的毛玻璃实现,就是静态毛玻璃。
开发环境
win 10
as 4+
jdk 1.8
代码
!!!源码在文末!!!
最终效果
思路
看到上图,首先就要观察有什么要点:
(1)毛玻璃的区域,是与图片“内容连贯”的,就是说,毛玻璃展示内容,和实际内容一致,视觉上就是“局部毛玻璃”的效果。
(2)毛玻璃的形状,看上去,毛玻璃四个角都是圆角,所以,就涉及到毛玻璃展示过程中的圆角绘制。
根据要点,可以总结出,原图,必须要先加载出来,然后设置到ImageView上面,再获取ImageView正在显示的图像(先设置到ImageView,因为会收到其ScaleType属性影响),然后自定义view对获取的图片进行处理即可。
实现
原理说完了,那又要怎么实现呢?列出步骤:
(一)把图片显示在背景控件上面
(二)把背景控件显示后的图片,传入到自定义毛玻璃控件中
(三)通过canvas处理,绘制,显示
下面开始具体描述如何实现:
(一)把图片显示在背景控件上面
这一步就是常规操作了,可以用glide,也可以本地随便搞一个图片设置到背景控件中即可,笔者这里的demo是通过ImageView xml属性配置的。
(二)把背景控件显示后的图片,传入到自定义毛玻璃控件中
这里就涉及到一个“获取显示控件的图片”这一个逻辑,具体的实现代码如下:
/**
* 普通截图
*/
public static void viewSnapShot(View view, ViewSnapListener listener) {
try {
//使控件可以进行缓存
view.setDrawingCacheEnabled(true);
//获取缓存的 Bitmap
Bitmap drawingCache = view.getDrawingCache();
//复制获取的 Bitmap
drawingCache = Bitmap.createBitmap(drawingCache, 0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
//关闭视图的缓存
view.setDrawingCacheEnabled(false);
view.destroyDrawingCache();
if (drawingCache != null) {
if (listener != null) {
listener.success(drawingCache);
}
} else {
if (listener != null) {
listener.failed("draw cache is null");
}
}
} catch (Exception e) {
if (listener != null) {
listener.failed(e.getMessage());
}
}
}
通过该方法,可以获取ImageView控件显示中的图片,并且返回一个bitmap对象给外部了。
(三)通过canvas处理,绘制,显示
这里涉及到自定义view。首先,先看xml设置的布局位置,代码如下:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="@+id/thirdImg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@drawable/testblur" />
<com.example.ktdemo.blur.blurwidget.BlurAlignBottomView
android:id="@+id/ivBlurView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true" />
</RelativeLayout>
可以看到,自定义毛玻璃控件BlurAlignBottomView的宽高是与背景图片显示控件的宽高一样的。
再说核心实现。因为宽高一样,所以处理起来,就简单很多了。
(1)通过clipRect方法,剪裁不需要显示的区域
(2)最后把bitmap高斯模糊后通过canvas画上去
对bitmap进行高斯模糊的代码如下:
public static Bitmap blur(Context context,int blurRadius, Bitmap srcBitmap) {
try {
float radius = Math.min(25,blurRadius);
radius = Math.max(0,radius);
Bitmap bitmap = srcBitmap.copy(srcBitmap.getConfig(), true);
final RenderScript rs = RenderScript.create(context);
final Allocation input = Allocation.createFromBitmap(rs, srcBitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
final Allocation output = Allocation.createTyped(rs, input.getType());
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setRadius(radius /* e.g. 3.f */);
script.setInput(input);
script.forEach(output);
output.copyTo(bitmap);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return srcBitmap;
}
}
使用原生RenderScript模糊即可。
剪裁显示范围以及绘制bitmap核心代码如下:
int mesWidth = getMeasuredWidth();
int mesHeight = getMeasuredHeight();
float radius = mBlurInfo.getRadius();
Bitmap mSrcBitmap = mBlurInfo.getSrcBitmap();
canvas.save();
canvas.clipRect(0, mesHeight - mBlurInfo.getBlurHeight(), mesWidth, mesHeight);
RectF canvasRectF = new RectF(0, mesHeight - mBlurInfo.getBlurHeight(), mesWidth, mesHeight);
Path mPath = new Path();
mPath.addRoundRect(canvasRectF, radius, radius, Path.Direction.CW);
canvas.clipPath(mPath);
Rect bitmapRect = new Rect(0, 0, mesWidth, mesHeight);
canvas.drawBitmap(mSrcBitmap, null, bitmapRect, null);
canvas.restore();
}
总结。实现方式其实用很多,而文中的实现方式,仅仅是众多方法中的一种而已,各位若有更好的方法请在评论区留言。
代码地址
搜索BlurAlignBottomView类
that's all----------------------------------------------------------------------------