Android应用截图和SurfaceView截图问题总结
最近在做android截图应用的过程遇到很多问题,接触了好些截图方法,但是还是不能实现SufaceView截图功能。今天就把我尝试过的方法总结下,希望把我惨痛的经历写出来后能够帮助到要做此功能的朋友少走弯路,或者是给一些思路吧。如果哪位大侠能够做到SurfaceView截图,还请分享下思路。
一、无SurfaceView情况下的截图功能实现
public static Bitmap takeScreenShot(Activity act) { if (act == null || act.isFinishing()) { Log.d(TAG, "act参数为空."); return null; } // 获取当前视图的view View scrView = act.getWindow().getDecorView(); scrView.setDrawingCacheEnabled(true); scrView.buildDrawingCache(true); // 获取状态栏高度 Rect statuBarRect = new Rect(); scrView.getWindowVisibleDisplayFrame(statuBarRect); int statusBarHeight = statuBarRect.top; int width = act.getWindowManager().getDefaultDisplay().getWidth(); int height = act.getWindowManager().getDefaultDisplay().getHeight(); Bitmap scrBmp = null; try { // 去掉标题栏的截图 scrBmp = Bitmap.createBitmap( scrView.getDrawingCache(), 0, statusBarHeight, width, height - statusBarHeight); } catch (IllegalArgumentException e) { Log.d("", "#### 旋转屏幕导致去掉状态栏失败"); } scrView.setDrawingCacheEnabled(false); scrView.destroyDrawingCache(); return scrBmp; }
这种情况只能够截取普通应用的截图,当应用含有SurfaceView时, SurfaceView区域为黑色,具体什么原因,请移步:http://blog.csdn.net/luoshengyang/article/details/8661317。
二、含有SurfaceView的截图实现
2.1 系统root的情况下的实现代码
private static final String DEVICE_NAME = "/dev/graphics/fb0"; @SuppressWarnings("deprecation") public static Bitmap acquireScreenshot(Context context) { WindowManager mWinManager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics metrics = new DisplayMetrics(); Display display = mWinManager.getDefaultDisplay(); display.getMetrics(metrics); // 屏幕高 int height = metrics.heightPixels; // 屏幕的宽 int width = metrics.widthPixels; int pixelformat = display.getPixelFormat(); PixelFormat localPixelFormat1 = new PixelFormat(); PixelFormat.getPixelFormatInfo(pixelformat, localPixelFormat1); // 位深 int deepth = localPixelFormat1.bytesPerPixel; byte[] arrayOfByte = new byte[height * width * deepth]; try { // 读取设备缓存,获取屏幕图像流 InputStream localInputStream = readAsRoot(); DataInputStream localDataInputStream = new DataInputStream( localInputStream); localDataInputStream.readFully(arrayOfByte); localInputStream.close(); int[] tmpColor = new int[width * height]; int r, g, b; for (int j = 0; j < width * height * deepth; j += deepth) { b = arrayOfByte[j] & 0xff; g = arrayOfByte[j + 1] & 0xff; r = arrayOfByte[j + 2] & 0xff; tmpColor[j / deepth] = (r << 16) | (g << 8) | b | (0xff000000); } // 构建bitmap Bitmap scrBitmap = Bitmap.createBitmap(tmpColor, width, height, Bitmap.Config.ARGB_8888); return scrBitmap; } catch (Exception e) { Log.d(TAG, "#### 读取屏幕截图失败"); e.printStackTrace(); } return null; } /** * @Title: readAsRoot * @Description: 以root权限读取屏幕截图 * @throws Exception * @throws */ public static InputStream readAsRoot() throws Exception { File deviceFile = new File(DEVICE_NAME); Process localProcess = Runtime.getRuntime().exec("su"); String str = "cat " + deviceFile.getAbsolutePath() + "\n"; localProcess.getOutputStream().write(str.getBytes()); return localProcess.getInputStream(); }
系统root的时候,可以直接读取frame buffer, 因此即使含有SurfaceView也能够读取整个窗口的图像。
2.2 使用SurfaceView和MediaPlayer来播放视频的情况,这里只获取SurfaceView的图像
/** * @Title: getVideoFrame * @Description: 获取视频某帧的图像,但得到的图像并不一定是指定position的图像。 * @param path 视频的本地路径 * @param position 视频流播放的position * @return Bitmap 返回的视频图像 * @throws */ @SuppressLint("NewApi") public static Bitmap getVideoFrame(String path, MediaPlayer mediaPlayer) { Bitmap bmp = null; // android 9及其以上版本可以使用该方法 MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { retriever.setDataSource(path); // 这一句是必须的 String timeString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); // 获取总长度,这一句也是必须的 long titalTime = Long.parseLong(timeString) * 1000; long videoPosition = 0; try { mediaPlayer.setDataSource(path); if (path.startsWith("http")) { mediaPlayer.prepareAsync(); } else { mediaPlayer.prepare(); } int duration = mediaPlayer.getDuration(); // 通过这个计算出想截取的画面所在的时间 videoPosition = titalTime * position / duration; } catch (IOException e) { e.printStackTrace(); } if (videoPosition > 0) { bmp = retriever.getFrameAtTime(videoPosition, MediaMetadataRetriever.OPTION_CLOSEST); } } catch (IllegalArgumentException ex) { ex.printStackTrace(); } catch (RuntimeException ex) { ex.printStackTrace(); } finally { try { retriever.release(); } catch (RuntimeException ex) { } } return bmp; }
以上代码单纯的获取 SurfaceView上的图像,其他控件的图像并没有获取。其原理是读取本地文件某一position的图像,而不是截图。