【Android】多种方式实现截图(屏幕截图、View截图、长图)

目录

  • 一、截图原理
  • 二、实现方式
    • 1. View截图
    • 2. WebView截图
    • 3. 屏幕截图
  • 三、格式转换方法

一、截图原理

在这里插入图片描述
我们的手机一般同时按下音量-键和电源键就会将当前屏幕显示的内容截取下来,那里面具体经过哪些流程呢?

Android中每一个页面都是一个Activity,通过Window对象实现页面的显示,每个Window对象实际上都是PhoneWindow的实例,当我们在Activity页面点击屏幕的时候,会触发点击事件,这个事件会一层层分发到处理它的view上,大致会经过这些view:
在这里插入图片描述
先会调PhoneWindowManager中的dispatchUnhandledKey方法,一层层往下,这里不详细展开,我们往下找会发现最终会调用一个takeScreenshot截屏的方法:

private void takeScreenshot() {synchronized (mScreenshotLock) {if (mScreenshotConnection != null) {return;}ComponentName cn = new ComponentName("com.android.systemui","com.android.systemui.screenshot.TakeScreenshotService");Intent intent = new Intent();intent.setComponent(cn);ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {synchronized (mScreenshotLock) {if (mScreenshotConnection != this) {return;}Messenger messenger = new Messenger(service);Message msg = Message.obtain(null, 1);final ServiceConnection myConn = this;Handler h = new Handler(mHandler.getLooper()) {@Overridepublic void handleMessage(Message msg) {synchronized (mScreenshotLock) {if (mScreenshotConnection == myConn) {mContext.unbindService(mScreenshotConnection);mScreenshotConnection = null;mHandler.removeCallbacks(mScreenshotTimeout);}}}};msg.replyTo = new Messenger(h);msg.arg1 = msg.arg2 = 0;if (mStatusBar != null && mStatusBar.isVisibleLw())msg.arg1 = 1;if (mNavigationBar != null && mNavigationBar.isVisibleLw())msg.arg2 = 1;try {messenger.send(msg);} catch (RemoteException e) {}}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};if (mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {mScreenshotConnection = conn;mHandler.postDelayed(mScreenshotTimeout, 10000);}}}

这里通过反射机制调用了TakeScreenshotService的bindServiceAsUser方法,创建TakeScreenshotService服务,再通过其内部的SurfaceControl.screenshot 生成 bitmap,生成图片成功会给系统发送通知。

系统截图的大致流程就是这样,在里面截图原理大致就是:获取需要截屏的区域的宽高,创建一个画布,然后区域内的内容绘制在画布上,最后生成bitmap图片。

二、实现方式

Android 截图主要为四种:View 截图、WebView 截图、屏幕截图、系统截图和 adb 截图。后两种截图不常用,不详细展开。

1. View截图

可以截取到View不可见的部分,生成长图,状态栏和导航栏无法截到
在这里插入图片描述

fun screenshotView(view: ViewGroup):Bitmap?{var h = 0var bitmap:Bitmap?=nullfor(i in 0 until view.childCount){h += view.getChildAt(i).heightview.getChildAt(i).setBackgroundColor(Color.parseColor("#6CC287"))}bitmap = Bitmap.createBitmap(view.width, h, Bitmap.Config.RGB_565)val canvas = Canvas(bitmap)view.draw(canvas)//重新赋色for(i in 0 until view.childCount){view.getChildAt(i).setBackgroundDrawable(null)}return bitmap
}

2. WebView截图

WebView 作为一种特殊的控件,不能像其他系统 View 或者截屏的方式来截图,有特定的Api
在这里插入图片描述

// 1.capturePicture方法废弃
// 2.getScale方法废弃// 3.getDrawingCache方法
private static byte[] screenshotWebView() {Bitmap bitmap = webView.getDrawingCache();byte[] drawByte = getBitmapByte(bmp);return drawByte;
}// 4.draw方法
private static byte[] screenshotWebView() {// webView.setDrawingCacheEnabled(true); 设置缓存Bitmap bitmap = Bitmap.createBitmap(webView.getWidth(), webView.getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);webView.draw(canvas);webView.destroyDrawingCache();byte[] drawByte = getBitmapByte(bitmap);return drawByte;
}

可能会截取不到 cavans 元素,原因是开启了硬件加速(关闭硬件加速可能导致页面异常),可在 AndroidManifest.xml 中设置:

android:hardwareAccelerated="false"

截长图的话需要配置:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {WebView.enableSlowWholeDocumentDraw();
}
setContentView(R.layout.webview);

3. 屏幕截图

截取应用当前屏幕的图片:

	/*** 获取当前屏幕截图,包含状态栏** @param activity activity* @return Bitmap*/public static Bitmap captureWithStatusBar(Activity activity) {View view = activity.getWindow().getDecorView();view.setDrawingCacheEnabled(true);view.buildDrawingCache();Bitmap bmp = view.getDrawingCache();int width = getScreenWidth(activity);int height = getScreenHeight(activity);Bitmap ret = Bitmap.createBitmap(bmp, 0, 0, width, height);view.destroyDrawingCache();return ret;}/*** 获取当前屏幕截图,不包含状态栏** @param activity activity* @return Bitmap*/public static Bitmap captureWithoutStatusBar(Activity activity) {View view = activity.getWindow().getDecorView();view.setDrawingCacheEnabled(true);view.buildDrawingCache();Bitmap bmp = view.getDrawingCache();int statusBarHeight = getStatusBarHeight(activity);int width = getScreenWidth(activity);int height = getScreenHeight(activity);Bitmap ret = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height - statusBarHeight);view.destroyDrawingCache();return ret;}/*** 得到屏幕的高** @param context* @return*/public static int getScreenHeight(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);int height = wm.getDefaultDisplay().getHeight();return height;}/*** 得到屏幕的宽** @param context* @return*/public static int getScreenWidth(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);int width = wm.getDefaultDisplay().getWidth();return width;}/*** 获取状态栏高度** @param context 上下文* @return 状态栏高度*/public static int getStatusBarHeight(Context context) {int result = 0;int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");if (resourceId > 0) {result = context.getResources().getDimensionPixelSize(resourceId);}return result;}

三、格式转换方法

下面列出了一些常用的转换方法:

// Bitmap 转 Base64
private static String getBitmapString(Bitmap bitmap) {String result = null;ByteArrayOutputStream out = null;try {if (bitmap != null) {out = new ByteArrayOutputStream();bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
​out.flush();out.close();byte[] bitmapBytes = out.toByteArray();result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);}} catch (IOException e) {e.printStackTrace();} finally {try {if (out != null) {out.flush();out.close();}} catch (IOException e) {e.printStackTrace();}}return result;
}// Bitmap 转 Byte
private static byte[] getBitmapByte(Bitmap bitmap){ByteArrayOutputStream out = new ByteArrayOutputStream();// 转换类型,压缩质量,字节流资源bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);try {out.flush();out.close();} catch (IOException e) {e.printStackTrace();}return out.toByteArray();
}// Drawable 转 Bitmap
public static Bitmap toBitmap(Drawable drawable) {if (drawable instanceof BitmapDrawable) {return ((BitmapDrawable) drawable).getBitmap();} else if (drawable instanceof ColorDrawable) {//colorBitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);canvas.drawColor(((ColorDrawable) drawable).getColor());return bitmap;} else if (drawable instanceof NinePatchDrawable) {//.9.pngBitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bitmap);drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());drawable.draw(canvas);return bitmap;}return null;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/35604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

精通Vim替换艺术:深入探索替换模式的奥秘

🔄 精通Vim替换艺术:深入探索替换模式的奥秘 🎨 Vim,这个源自伯克利的编辑器,以其强大的功能和灵活性在开发者和系统管理员中广受欢迎。在Vim中,替换模式是编辑文本时不可或缺的一部分,它允许我…

Java洗鞋小程序预约系统源码

💥洗鞋神器来袭!轻松预约,让你的鞋子焕然一新👟 🎉 告别洗鞋烦恼,洗鞋预约小程序来啦! 你是不是常常为洗鞋而烦恼?手洗太累,送去洗衣店又贵又麻烦。现在,好…

std::enable_if和std::is_base_of

std::enable_if,其主要为了完成模板特偏化,有两个参数,第一个为布尔值类型,第二个如果布尔值为true,其为默认空值,如果已经赋值,则为对应的类型。 std::is_base_of,其一共存在两个参数&#xff…

外汇的基本面分析需要关注什么?

外汇基本面分析的核心在于关注可能影响单一货币供求及国家货币价值的经济、社会和地缘政治事件与趋势。但值得注意的是,这些事件和因素往往具有更广泛的影响力,不仅限于单一国家。它们可能是影响整个地区或国家集团的重要事件,甚至一些事件&a…

使用docker搭建lnmp环境

近期闲来无事,用公司电脑部署了一套环境,记录一下部署经过和踩的坑。 Nginx 1、拉取镜像,需要使用镜像创建容器 拉取的Nginx版本可以自己设,我是用的nginx:latest // 拉取镜像 docker pull nginx:1.18.01.18.0: Pulling from …

差分数组与树上差分

假设我们现在需要维护一段区间,实现对一段区间[L,R]的数的加/减,以及对区间中某个数的查询 最简单的方法把这段区间的数都记录到数组a[N]中,每次修改都对区间[L,R]的每一个数进行加减操作,然后在查询第k个数的时候返回a[k] 在上…

数学建模 —— MATLAB中的矩阵(下)

目录 矩阵的拼接 矩阵的重复 矩阵的重构和重新排列 (1)reshape 函数 (2)sort 函数 (3)sortrows 函数 (4)flip / fliplr / flipud 函数 (5)rot90 函数 矩阵的拼接 有时候我们需要对多个矩阵进行拼接,变成一个大的矩阵。根据矩阵拼接的方向,我们可 …

C++性能优化篇:深拷贝、浅拷贝与移动构造

引言 最近在做性能优化,遇到了一个明显的性能问题,就是在大量数据做深拷贝的时候,程序耗时严重,几乎三分之一的时间都耗在了这里。于是乎仔细看了下此处的代码,发现这些深拷贝完全可以避免。 深拷贝、浅拷贝与移动构…

Java实现RS485串口通信

博客链接地址 近期,我接到了一个任务,将报警器接入到Java项目中,而接入的方式就是通过RS485接入,本人之前可以说是对此毫无所知。不过要感谢现在的互联网,通过网络我查到了我想要知道的一切,这里记录下本次…

对数据分析行业的疑问

我之前是做大数据的开发的,当然也做业务比如Java、springcloud的后端开发、也做了ai的。 但是我一直没怎么做数据分析,当我加入了新公司后发现,现在做大数据的大部分都是数据分析,用python和sql。 我今天一看他们的统计&#xf…

STM32音频应用开发:DMA与定时器的高效协作

摘要: 本文章将深入浅出地介绍如何使用STM32单片机实现音频播放功能。文章将从音频基础知识入手,逐步讲解音频解码、DAC转换、音频放大等关键环节,并结合STM32 HAL库给出具体的代码实现和电路设计方案。最后,我们将通过一个实例演示如何播放W…

NXP RT1060学习总结 - fsl_flexcan CAN FD 函数说明 -1

概要 CAN测试源码: https://download.csdn.net/download/qq_35671135/89425377 根据fsl_flexcan.h文件从文件末尾往前面梳理,总共CAN FD处理函数; 使用的是RT1064开发板进行测试。 1、中止中断驱动的消息接收进程(CAN FD) 函 数 &#xf…

揭示优化Prompt的秘诀:如何让API表现媲美网页版

为什么用GPT API(GPT-3.5-turbo)进行程序分析时,效果好像比网页版的GPT-3.5差一点?这可能有几个原因,咱们细说一下。 1. Prompt不同 这是最常见的问题之一。API调用时的指令(prompt)往往比较简…

Ceil()——向上取整函数

函数原型为: double ceil(double x); 大家可以在这个网站里更清晰的了解ceil - C Reference (cplusplus.com) 下面借助一道例题来帮助大家理解:牛牛的快递_牛客题霸_牛客网 (nowcoder.com) 我们分析题得知,在大于1的情况下,只要…

学懂C#编程:常用高级技术——学会Lambda表达式的应用(二)

上一篇介绍了Lambda基础的知识,接着我们通过讲解Lambda的几种应用场景来彻底学懂Lambda在编程中的应用。 Lambda表达式在C#中非常灵活,可以用在多种场合。以下是一些Lambda表达式示例,帮助你更全面地理解其用途: 1. 数组过滤&am…

【ocean】ocnPrin结合getData导出数据

核心就是这一句ocnPrint(?output fout leafValue( getData(“/output” ?result “dc”))) r_list list(4000, 4100, 4200) multi_list list(20,21,22) fout outfile("/home/yourpath/results.txt" "w") foreach(r_value r_listforeach(multi_value …

【Linux基础命令】文件管理命令(二)

文章目录 前言创建删除目录创建目录删除目录cp命令mv命令查看文件内容总结前言 在我们的上一篇文章中,我们介绍了一些基本的Linux文件管理命令,如 cd、ls 等。这些命令对于日常的文件操作任务非常有用。然而,Linux 提供的功能远不止这些。在本文中,我们将继续探索更多的文…

【unity实战】制作unity数据保存和加载系统——大型游戏存储的最优解

最终效果 文章目录 最终效果前言存储位置信息存储更多数据存储场景信息持久化存储数据完结 前言 前面写过小型游戏存储功能: 【unity实战】制作unity数据保存和加载系统——小型游戏存储的最优解(包含数据安全处理方案的加密解密) 这次做一…

基于RK3568车载电脑助力日本巴士公司高效完成巴士到站系统项目部署

无处不在的物联网(IoT)技术已经渗透到了人类生活的各个角落,如日常出行乘坐的公交车上,物联网(IoT)技术的应用就得到完美诠释!其通过公交车上的车载电脑网络与中控室服务器连接来对公交车的运行…

Vue-Ci搭建项目

项目创建 vue-cli 官方提供的一个脚手架,用于快速生成一个vue的项目模板;预先定义 好的目录结构及基础代码,就好比咱们在创建Maven项目时可以选择创建一个 骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速; 主要的功能 ● 统一的目录结构 ● 本地调试 热部署 ● 单元…