最近在做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. maven安装报错

    今天在安装maven时安照说明配置环境变量,通过命令检查: mvn -v 竟然出现以下错误,很郁闷的是我明明配置了JAVA_HOME,并且别的依赖java的东西都能用,通过java -version也 ...

  2. VIMTUTOR《VIM教程》

    =============================================================================== =      欢     迎     阅 ...

  3. 增强学习————K-摇臂赌博机

    探索与利用增强学习任务的最终奖赏是在多步动作之后才能观察到,于是我们先考虑最简单的情形:最大化单步奖赏,即仅考虑一步操作.不过,就算这样,强化学习仍与监督学习有显著不同,因为机器要通过尝试来发现各个动 ...

  4. 关于myeclipse代码提示的一些问题

    默认是  .xxx  输入点提示,要写注释 @xxx的时候怎么输入@后面有代码提示呢? Auto activation delay 是代码提示出现的速度  下面一行是出现代码提示的条件 我们在.后面加 ...

  5. vasprintf的实现

    有些系统,例如AIX,没有vasprintf和asprintf ,如果是做porting,有时需要写一个简单的代替. 代码如下: #if !defined(HAVE_VASPRINTF) #if de ...

  6. Posix线程编程指南(1) 线程创建与取消

    线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列.在串行程序基础上引入线程和进程是为了提高程序的 ...

  7. 手写PE文件(一)

    DOS Header(IMAGE_DOS_HEADER)->64 Byte DOS头部 DOS Stub 112字节 "PE"00(Signature) 4个字节 IMAGE ...

  8. WinHex分析PE格式(1)

    最近在一直努力学习破解,但是发现我的基础太差了,就想学习一下PE结构.可是PE结构里的结构关系太复杂,看这老罗的WiN32汇编最后一章 翻两页又合上了..把自己的信心都搞没了.感觉自己的理解能力不行, ...

  9. Ruby Profiler 详解之 ruby-prof(I)

    项目地址: ruby-prof 在上一篇 Ruby 中的 Profiling 工具中,我们列举了几种最常用的 Profiler,不过只是简单介绍,这一次详细介绍一下 ruby-prof 的使用方法. ...

  10. POJ 1978

    #include <iostream> #define MAXN 55 using namespace std; int _m[MAXN]; int tem[MAXN]; void cop ...