最近在做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(), , 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 = ; j < width * height * deepth; j += deepth) {
b = arrayOfByte[j] & 0xff;
g = arrayOfByte[j + ] & 0xff;
r = arrayOfByte[j + ] & 0xff;
tmpColor[j / deepth] = (r << ) | (g << ) | 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) * ; long videoPosition = ;
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 > ) {
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的图像,而不是截图。

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

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

结尾: 

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

我知道你说的。我也做过截屏的软件,但是视频图像截不了。
就我之前的研究来看,视频数据解码后直接由解码芯片推到屏幕上了,根本就不会
通过surfaceflinger来混屏,在高通芯片上就是如此。
你想想吧,windows和Linux的截屏软件都无法截取视频播放的图像,正是这个原因。 不过有些芯片平台也许会将视频数据通过surfaceflinger来混屏,我个人觉得这种情况比较少。
Br
邓凡平

总之,这条路似乎很难走通,希望有有需求的朋友少走弯路吧。

Android应用截图和SurfaceView截图问题总结的更多相关文章

  1. Android之View和SurfaceView

    Android之View和SurfaceView Android游戏当中主要的除了控制类外就是显示类View.SurfaceView是从View基类中派生出来的显示类.android游戏开发中常用的三 ...

  2. Android 学习笔记之SurfaceView的使用+如何实现视频播放...

    学习内容: 1.掌握Surface的使用... 2.Android中如何实现视频播放... 1.SurfaceView类的使用   在Android中,一般播放音频时我们可以去使用Android提供的 ...

  3. C#区域截图——调用API截图

    原文:C#区域截图——调用API截图 前言:截图对于一个C++开发者来说无非是小菜一碟,也有朋友使用C#的 Graphics.CopyFromScreen 方法屏幕操作,作为一名整天想着用 C++ 开 ...

  4. 【转】Android Fragment中使用SurfaceView切换时闪一下黑屏的解决办法

    重构了下之前自己的一个新闻客户端,全部使用了Fragment来进行页面切换,只有一个入口Activity作为程序的启动Activity,其中有一个界面需要调用摄像头识别二维码, 于是就会用到Surfa ...

  5. Android之WebView网页滚动截图

    WebView 网页滚动截屏,可对整个网页进行截屏而不是仅当前屏幕哦! 注意若Web页面存在position:fixed; 的话得在调用前设置为 position:absolute; 哦,否则会出现很 ...

  6. android view 转Bitmap 生成截图

    文章链接:https://mp.weixin.qq.com/s/FQmYfT-KYiDbp-0HzK_Hpw 项目中经常会用到分享的功能,有分享链接也有分享图片,其中分享图片有的需要移动端对屏幕内容进 ...

  7. 玩转Android Camera开发(一):Surfaceview预览Camera,基础拍照功能完整demo

    杂家前文是在2012年的除夕之夜仓促完成,后来很多人指出了一些问题,琐事缠身一直没有进行升级.后来随着我自己的使用,越来越发现不出个升级版的demo是不行了.有时候就连我自己用这个demo测一些性能. ...

  8. android 使用两个surfaceview 在摄像机画面上绘图

    转载自http://blog.csdn.net/jesse__zhong/article/details/24934083 使用双surface,将第一个设置为透明背景,在摄像机上绘制图像,纠结搞了一 ...

  9. Android Button悬浮在SurfaceView上

    实现Button悬浮于与SurfaceView之上实现 注意:你实现的SurfaceView和android中的Button,EditView是同级的,不能把一个包含在另一个里面 1.创建自己的Sur ...

随机推荐

  1. java使用ms-dos编译,运行程序

    1.安装好JDK,并配置好环境变量. 2.编辑好源程序,如Test.java public class Test{ public static void main(String[] args){ Sy ...

  2. ThinkPHP运算符与PHP运算符对照表

    ThinkPHP运算符与PHP运算符对照表 ThinkPHP标签 说明及对应PHP标签 备注 eq 等于(=)(==:用于模板判断时) 可用于查询条件与模板判断 neq 不等于(!=) 可用于查询条件 ...

  3. 五、案例-指令参考-freemarker指令、表达式

    案例-指令参考描述:本人自己测试写了一遍,如有错的地方,懂freemarker的朋友望指点指点! 案例-指令参考 表达式 一. Assign 1.<#assign name1="北京& ...

  4. npm ERR!无法安装任何包的解决办法

    npm ERR! Windows_NT 6.1.7601npm ERR! argv "E:\\node\\\\node.exe" "E:\\node\\node_modu ...

  5. 浅谈IT认识

    我理解的IT       新华电脑学院,引领IT潮流.这句耳熟能详的广告语,从小就在我的记忆里深存,当看到别人QQ用黑客技术刷了好多钻,于是乎无比的向往这种黑客技术,后来网上购物更是在互联网上一疯而起 ...

  6. HDU 1598 find the most comfortable road 并查集+贪心

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1598 find the most comfortable road Time Limit: 1000 ...

  7. Codeforces Round #350 (Div. 2) E. Correct Bracket Sequence Editor 模拟

    题目链接: http://codeforces.com/contest/670/problem/E 题解: 用STL的list和stack模拟的,没想到跑的还挺快. 代码: #include<i ...

  8. bzoj 1196 二分+生成树判定

    我们先二分一个答案,对于每个答案,先加一级公路,如果不够k直接break, 然后再加二级公路,加的过程类似Kruskal. /************************************* ...

  9. [bzoj 3226]校门外的区间

    题意 输出最后的集合   题解 校门外的树会做吧 区间知道是什么东西吧 校门外的区间会做了吧 昨天做个大线段树没做出来,今天做个小线段树压压惊 py一下输入数据,然后操作变成: U 区间涂1 I 两侧 ...

  10. FormCreate & FormActivate & FormShow执行顺序演示

    procedure TForm1.FormCreate(Sender: TObject);begin  form1.Caption:=form1.Caption +'+Create'; end; pr ...