作者:大师傅姑爷
转载地址:https://juejin.cn/post/7116432182448488479

简介

任务:window弹窗的背景模糊

实现原理:在window弹窗内容显示出来之前先截取手机屏幕,然后模糊,等弹窗出来之后根据弹窗大小自适应截图,设置为背景,从而实现弹窗背景模糊的效果

难点:

  1. 由于我们的应用没有activity常驻,而且是系统应用,所以和普通应用的截屏不同;
  2. 使用反射的方法调用系统api进行截屏时,获取屏幕高度有个坑,需要特别注意;
  3. 直接使用原生api进行模糊,radius设置为最大25并不能满足业务要求,所以还另外需要对图片进行处理。

截屏

截屏的方法有很多种,这里介绍常用的几种:

  1. adb命令截屏

    adb命令截屏需要root权限,而且由于涉及io,所以比较耗时

    private Bitmap getScreenByAdb(String localImgPath) {//       adb命令获取屏幕截图,与实际屏幕尺寸相同,但是涉及IO所以耗时Process process = null;try {process = Runtime.getRuntime().exec("screencap " +  localImgPath);process.waitFor();//比较耗时,尤其屏幕色彩比较复杂时,耗时甚至会到3s} catch (Exception e) {e.printStackTrace();} finally {if (process != null) {process.destroy();}}FileInputStream fis = null;try {fis = new FileInputStream(localImgPath);} catch (FileNotFoundException e) {e.printStackTrace();}Bitmap originalBgBitmap = BitmapFactory.decodeStream(fis);return originalBgBitmap;}```2.  反射调用系统api截屏使用该方法截屏时需要是系统应用,我最后也是采用的这种方式。```private Bitmap getScreenBySysMethod() {DisplayMetrics dm = getContext().getResources().getDisplayMetrics();Bitmap originalBgBitmap = null;try {Class<?> mClassType = Class.forName("android.view.SurfaceControl");Method nativeScreenshotMethod = mClassType.getDeclaredMethod("screenshot", Rect.class, int.class, int.class, int.class);nativeScreenshotMethod.setAccessible(true);Bitmap sBitmap = (Bitmap) nativeScreenshotMethod.invoke(mClassType, new Rect(), dm.widthPixels,dm.heightPixels, Surface.ROTATION_0);originalBgBitmap = sBitmap.copy(Bitmap.Config.ARGB_8888, true);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return originalBgBitmap;}

在使用这个方法截屏时有个坑,当时折腾了我挺长时间,就是在部分全面屏手机上直接使用dm.heightPixels获取的屏幕高度不包含状态栏,看dm.heightPixels的源码,注释写的是The absolute height of the available display size in pixels,所以不包含状态栏也对。这时通过另外一种方式可以获取屏幕高度:

      /*** 在activity中获取屏幕的真实高度,由于在部分全面屏手机上,* 直接使用DisplayMetrics heightPixels获取屏幕高度没有包含状态栏或者虚拟按键* 所以会比实际的小几十个像素* @param context* @return*/public static int getScreenHeight(Activity context) {DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();context.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);return displayMetrics.heightPixels;}
  1. 普通应用截屏

    这个方法一般用于activity或者fragment中调用,因为需要调用getWindow().getDecorView()获取根布局。所以,如果在需要截屏的时候,如果没有activity活动,是无法使用这个方法的。

       private Bitmap getScreenshotView() {View view = getWindow().getDecorView();view.setDrawingCacheEnabled(true); // 设置缓存,可用于实时截图Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);view.draw(canvas);view.setDrawingCacheEnabled(false); // 清空缓存,可用于实时截图return bitmap;}
同时,由于这个方法在获取截屏时是通过view进行的,所以也可以只对某个view进行局部截图,只要将方法中的view对象换成需要截图的view就可以了。

Bitmap模糊

GitHub上有现成的模糊控件,可以满足很多场景,比如github.com/mmin18/Real…等。可惜不满足我的需求,我这边只需要对拿到的bitmap进行模糊,直接使用Android原生的就可以,代码如下:

/**
*
* @param context 上下文
* @param src 原图
* @param radius 模糊半径,有效范围0-25
* @return
*/
private Bitmap blur(Context context, Bitmap src, float radius) {RenderScript rs = RenderScript.create(context);final Allocation input = Allocation.createFromBitmap(rs, src);final Allocation output = Allocation.createTyped(rs, input.getType());final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));script.setRadius(radius);script.setInput(input);script.forEach(output);output.copyTo(src);return src;
}

如果这个方法模糊度不够的话,还可以这样来做:先将bitmap缩小,再进行模糊,然后再放大,这样得到的模糊度一般是可以满足需求的。

缩小bitmap

BitmapTransformUtil.scaleBitmap(originalBgBitmap, 0.2f, 0.2f, true);

放大bitmap

BitmapTransformUtil.scaleBitmap(blurredBgBitmap, 5.0f, 5.0f, true);

其中scaleBitmap方法如下所示:

   /*** 根据指定的宽度比例值和高度比例值进行缩放** @param srcBitmap* @param scaleWidth* @param scaleHeight* @param recycleSrc 是否回收Bitmap* @return*/public static Bitmap scaleBitmap(Bitmap srcBitmap, float scaleWidth, float scaleHeight, boolean recycleSrc) {int width = srcBitmap.getWidth();int height = srcBitmap.getHeight();Matrix matrix = new Matrix();matrix.postScale(scaleWidth, scaleHeight);Bitmap bitmap = Bitmap.createBitmap(srcBitmap, 0, 0, width, height, matrix, true);if (bitmap != null) {/**回收*/if (recycleSrc && srcBitmap != null && !srcBitmap.equals(bitmap) && !srcBitmap.isRecycled()) {GlideBitmapPool.putBitmap(srcBitmap);}return bitmap;} else {return srcBitmap;}}

Bitmap截取

拿到整个手机屏幕的截图后,需要借去部分作为背景,截取bitmap方法如下:

   /*** 裁剪一定高度保留上半部分** @param srcBitmap 原图* @param x         起始坐标x* @param y         起始坐标y* @param width     目标宽度* @param height     目标高度* @param recycleSrc 是否回收原图* @return*/public static Bitmap cropBitmapTop(Bitmap srcBitmap, int x, int y, int width, int height, boolean recycleSrc) {/**裁剪关键步骤*/Bitmap cropBitmap = Bitmap.createBitmap(srcBitmap, x, y, width, height);/**回收之前的Bitmap*/if (recycleSrc && srcBitmap != null && !srcBitmap.equals(cropBitmap) && !srcBitmap.isRecycled()) {GlideBitmapPool.putBitmap(srcBitmap);}return cropBitmap;}

Bitmap圆角设置

   /*** 设置圆角** @param srcBitmap* @param recycleSrc 是否回收原图* @return*/public static Bitmap getRoundedCornerBitmap(Bitmap srcBitmap, float radius, boolean recycleSrc) {Bitmap output = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.ARGB_8888);Paint paint = new Paint();Rect rect = new Rect(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight());RectF rectF = new RectF(rect);paint.setAntiAlias(true);Canvas canvas = new Canvas(output);canvas.save();canvas.drawARGB(0, 0, 0, 0);canvas.drawRoundRect(rectF, radius, radius, paint);paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));canvas.drawBitmap(srcBitmap, rect, rect, paint);canvas.restore();/**回收之前的Bitmap*/if (recycleSrc && srcBitmap != null && !srcBitmap.equals(output) && !srcBitmap.isRecycled()) {GlideBitmapPool.putBitmap(srcBitmap);}return output;}

Bitmap保存

    /*** 保存bitmap到本地* @param bitmap * @param filePath */private void saveBitmap(Bitmap bitmap, String filePath){FileOutputStream fos = null;try {fos = new FileOutputStream(filePath);bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);fos.flush();} catch (IOException e) {e.printStackTrace();} finally {try {if (fos != null) {fos.close();}} catch (IOException e) {e.printStackTrace();}}}

推荐一个bitmap复用池:github.com/amitshekhar…,毕竟bitmap是内存消耗大户,合理使用bitmap可以避免内存抖动和OOM。具体使用方法直接点进去看吧。

最后贴一个实现效果图吧

Android 截屏、图片模糊及bitmap相关相关推荐

  1. iOS 截屏图片模糊,提高清晰度

    最近项目遇到截屏功能,截屏时图片太模糊,现做以下是处理方法. 直接上代码: [selfsaveScreenshotToPhotosAlbum:[selfcaptureScreen]]; //截屏操作 ...

  2. Android平台监听系统截屏方案预研及相关知识点

    最近有个针对系统截屏的需求,所以预研了Android平台上捕获系统截屏的方案. 最直接的方式就是监听手机的系统截屏组合键(电源键+音量下键),但是这种方式实现难度大,且有的机型使用特殊手势进行截屏,兼 ...

  3. Android截屏与WebView长图分享经验总结【转】

    原文:https://youzanmobile.github.io/2017/05/19/android-screenshot-and-webview/ 最近在做新业务需求的同时,我们在 Androi ...

  4. Android 截屏与 WebView 长图分享经验总结

    最近在做新业务需求的同时,我们在 Android 上遇到了一些之前没有碰到过的问题,截屏分享. WebView 生成长图以及长图在各个分享渠道分享时图片模糊甚至分享失败等问题,在这过程中踩了很多坑,到 ...

  5. Android 截屏监听(截图分享功能实现)

    具体来说就是,检测到了用户在应用内有截图,弹出一个分享界面, 在截图下方添加一个二维码,进行分享. ●●●  前言 Android系统没有直接对截屏事件监听的接口,也没有广播,只能自己动手来丰衣足食, ...

  6. Android截屏一键分享开发与实现方式的反思和总结

    本文主要记录android系统截屏一键分享开发,后续优化. 近期接到新需求,需要做截屏,然后弹出小窗口,显示一键分享与截屏小图预览.类似于同花顺app截图弹出框,点击后出现分享界面. 效果图如下:   ...

  7. Android截屏方法总结

    最近研究了一些Android的截屏方法,做一个总结. 图片剪裁方法 使用View.getDrawingCache()得到Bitmap.非常简单但是只能截图本应用的图片,并且没办法控制截图的范围. 对B ...

  8. android华为虚拟截屏黑屏,Android截屏表面视图显示黑屏

    Android截屏表面视图显示黑屏 我试图通过代码拍摄我的游戏的截图,并通过一个意图来分享它.我能做这些事情,但是截图总是看起来是黑色的.下面是与分享截图相关的代码:View view = MainA ...

  9. Android截屏分享

    最近项目需要实现Android截屏分享功能,包括Android截屏获取图片.将图片保存到本地.通知系统相册更新.通过微信.QQ.微博分享截屏图片,本篇博客作为总结回顾. 一.Android截屏获取图片 ...

最新文章

  1. xml笔记整理_基础概括
  2. 《THE LEAN STARTUP》 《精益创业》
  3. Coprime Sequence 思维 gcd 删一个数
  4. wins宝塔安装提示已经有php,centOS安装宝塔提示报错
  5. 2-1 git合并 打tag
  6. MapReduce之InputFormat理解
  7. 深度学习中的常用的归一化方法汇总
  8. 二分图最大权匹配【KM算法 BFS优化下的真正的O(N3)的KM算法】【KM算法模板】
  9. 【地震数据处理】GAN网络基础知识
  10. RK Android系统开机启动流程
  11. 淘宝/京东/苏宁/拼多多/唯品会 返利消息批量转链思路
  12. 文件服务器域用户配额,域账号配额
  13. 语音合成(TTS)论文优选:Forward Attention in Sequence- To-Sequence Acoustic Modeling for Speech Synthesis
  14. php习题,PHP 练习题
  15. 图形化初始化达梦数据库
  16. 【Linux】进程间通讯
  17. 知其然不知其所以然 系列
  18. Java - 日期和时间:如何取得年月日、时分秒?如何取得从1970年1月1日0时0分0秒到现在的毫秒数?如何取得某月的最后一天?如何格式化日期?
  19. 如何克隆服务器系统盘,如果把云服务器系统盘克隆
  20. 基于Linux 5.4.18的nvme驱动学习 - Linux相关概念 (一)

热门文章

  1. 单例模式在单线程多线程下的写法
  2. IDEA添加子Module的正确姿势
  3. C# excel工具栏菜单栏等操作
  4. 山海镜花手游如何用电脑玩 山海镜花手游PC电脑版教程
  5. 工具-6:如何解决U盘无法弹出的问题?
  6. matlab中的plot的用法
  7. ubuntu20.04设置rc.local
  8. Ubuntu把数据从一个硬盘完全拷贝到另一个硬盘的方法
  9. 武汉大学计算机专业博士导师,武汉大学计算机学院博士生导师简介:袁志勇
  10. 软件测试技能大赛教学平台