Android 5.0以上提供了MediaProjection,方便截屏录屏等功能。在工作过程中一般系统提供的API即可以满足我们的需求,但是特殊的情况下无法满足本文将通过MediaProjection实现截屏功能以及解决遇到的问题
版权声明: 本站所有博文内容均为原创,如若出现不严谨的地方欢迎指出。转载请务必注明作者与原文链接,且不得篡改原文内容
General Screenshot
1 2 3 4 5 6 7 8 9 10 11 12
| public static Bitmap snapShotWithStatusBar(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 bp = Bitmap.createBitmap(bmp, 0, 0, width, height); view.destroyDrawingCache(); return bp; }
|
像这种基本上就可以满足日常截屏功能,同时也不需要特别的权限。不过当布局中包含WebView 或者 其他特殊的自定义View通过硬件加速处理的情况下可能会出现如下问题:
正常截图:
问题截图:
通过上面的截图我们可以看出曲线图部分并没有被截下来。经过查阅资料以及官方文档,从Android 5.0 开始官方提供了系统级的 MediaProjection API。
MediaProjection 不仅可以进行系统级的截屏还支持屏幕录制,本文暂时只讲解截屏相关问题。
MediaProjection Screenshot
其中,捕捉屏幕是需要用户确认权限才可以,这权限对话框是由createScreenCaptureIntent方法创建的,在用户点击允许之后,在onActivityResult得到确认码,才可以拿到MediaProjection对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| projectionManager = (MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE); startActivityForResult(projectionManager.createScreenCaptureIntent(), REQUEST_CODE); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (RESULT_OK == resultCode && REQUEST_CODE == requestCode) { mediaProjection = projectionManager.getMediaProjection(resultCode, data); ...... } }
mediaProjection.stop();
|
官方及网上代码类似如下:
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 35 36 37 38 39 40 41 42 43 44 45 46 47
|
imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2); virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenShot", width, height, mDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY , imageReader.getSurface(), null, handler); imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { Image image = null; FileOutputStream fos = null; Bitmap bitmap = null; try { image = reader.acquireLatestImage(); if (image != null) { Image.Plane[] planes = image.getPlanes(); ByteBuffer buffer = planes[0].getBuffer(); int pixelStride = planes[0].getPixelStride(); int rowStride = planes[0].getRowStride(); int rowPadding = rowStride - pixelStride * width; bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer); Date currentDate = new Date(); SimpleDateFormat date = new SimpleDateFormat("yyyyMMddhhmmss"); String fileName = DOWNLOAD_DIR + "/screenshot_" + date.format(currentDate) + ".png"; fos = new FileOutputStream(fileName); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); mediaProjection.stop(); } } catch (Exception e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (bitmap != null) { bitmap.recycle(); } if (image != null) { image.close(); } } } }, handler); }
|
Warn
关键代码: windowManager.defaultDisplay.getRealMetrics(metrics)
上面的代码可以实现截屏功能,但是生成的图片会有黑边的问题。注意,用Display获取屏幕尺寸要用真实的尺寸,使用getRealMetrics方法。如果使用getMetrics方法,得到的高度是缺少虚拟导航栏 Navigaiton Bar的高度。得到的尺寸和屏幕不一致,最终得到的图像会是等比例缩放到屏幕大小的图像,然后空白的地方会显示黑边。