一个需要截屏的需求,并且如果可以做到的话,需要截取用户的状态栏图象。
方法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 );
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) { 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 : if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 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) { 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的步骤仅供参考。