Android三种方式截取任意界面屏幕

2023-05-16

一、使用MediaProjectionManager

Android5.0之后,开放截取屏幕的API,也就是利用MediaProjectionManager创建VirtualDisplay,传入与ImageReader关联的Surface,这样就可以从ImageReader中获取到Image,然后把Image的像素数组拷贝到Bitmap,如果要保存为图片,就用得到的Bitmap压缩为JPEG格式的图片。

首先是使用startActivityForResult发起录屏的请求:

    private void startScreenShot(){
        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        if (windowManager != null) {
            DisplayMetrics displayMetrics = new DisplayMetrics();
            windowManager.getDefaultDisplay().getMetrics(displayMetrics);
            width = displayMetrics.widthPixels;
            height = displayMetrics.heightPixels;
            dpi = displayMetrics.densityDpi;
        }

        mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        if (mediaProjectionManager != null) {
            startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 123);
        }
    }

在得到用户授权录屏后,在onActivityResult中获取MediaProjection-->VirtualDisplay-->ImagReader-->Image-->Bitmap:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        MediaProjection mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
        if (mediaProjection != null){
            getBitmap(mediaProjection);
        }
    }
private void getBitmap(MediaProjection mediaProjection){
        ImageReader imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 3);
        mediaProjection.createVirtualDisplay("screen_shot",
                width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                imageReader.getSurface(), null, null);
        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireNextImage();
                int width = image.getWidth();
                int height = image.getHeight();
                final Image.Plane[] planes = image.getPlanes();
                final ByteBuffer buffer = planes[0].getBuffer();
                int pixelStride = planes[0].getPixelStride();
                int rowStride = planes[0].getRowStride();
                int rowPadding = rowStride - pixelStride * width;
                Bitmap bitmap = Bitmap.createBitmap(width+rowPadding/pixelStride, height, Bitmap.Config.ARGB_8888);
                bitmap.copyPixelsFromBuffer(buffer);
                String filePath = Environment.getExternalStorageDirectory().getPath() + "/hello.jpg";
                //bitmap保存为图片
                saveBitmap(bitmap, filePath);
                image.close();
            }
        }, null);

    }
private void saveBitmap(Bitmap bitmap, String filePath){
        try {
            FileOutputStream outputStream = new FileOutputStream(filePath);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

优点:不用系统签名,不依赖系统底层API;

缺点:弹出确认框,需要用户授权录屏;

 

二、使用SurfaceControl

从上面分析可知,MediaProjectionManager录屏依赖Surface,分析Surface源码后,发现Surface其实是调用SurfaceControl,也就是说可以用SurfaceControl走捷径去截屏。其实系统框架层的截屏也是调用SurfaceControl。但是,由于SurfaceControl属于系统API,对用户不开放,我们无法直接调用。说到这里,大家应该都想到,用反射机制来调用。

正确的调用方式是这样的:

Bitmap bitmap = SurfaceControl.screenshot(width, height);

通过系统源码,可以知道它在Android.view.SurfaceControl路径下。既然路径、类、方法、参数都知道了,反射就没问题:

	//使用反射调用截屏
	private void screenShotByReflect(){
		DisplayMetrics mDisplayMetrics = new DisplayMetrics();
		float[] dims = { mDisplayMetrics.widthPixels,
				mDisplayMetrics.heightPixels };
		try {
			Class<?> demo = Class.forName("android.view.SurfaceControl");
			Method method = demo.getDeclaredMethod("screenshot", int.class,int.class);
			mScreenBitmap = (Bitmap) method.invoke(null,(int) dims[0],(int) dims[1]);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

在调用截屏之前,判断屏幕是否发生旋转:

	private Bitmap takeScreenshot() {
		 mDisplay.getRealMetrics(mDisplayMetrics);
		 float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
		float degrees = getDegreesForRotation(mDisplay.getRotation());
		boolean requiresRotation = (degrees > 0);
		//如果屏幕发生旋转,通过matrix旋转回来
		if (requiresRotation) {
			mDisplayMatrix.reset();
			mDisplayMatrix.preRotate(-degrees);
			mDisplayMatrix.mapPoints(dims);
			dims[0] = Math.abs(dims[0]);
			dims[1] = Math.abs(dims[1]);
		}
		//调用截屏
		screenShotByReflect();
		return mScreenBitmap;
	}

优点:不用弹框授权,不用系统签名;

缺点:使用反射机制,如果系统API或者方法发生更改,导致无法调用;

 

三、使用screencap的adb命令

命令行是这样的:adb shell screencap -p file_path

在代码中执行,就不用adb shell,直接screencap -p file_path,调用Runtime的进程来执行:

	public static void screenShotByShell(String filePath){
		String shotCmd = "screencap -p " + filePath + " \n";
		try {
			Runtime.getRuntime().exec(shotCmd);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

优点:代码简单,直接获取到图片;

缺点:需要系统签名;

 

以上三种截屏方式,大家可以根据应用场景来使用。如果是用户app,需要连续录屏,建议采用MediaProjectionManager;如果是希望得到单个Bitmap,那么可以用SurfaceControl;如果是系统app,并且希望得到图片,首选screencap的adb命令行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android三种方式截取任意界面屏幕 的相关文章

  • 在第一个框中输入字符后,将焦点转到下一个框

    我必须实现一个应用程序 其中我设置了较小的值edittext用于输入 PIN 码和手机号码 每个编辑文本一次包含 1 个字符 现在 当我运行这个应用程序时 我发现对于每个框 我需要将焦点放在每个框上edittext 因此 在这种情况下 是否
  • Android 卷页动画

    我对 Android 动画有点陌生 目前我正在开发一个故事活动 需要像 iPhone 中那样使用卷页动画 我发现 iPhone 中有一种方法可以做到这一点 但我仍然找不到在android中做的方法 所以请帮我解决这个问题 谢谢大家 谷歌代码
  • PopupWindow onitemclick 中的 Android Listview 在某些设备上不起作用

    我的 ListView 在 PopupWindow 内 当我显示 PopupWindow 并单击设备 ASUS K00z 中的 Listview 行时 fonepad 工作得很好 但在 HTC Z715e 中不起作用 项目单击事件未触发 1
  • ViewFlipper中的VideoView在播放视频时是透明的

    我有一个 Activity 在 ViewFlipper 中设置了两个视图 其中一个视图是带有 GLSurfaceView 和一些其他小部件的布局 另一个视图只有带有 TextView 和 VideoView 的布局 当我单击 GLSurfa
  • Twowayview 滚动时自动添加内边距

    我在用双向视图 https github com lucasr twoway view在我的一个项目中 这是android的扩展回收者视图 https developer android com reference android supp
  • 地理编码 API 与地理编码器

    在我的应用程序中 我需要使用地理编码 但我不太清楚该使用哪种方法 直到昨天我在URL中添加了参数maps googleapis com maps api geocode json address myparameter sensor fal
  • 有人可以给出一个 android 中 webview 实现的确切例子吗

    嗨 我正在使用开发 Android 应用程序WebView执行 我跟着官方安卓教程 http developer android com resources tutorials views hello webview html 在 Ecli
  • Android Studio 安装失败,APK 未签名

    最近从 Eclipse 更改为 Android Studio 我还更改了 JDKjava open jdk to jdk1 7 0 45 现在我尝试运行我的第一个应用程序 并收到以下消息 Installation failed since
  • Android WebView文件上传

    我正在开发一个 Android 应用程序 基本上它是一个WebView和一个进度条 Facebook 的移动网站 m facebook com 已加载到WebView 当我单击 选择文件 按钮上传图像时 没有任何反应 我已经尝试了所有的解决
  • ProgressBar.setInminateDrawable() 不起作用

    当我尝试更改我的 indeteminateDrawable 进度条就消失了 我必须更改我的进度条的可绘制对象 我尝试了invalidate requestLayout等 我不知道如何解决它 谢谢 这里的代码 progressBar setI
  • 合并 dex 程序类型已存在时出错:android.support.v4.os.ResultReceiver$MyResultReceiver

    合并dex时出错 以下是依赖项 ext anko version 0 10 5 support lib 1 0 0 alpha1 room lib 1 1 0 dependencies implementation org jetbrain
  • java.net.ProtocolException:流意外结束

    我面临一个奇怪的问题 并且无法调试它 我已经实现了上传数据流的逻辑 并使用 Volley 来实现相同的功能 我在HurlStack addBodyIfExistsapi 以便可以处理 application octet stream 类型的
  • Android 媒体播放器搜索栏

    我有一个创建 播放和处理媒体播放器 只是音频 的服务 但我在主要活动中有一个搜索栏 我想自然地显示音频文件的进度并允许用户搜索到不同的位置 我花了很长时间才弄清楚 将 UI 中的搜索栏连接到服务中的媒体播放器的最佳或正确方法是什么 我将这样
  • 如何使用共享首选项在两个 Android 应用程序之间共享数据?

    我有两个应用程序 App1 和 App2 我想使用共享首选项在 App1 中保存数据并在 App2 中访问 反之亦然 我可以在 App1 中保存数据并在 App2 中访问数据 但反之则不行 这就是我现在正在做的 在清单中 android s
  • phonegap html5 android 同步文件系统 IO

    如何使用 PhoneGaps 文件系统 API 同步读写文件 有可用的同步包装器吗 无法通过提供的 api 同步访问文件 从phonegap的实现方式猜测 我怀疑您是否可以编写一个插件来同步执行此操作
  • 如何获取小区广播消息?

    我尝试像模拟人生一样获取小区广播消息的文本 但它不起作用 public class SMSReceiver extends BroadcastReceiver Override public void onReceive Context c
  • 如果我的应用程序安装在 SD 卡上,私人数据也在那里吗?

    我假设应用程序的私有数据 例如 SharedPreferences 和 SQLite 数据库 位于手机的内部存储而不是 SD 卡上 即使应用程序本身安装在 SD 卡上 我在任何地方都找不到对此的简单明确的确认 有人可以确认一下吗 是的 私有
  • Android Jetpack Compose 尺寸随持续时间变化的动画

    如何在 Jetpack Compose 中添加内容大小更改动画的持续时间 尝试使用Modifier animateContentSize 并通过动画规格具有持续时间 但它只是突然进入或退出 没有观察到持续时间 Column Modifier
  • 传递 Android DialogFragment 参数时,onCreateDialog 捆绑参数意外为 null

    我正在尝试使用 DialogFragment 在 Android 中显示一个基本对话框 并使用对话框消息的参数 如中所述StackOverflow线程 https stackoverflow com questions 15459209 p
  • Android Volley - 发布请求 - 无法在线工作

    我试图通过 Volley 发出 POST 请求 它在我的本地主机中工作得很好 但是当我将它移动到网络服务器时 响应为空 Java代码 RequestQueue queue Volley newRequestQueue this String

随机推荐

  • 模式识别:最小错误率贝叶斯决策分类

    一 引言 1 用贝叶斯决策理论分类要事先知道两个条件及要求 xff1a 各类的先验概率 xff1a 及特征向量的条件概率密度 xff1a 或后验概率 xff1a 决策分类的类别一定 2 解决的问题 xff1a 已知一定数目的样本 xff0c
  • 模式识别:BP神经网络算法

    1 BP神经网络分类器 1 1 BP算法基本原理 神经网络结构大概如下图1 1 xff1a 图1 1 包括输入层 xff0c 隐层和输出层 包含一层隐层的神经网络称为浅层神经网络 xff0c 即SNN 包含多层隐层的神经网络称为深度神经网络
  • 模式识别:C-means(K-means)聚类算法与分级聚类(层次聚类)算法

    C均值聚类算法与分级聚类算法的聚类分析 一 实验目的 理解聚类的整体思想 xff0c 了解聚类的一般方法 xff1b 掌握 C means与分级聚类算法算法思想及原理 xff0c 并能够熟练运用这些算法进行聚类分析 xff1b 能够分析二者
  • ROS 配置多网口通讯

    列出当前所有的网络设备 ifconfig a 结果如下 xff1a enp1s0 Link encap Ethernet HWaddr 00 2f 5c 68 06 ad inet addr 192 168 1 101 Bcast 192
  • qt creator开启openMP加速方法

    环境 Qt creator4 11 for msvc2017 内置openmp库 启用方法 1 在pro文件加上QMAKE CXXFLAGS 43 61 openmp 2 添加头文件omp h
  • c++中::的用法

    是运算符中等级最高的 xff0c 它分为三种 1 global scope 全局作用域符 xff09 xff0c 用法 xff08 name 2 class scope 类作用域符 xff09 xff0c 用法 class name 3 n
  • 【ubuntu】——gflags&glog卸载与安装

    gflags glog 通过apt安装的glog xff0c gflags没有config cmake xff0c 所以在一些情况下需要手动编译 1 卸载gflags amp glog 只适用于通过apt安装的方式 span class t
  • 【算法】A* 寻路 可视化

    如下图 寻路图A 使用A 算法 xff0c 需要将地图抽象成一个个方块 xff0c 蓝色代表不可以动 墙 xff0c 黄色为起始点 xff0c 红色为目标点 其地图的二维坐标如图所示 xff0c 每一个单位为1米 A 的基本公式为 F n
  • 实验室新生成长指南[2.2.1] · 连接器

    欢迎进入 实验室新生成长指南 第二章 xff1a 硬件 本篇是 实验室新生成长指南 第二章第二节第一篇 xff1a 连接器 整个2 2节将帮助新手快速建立设计电路系统的一些基本知识储备 更多关于 实验室新生成长指南 的介绍 xff0c 请前
  • 走进音视频的世界——音视频的基本概念

    音视频通用的基本概念有码率 时长 xff0c 而不同音视频有不同的封装格式 编码协议 其中视频关键参数有分辨率 帧率 画质 旋转角度 像素格式 xff0c 而音频关键参数有采样率 声道数 声道布局 音质 采样数 采样位数 帧时长 接下来与大
  • 走进音视频的世界——新一代开源编解码器AV1

    AOMedia Video 1 xff08 AV1 xff09 是一种开源 免版税的编解码器 xff0c 最初设计用于Internet上的视频传输 它是由开放媒体联盟 xff08 AOMedia xff09 于VP9的继任者开发的 xff0
  • FFmpeg源码分析:avformat_find_stream_info分析码流信息

    FFmpeg在调用avformat open input 之后 xff0c 可能码流信息不够完整 xff0c 可以使用avformat find stream info 获取更多的码流信息 比如获取视频帧率 视频宽高 xff0c 重新计算最
  • Miracast投屏协议深入剖析

    Miracast由WiFi联盟制定 xff0c 以WiFi Direct IEEE802 11为无线传输标准 xff0c 允许手机向电视或其他接收设备进行无线投送视频 图片 和Miracast类似的投屏协议 xff0c 还有Airplay
  • c++:DFS与BFS详解

    DFS xff08 深度优先搜索 xff09 xff1a 从某个状态开始 xff0c 不断转移状态到无法转移为止 xff0c 然后退回到前一步 xff0c 继续转移到其他状态 xff0c 不断重复 xff0c 直至找到最终的解 总是从最开始
  • 一文掌握OpenGL的shader内置函数

    OpenGL ES有大量的GLSL内置函数 xff0c 包括 xff1a 三角函数 指数函数 通用函数 浮点函数 几何函数 矩阵函数 矢量关系函数 纹理函数 原子函数 图像函数 插值函数等 目录 一 三角函数 1 radians degre
  • 安全可靠的SRT实时传输协议

    Secure Reliable Transport SRT 是安全 可靠 低延时的多媒体实时传输协议 SRT协议使用AES进行数据加密 xff0c 运用FEC进行前向纠错 xff0c 并且有流量控制 拥塞控制 类似于QUIC协议 xff0c
  • android端使用openCV实现车牌检测

    现在 xff0c 汽车的踪影无处不在 xff0c 公路上疾驰 xff0c 大街边临停 xff0c 小区中停靠 xff0c 车库里停泊 管理监控如此庞大数量的汽车是个头疼的问题 精明的人们把目光放在车牌上 xff0c 因为车牌是汽车的 身份证
  • android端使用openCV与深度学习实现车牌识别

    车牌识别的应用场景随处可见 xff1a 高速公路上超速抓拍 小区门口关卡 车库入口关卡 xff0c 甚至出现在车载设备上 它的工作原理大致这样 xff1a 使用摄像头充当 眼睛 xff0c 使用openCV与深度学习充当 大脑 实时车牌识别
  • FFmpeg音频处理——音频混合、拼接、剪切、转码

    接触FFmpeg有一段时间了 xff0c 它是音视频开发的开源库 xff0c 几乎其他所有播放器 直播平台都基于FFmpeg进行二次开发 本篇文章来总结下采用FFmpeg进行音频处理 xff1a 音频混合 音频剪切 音频拼接与音频转码 采用
  • Android三种方式截取任意界面屏幕

    一 使用MediaProjectionManager Android5 0之后 xff0c 开放截取屏幕的API xff0c 也就是利用MediaProjectionManager创建VirtualDisplay xff0c 传入与Imag