Android实现截屏的两种方式

一个需要截屏的需求,并且如果可以做到的话,需要截取用户的状态栏图象。

方法1 使用View的DrawCache来获取当前显示的内容

优点:方法简单。
缺点:无法截取到状态栏的内容,包括时间,电量,wifi,4G状态等。

注意这里要使用Bitmap.createBitmap否则Bitmap可能会被回收造成崩溃。

1
2
3
4
View rootView = activity.getWindow().getDecorView().getRootView();
rootView.setDrawingCacheEnabled(true);
Bitmap drawingCache = Bitmap.createBitmap(rootView.getDrawingCache());
rootView.setDrawingCacheEnabled(false);

方法2 使用MediaProjectionManager

MediaProjectionManager是一个系统级的服务,用于采集屏幕上的信息。可以用来实现截屏或录屏操作。

优点:可以截取状态栏的图象信息。
缺点:在各种设备上的体验并不是很好,因为每次都需要确认权限,并且MediaProjectionManager不回收,会造成设备发热等问题。

首先需要获取屏幕截图权限,但是在Android10上的机器会反复弹出采集屏幕的权限提示,无法选择永久允许。
原生可以选择。Android10的MIUI华为测试不可永久允许。

1
2
3
4
5
6
7
8
public void requestCapturePermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//5.0 之后才允许使用屏幕截图
return;
}
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 1);
}

当用户确认权限后,可以在Result的返回中,获取到MediaProjection对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1: //REQUEST_MEDIA_PROJECTION
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//5.0 之后才允许使用屏幕截图
return;
}
if (resultCode == Activity.RESULT_OK && data != null) {
MediaProjection mediaProjection = ((MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE))
.getMediaProjection(Activity.RESULT_OK, data);
} else {
//如果获取失败的处理,可以选择再次请求
requestCapturePermission();
}
break;
}
}

设置MediaProjection

1
2
3
4
5
6
7
public void setMediaProjection(MediaProjection mediaProjection) {
this.mediaProjection = mediaProjection;
screenHeight = DisplayUtils.getScreenHeight();
screenWidth = DisplayUtils.getScreenWidth();
density = App.getInstance().getResources().getDisplayMetrics().density;
imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 2);
}

当开始截屏时,使用virtualDisplay执行以下操作,来获取图像(截屏),图像信息保存在imageReader中。

1
2
3
4
5
6
7
8
9
10
private void startVirtual() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//5.0 之后才允许使用屏幕截图
return;
}
virtualDisplay = mediaProjection.createVirtualDisplay("ScreenShot",
screenWidth, screenHeight, (int) density,
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
imageReader.getSurface(), null, null);
}

提示:截屏这里需要放在Runnable里面执行,否则有可能获取不到图像。
当无法获得Image时,重新执行上面的操作,直到获取到图象为止。
通过imageReader.acquireLatestImage()就可以获取到Image对象,并可以转换成为Bitmap完成截图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void startScreenShot() {
startVirtual();
startCapture();
}

private void startCapture() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Image image = imageReader.acquireLatestImage();
if (image == null) {
releaseDisplay();
startScreenShot();
Timber.e("Capture Failed");
return;
}

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);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
image.close();
}
}, 100);
}

最后执行释放资源的操作

1
2
3
4
5
6
7
8
9
10
11
private void releaseResourse() {
if (virtualDisplay != null) {
virtualDisplay.release();
virtualDisplay = null;
}

if (mediaProjection != null) {
mediaProjection.stop();
mediaProjection = null;
}
}

mediaProjection可以释放,但是释放之后,再次请求获取屏幕就需要重新申请权限了。

目前存在的问题,第一张截图有可能为空,再次获取就会正常。
所以说方法2的代码需要优化。但是后面由于获取权限等问题,对于用户过于不友好,换回使用1方法了,所以方法2的步骤仅供参考。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×