近期实现任意屏幕上滑弹出一个快捷栏,http://blog.youkuaiyun.com/kong92917/article/details/48023579。由于做完发现布局效果太差。刚巧看到ios的毛玻璃于是想模仿下。
注意点如下:上滑和弹出操作由于之前的动画和其他数据加载操作已经较为耗时,应尽量减少时间损耗。该方法是系统级别性质,应用级的实现更方便。
1、实现思路大概方向:
a、获取屏幕截图
b、对图片处理剪裁
c、对处理后的图片进行模糊运算
2、具体实现:
a、获取屏幕截图:
android L可以使用新的接口MediaProjection API,可参照http://binwaheed.blogspot.com/2015/03/how-to-correctly-take-screenshot-using.html。之前的版本请参照http://blog.youkuaiyun.com/buptgshengod/article/details/39155979。
但此处使用以上两种方法耗时都很明显,而且此处不需要保存图片为文件,第二种更是通过启动service来调用JNI实现截图,于是想到可以直接通过view来获取避免两次io流的操作。实现如下:
对于应用级的
private Bitmap getWindowBitmap(View v) {
View view = v.getRootView();// getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache(true);
Bitmap bmpcache = view.getDrawingCache();
return bmpcache;
}
遇到getDrawCache为null时参考这博文http://www.cnblogs.com/devinzhang/archive/2012/06/05/2536848.html
对于系统级需要获取其他每个界面的view截图,采用如上方法时发现获取不到DecorView(我是在phonewindowmanager下无法获取,待补充)想过如下实现:根据TopRunningProcess包名和Activity获取当前top顶层的实例,再activity.getWindow().getDecorView(); 该方法获取实例时(Class.forName(name.getClassName()).newInstance());获取到的是重新打开的activity实例,无法用于截取当前的view截图。
在phonewindow中使用DecorView,
<span style="font-family:FangSong_GB2312;font-size:14px;"> // This is the top-level view of the window, containing the window decor.
private DecorView mDecor;</span>
贴个别处盗的图,view.getDrawingCache()在PhoneWindow中也有通过DecorView类似实现
好吧,没想到其他办法了就先用这个。
测试发现。该方法获取当前屏幕的bitmap时,实时性和效率会有点问题,正在研究解决方法中,待补充
b、对图片处理剪裁
/**
* 从屏幕截图中剪切出view控件的bitmap
* @param bkg 获取到的屏幕截图
* @param view 需要截取的图的大小,这里是截取view的大小,也可以直接定义截取范围
* @return
*/
private Bitmap cutBitmap(Bitmap bkg, View view) {
float scaleFactor = 4;// 图片缩放比例;
Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth() / scaleFactor),
(int) (view.getMeasuredHeight() / scaleFactor), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(overlay);
canvas.translate(-view.getLeft() / scaleFactor, -view.getTop() / scaleFactor);//重新定义位置
canvas.scale(1 / scaleFactor, 1 / scaleFactor);//缩放
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(bkg, 0, 0, paint);
return overlay;
}
也可使用这种方法
private static Bitmap blur(Bitmap bkg) {
float scaleFactor = 16;// 图片缩放比例;
Matrix matrix = new Matrix();
matrix.postScale(1/scaleFactor, 1/scaleFactor);
Bitmap bitmapBlur = Bitmap.createBitmap(bkg, x, y, width, height, matrix, true);
return bitmapBlur;
}
留意下为何要压缩图片。
c、对处理后的图片进行模糊运算
使图片模糊大多使用的高斯算法,高斯模糊就是将指定像素变换为其与周边像素加权平均后的值,权重就是高斯分布函数计算出来的值。主要点这两篇博客已经写得很不错了可直接参考:http://blog.jobbole.com/63894/ 。http://blog.youkuaiyun.com/huli870715/article/details/39378349
高版本可以利用现有Android结构,通过RenderScript调用底层接口实现高斯模糊计算,但这种方法模糊半径过大,大于25(数值来源知乎)时会产生性能问题。
//来源:知乎
// Remix Blur
private void blur(Bitmap bkg, View view) {
…
RenderScript rs = RenderScript.create(getActivity());
Allocation overlayAlloc = Allocation.createFromBitmap(rs, overlay);
ScriptIntrinsicBlur blur =
ScriptIntrinsicBlur.create(rs, overlayAlloc.getElement());
blur.setInput(overlayAlloc);
blur.setRadius(radius);
blur.forEach(overlayAlloc);
overlayAlloc.copyTo(overlay);
view.setBackground(new BitmapDrawable(getResources(), overlay));
rs.destroy();
}
由于单纯的进行高斯模糊仍然会执行很长时间,我使用上个链接提供的方法耗时大概是200ms~500ms不等,依机型和图片大小质量有关。由于高斯模糊是与周边像素加权平均后的值,高斯运算时对于高质量图片会做出大量循环加权平均,所以可以现将图片压缩减小运算时的量,得到的效果也几乎没有差别。测试发现压缩10倍后,高斯运算的时间可以控制在100ms以内。
但是!
在Android中渲染一帧的时间应该不超过16ms(60fps)。虽然没有太大的卡顿,但还是问题还是会有,参看了微博最新版本点击中间+号的模糊背景,发现模糊化过大只能显示下方的橙色,我觉得更可能它是直接用的一张模糊图片。
以上仅作为学习记录