android 5.0截图功能,Android录屏与截图功能(5.0以上有效)

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

概述这篇文章,会带你学习如何使用 MediaProjection,MediaCodec 以及 MediaMuxer 来实现简单的截屏和录屏功能。

因为 MediaProjection 是 5.0以上 才出现的,所以今天所讲述功能实现,只在 5.0以上 的系统有效。

截屏

步骤如下

1:获取 MediaProjectionManager

2:通过 MediaProjectionManager.createScreenCaptureIntent() 获取 Intent

3:通过 startActivityForResult 传入 Intent 然后在 onActivityResult 中通过 MediaProjectionManager.getMediaProjection(resultCode,data) 获取 MediaProjection

4:创建 ImageReader,构建 VirtualDisplay

5 : 最后就是通过 ImageReader 截图,就可以从 ImageReader 里获得 Image 对象。

6 : 将 Image 对象转换成 bitmap

实现

步骤已经给出了,我们就按照步骤来实现代码吧。

首先 MediaProjectionManager 是系统服务,我们通过

getSystemService(MEDIA_PROJECTION_SERVICE)获取它1projectionManager = (MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE)

然后调用 startActivityForResult 传入 projectionManager.createScreenCaptureIntent() 创建的 Intent1startActivityForResult(projectionManager.createScreenCaptureIntent(), SCREEN_SHOT)

紧接着我们就可以在 onActivityResult(int requestCode, int resultCode, Intent data) 中通过 resultCode 和 data 来获取 MediaProjection:

FvvwP2iend8mydeWF0tijrsyMnbx.png

然后就是创建 ImageReader 和 VirtualDisplay:

FtclwbgSmfNuAKRzLV7ItmxQ2TUB.png

这里我们依次讲解一下:

首先是 ImageReader.newInstance 方法:

1public static ImageReader newInstance(int width, int height, int format, int maxImages)

方法里接收四个参数。前两个 width,height 是用来指定生成图像的宽和高。

第三个参数 format 是图像的格式,这个格式必须是 ImageFormat或 PixelFormat 中的一个,这两个 Format 里有很多格式,大家可以点进去看看,我们例子中使用的是 PixelFormat.RGBA_8888 格式(需要注意的是并不是所有的格式都被 ImageReader 支持,比如说 ImageFormat.NV21)。

第四个参数 maxImages,这个参数指的是你想同时在 ImageReader 里获取到的 Image对象 的个数,这个参数我不是很懂,我不理解同时的意思。我的理解是 ImageReader 是一个类似数组的东西,然后我们可以通过 acquireLatestImage() 或 acquireNextImage() 方法来得到里面的 Image对象(可能有误,仅供参考)。这个值应该设置的越小越好,但是得大于0,所以我们上面设置的是1。

然后我们看看 mediaProjection.createVirtualDisplay 方法:

FtUczK9qlxRq-TQdEndwzXTgfn6F.png

首先这个方法返回的是 VirtualDisplay。

前四个不用说了,分别是 VirtualDisplay 的名字,宽,高和dpi。

第五个参数,大家可以访问

查看 DisplayManager 的所有flags,我没有具体的研究过,在本次要实现的例子里,除了 VIRTUAL_DISPLAY_FLAG_SECURE 这个会报错,其他的 flags 效果都一样。

第六个参数 是一个 Surface。我这里表达一下我的理解,当 VirtualDisplay 被创建出来时,也就是 createVirtualDisplay 调用后,你在真实屏幕上的每一帧都会输入到 Surface参数 里。也就是说,如果你放个 SurfaceView,然后传入 SurfaceView 的 Surface 那么你在屏幕上的操作都会显示在 SurfaceView 里(这里我们后面录屏会讲)。我们这里传入的是 ImageReader 的 Surface。这其中的逻辑我的理解是这样的,真实屏幕的每一帧都都会传给 ImageReader,根据 ImageReader 的 maxImages参数,比如说 maxImages 是 2,那么 ImageReader 始终保持两帧图片,但这两帧图片是一直随着真实屏幕的操作而更新的(不知道大家有没有听懂)。

第七个参数 是一个回调函数,在 VirtualDisplay 状态改变时调用。因为我们这里没有,所以传 null。

第八个参数 这里我给出原文:“The Handler on which the callback should be invoked, or null if the callback should be invoked on the calling thread’s main Looper.” 因为我翻译不好。不过和普通的 Handler 使用场景类似。

现在我们 ImageReader 和 VirtualDisplay,接下来我们就可以通过 ImageReader 的 acquireLatestImage() 或 acquireNextImage() 来得到 Image对象 了。1

2SystemClock.sleep(1000);

Image image = imageReader.acquireNextImage();

这里有个坑,就是你在获取 Image 的时候,得先暂停1秒左右,不然就会获取失败(原因未知)。

现在我们有了 Image对象,但是 Image对象 并不能直接作为UI资源被使用,我们可以将它转换成 Bitmap对象。

FkMjyveYJT5Rk3XXBS6V8-3xAb31.png

这里最主要的逻辑就是像素与字节的转换,我们需要将 Image对象 的字节流写进 Bitmap 里,但是 Bitmap 接收的是像素格式的。

我们一行一行来看:

首先获取 image对象 的宽和高,注意 width 和 height 是像素格式的。

然后获取 ByteBuffer,里面存放的就是图片的字节流,是字节格式的。我是这么理解的,ByteBuffer 里面是一长串的字节序列,按照某种格式分成行列就变成了图片。

然后获取 PixelStride,这指的是两个像素的距离(就是一个像素头部到相邻像素的头部),这是字节格式的。

RowStride 是一行占用的距离(就是一行像素头部到相邻行像素的头部),这个大小和 width 有关,这里需要注意,因为内存对齐的原因,所以每行会有一些空余。这个值也是字节格式的。

紧接着我们需要创建一个 Bitmap 用来接受 Image 的 buffer 的输入,buffer 是字节流,它会按照我们设置的 format 转换成像素,所以这里最重要的一个地方就是 Bitmap 创建的大小,因为高度就是行数所以就是 height,但是宽度因为上面说的内存对齐问题会有些空余,所以我们要先求出空余部分,然后加上 width。1int rowPadding = rowStride - pixelStride * width;

这句话用整行的距离减去了一行里像素及空隙占用的距离,剩下的就是空余部分。但是这个是字节格式的。我们将它除以 pixelStride,也就是一个像素及空隙占用的字节大小,就转换成了像素格式。然后:1width + rowPadding / pixelStride

这个就是一行里像素的占用了,我们将它传给Bitmap:

FjyAwd7uOuIyoNuuaec1qCa3nymr.png

创建出合适大小的 Bitmap,然后把 Image 的 buffer 传给它,就成功的将 Image对象 转换成了 Bitmap。这里我可能讲的不清楚,我给大家画了张图:

FnWlneYKo0wXmd93bqaRDI4M34d4.png

上面的一小格一小格是一块块像素。

好了,现在我们已经获取到了 bitmap 了,我们可以把它放到 ImageView 里显示一下,我写了一个例子,效果如下:

FtWvLYP7rKhdUAWUPx34llXrOGK3.png

点击按钮,弹出一个对话框请求截屏,点击立即开始的话,截屏就会显示在下面的 ImageView 里。

录屏

步骤

录屏的前三步和截屏是一样的,出现分歧点的地方在于 VirtualDisplay 创建时传入的 Surface,还记得我们上面说的吗,说在创建 VirtualDisplay 的时候,传入一个 SurfaceView 的 Surface 的话,那么你在真实屏幕上的操作,都会重现在 SurfaceView 上。我们来试一下:

FnytHOMHEcK-U27XqYMkI8d69TrV.png

我们在Surface参数中传入一个 SurfaceView 的 Surface,效果如下:

FrUySmrKvQ8DIsbBwUNS2O_VRUMk.png

可以看到我们放了一个 Button,放了一个 ImageView,放了一个 SurfaceView。点击 Button,然后点立即开始之后,真实屏幕就映射到了 SurfaceView 里。

所以当创建 VirtualDisplay 时,真实屏幕就映射到了 Surface,也就是我们可以再 Surface 里拿到屏幕的一个输入。那我们要录屏的话,就只要把 Surface 转换成我们需要的格式就行了。

在本篇文章的例子中,我们会将 Surface对象 转换成 mp4格式。这就需要用到MediaCodec类 和 MediaMuxer类。MediaCodec 生成一个 Surface 用来接收屏幕的输出并按照格式编码,然后传给 MediaMuxer 用来封装成 mp4格式 的视频。

Frwb1-cIhukMvnpralVVIChnGnxj.png

上面讲了 MediaCodec 的创建,我们也可以从中看到屏幕数据是怎么进入 MediaCodec 的。具体的我已经注释了。

接下来我们创建一个 MediaMuxer对象:

FghD5GELyVoh3Q10yfrrEyxvGmIJ.png

然后创建 VirtualDisplay,把 MediaCodec的surface 传进去:

FlSvfgcwQfAnOYlwK965rqsxvQw1.png

最后就是视频的编码与转换MP4还有保存了:

FkgEsiyPE32QpetqNVJ-1RJsZZy2.png

好了,录屏到此结束了。我们来看下实例演示:

FuksI4ntCx3D9s-GcPmdh6vpQHu9.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值