Android应用截图和SurfaceView截图问题总结

最近在做android截图应用的过程遇到很多问题,接触了好些截图方法,但是还是不能实现SufaceView截图功能。今天就把我尝试过的方法总结下,希望把我惨痛的经历写出来后能够帮助到要做此功能的朋友少走弯路,或者是给一些思路吧。如果哪位大侠能够做到SurfaceView截图,还请分享下思路。

   一、无SurfaceView情况下的截图功能实现

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public static Bitmap takeScreenShot(Activity act) {  
  2.     if (act == null || act.isFinishing()) {  
  3.         Log.d(TAG, "act参数为空.");  
  4.         return null;  
  5.     }  
  6.   
  7.     // 获取当前视图的view  
  8.     View scrView = act.getWindow().getDecorView();  
  9.     scrView.setDrawingCacheEnabled(true);  
  10.     scrView.buildDrawingCache(true);  
  11.   
  12.     // 获取状态栏高度  
  13.     Rect statuBarRect = new Rect();  
  14.     scrView.getWindowVisibleDisplayFrame(statuBarRect);  
  15.     int statusBarHeight = statuBarRect.top;  
  16.     int width = act.getWindowManager().getDefaultDisplay().getWidth();  
  17.     int height = act.getWindowManager().getDefaultDisplay().getHeight();  
  18.   
  19.     Bitmap scrBmp = null;  
  20.     try {  
  21.         // 去掉标题栏的截图  
  22.         scrBmp = Bitmap.createBitmap( scrView.getDrawingCache(), 0, statusBarHeight,  
  23.                 width, height - statusBarHeight);  
  24.     } catch (IllegalArgumentException e) {  
  25.         Log.d("""#### 旋转屏幕导致去掉状态栏失败");  
  26.     }  
  27.     scrView.setDrawingCacheEnabled(false);  
  28.     scrView.destroyDrawingCache();  
  29.     return scrBmp;  
  30. }  
这种情况只能够截取普通应用的截图,当应用含有SurfaceView时, SurfaceView区域为黑色,具体什么原因,请移步: http://blog.csdn.net/luoshengyang/article/details/8661317


二、含有SurfaceView的截图实现

 2.1 系统root的情况下的实现代码

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static final String DEVICE_NAME = "/dev/graphics/fb0";  
  2. @SuppressWarnings("deprecation")  
  3.     public static Bitmap acquireScreenshot(Context context) {  
  4.         WindowManager mWinManager = (WindowManager) context  
  5.                 .getSystemService(Context.WINDOW_SERVICE);  
  6.         DisplayMetrics metrics = new DisplayMetrics();  
  7.         Display display = mWinManager.getDefaultDisplay();  
  8.         display.getMetrics(metrics);  
  9.         // 屏幕高  
  10.         int height = metrics.heightPixels;  
  11.         // 屏幕的宽  
  12.         int width = metrics.widthPixels;  
  13.   
  14.         int pixelformat = display.getPixelFormat();  
  15.         PixelFormat localPixelFormat1 = new PixelFormat();  
  16.         PixelFormat.getPixelFormatInfo(pixelformat, localPixelFormat1);  
  17.         // 位深  
  18.         int deepth = localPixelFormat1.bytesPerPixel;  
  19.   
  20.         byte[] arrayOfByte = new byte[height * width * deepth];  
  21.         try {  
  22.             // 读取设备缓存,获取屏幕图像流  
  23.             InputStream localInputStream = readAsRoot();  
  24.             DataInputStream localDataInputStream = new DataInputStream(  
  25.                     localInputStream);  
  26.             localDataInputStream.readFully(arrayOfByte);  
  27.             localInputStream.close();  
  28.   
  29.             int[] tmpColor = new int[width * height];  
  30.             int r, g, b;  
  31.             for (int j = 0; j < width * height * deepth; j += deepth) {  
  32.                 b = arrayOfByte[j] & 0xff;  
  33.                 g = arrayOfByte[j + 1] & 0xff;  
  34.                 r = arrayOfByte[j + 2] & 0xff;  
  35.                 tmpColor[j / deepth] = (r << 16) | (g << 8) | b | (0xff000000);  
  36.             }  
  37.             // 构建bitmap  
  38.             Bitmap scrBitmap = Bitmap.createBitmap(tmpColor, width, height,  
  39.                     Bitmap.Config.ARGB_8888);  
  40.             return scrBitmap;  
  41.   
  42.         } catch (Exception e) {  
  43.             Log.d(TAG, "#### 读取屏幕截图失败");  
  44.             e.printStackTrace();  
  45.         }  
  46.         return null;  
  47.   
  48.     }  
  49.   
  50.     /** 
  51.      * @Title: readAsRoot 
  52.      * @Description: 以root权限读取屏幕截图 
  53.      * @throws Exception 
  54.      * @throws 
  55.      */  
  56.     public static InputStream readAsRoot() throws Exception {  
  57.         File deviceFile = new File(DEVICE_NAME);  
  58.         Process localProcess = Runtime.getRuntime().exec("su");  
  59.         String str = "cat " + deviceFile.getAbsolutePath() + "\n";  
  60.         localProcess.getOutputStream().write(str.getBytes());  
  61.         return localProcess.getInputStream();  
  62.     }  

系统root的时候,可以直接读取frame buffer, 因此即使含有SurfaceView也能够读取整个窗口的图像。

 

 2.2 使用SurfaceView和MediaPlayer来播放视频的情况,这里只获取SurfaceView的图像

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.    * @Title: getVideoFrame 
  3.    * @Description: 获取视频某帧的图像,但得到的图像并不一定是指定position的图像。 
  4.    * @param path     视频的本地路径    
  5.    * @param position 视频流播放的position 
  6.    * @return Bitmap 返回的视频图像 
  7.    * @throws 
  8.    */  
  9.   @SuppressLint("NewApi")  
  10.   public static Bitmap getVideoFrame(String path, MediaPlayer mediaPlayer) {  
  11.   
  12.       Bitmap bmp = null;  
  13.       // android 9及其以上版本可以使用该方法  
  14.       MediaMetadataRetriever retriever = new MediaMetadataRetriever();  
  15.       try {  
  16.           retriever.setDataSource(path);  
  17.           // 这一句是必须的  
  18.           String timeString =  
  19.                   retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);  
  20.           // 获取总长度,这一句也是必须的  
  21.           long titalTime = Long.parseLong(timeString) * 1000;  
  22.   
  23.           long videoPosition = 0;  
  24.           try {  
  25.               mediaPlayer.setDataSource(path);  
  26.               if (path.startsWith("http")) {  
  27.                   mediaPlayer.prepareAsync();  
  28.               } else {  
  29.                   mediaPlayer.prepare();  
  30.               }  
  31.               int duration = mediaPlayer.getDuration();  
  32.               // 通过这个计算出想截取的画面所在的时间  
  33.               videoPosition = titalTime * position / duration;  
  34.           } catch (IOException e) {  
  35.               e.printStackTrace();  
  36.           }   
  37.           if (videoPosition > 0) {  
  38.               bmp = retriever.getFrameAtTime(videoPosition,  
  39.                       MediaMetadataRetriever.OPTION_CLOSEST);  
  40.           }   
  41.       } catch (IllegalArgumentException ex) {  
  42.           ex.printStackTrace();  
  43.       } catch (RuntimeException ex) {  
  44.           ex.printStackTrace();  
  45.       } finally {  
  46.           try {  
  47.               retriever.release();  
  48.           } catch (RuntimeException ex) {  
  49.           }  
  50.       }  
  51.       return bmp;  
  52.   }  
以上代码单纯的获取 SurfaceView上的图像,其他控件的图像并没有获取。其原理是读取本地文件某一position的图像,而不是截图。


2.3  没有root,又有SurfaceView,还想截图 ?
       这个方法我并没有成功,也不知道是否真的能够实现,这里给出相关资料,希望有需要的朋友能够得到帮助,如果能够实现,还请留个言指教一下,不甚感激。

       《Pro android c++ with NDK》的第十二张的Using Graphic API中的Using Native Window API.


结尾: 

     在经过很多探索后,请教了国内《深入Android系统》系列作者邓凡平,他给出的答案为:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 我知道你说的。我也做过截屏的软件,但是视频图像截不了。  
  2. 就我之前的研究来看,视频数据解码后直接由解码芯片推到屏幕上了,根本就不会  
  3. 通过surfaceflinger来混屏,在高通芯片上就是如此。  
  4. 你想想吧,windows和Linux的截屏软件都无法截取视频播放的图像,正是这个原因。  
  5.    
  6. 不过有些芯片平台也许会将视频数据通过surfaceflinger来混屏,我个人觉得这种情况比较少。  
  7. Br  
  8. 邓凡平  
总之,这条路似乎很难走通,希望有有需求的朋友少走弯路吧。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值