前段时间在做一个截屏分享的功能,所截取的内容包括一个surfaceView+MediaPlayer的视频,虽然最终实现了该功能,但是确实也走了很多弯路,当时本想总结一下的,但后来一直拖着没弄。最近又涉及到包含listView(要截取listView全部内容,包括屏幕以外的部分)截屏,弄完了就在此记录一下方便以后查看,免得过段时间忘了,又得到处找。
surfaceView截屏思路:
1.、先截取整个静态屏幕(screenBitmap)
2、获取当前的视频帧图片(vedioBitmap)
3、将获取帧图片vedioBitmap绘制在screenBitmap的黑色区域
首先截取静态屏幕,代码如下
<span style="white-space:pre"> </span>public static Bitmap takeScreenShot(Activity activity) {
// View是你需要截图的View
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap b1 = view.getDrawingCache();
// 获取状态栏高度
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
System.out.println(statusBarHeight);
// 获取屏幕长和高
int width = activity.getWindowManager().getDefaultDisplay().getWidth();
int height = activity.getWindowManager().getDefaultDisplay()
.getHeight();
// 去掉标题栏
// Bitmap b = Bitmap.createBitmap(b1, 0, 25, 320, 455);
Bitmap b = Bitmap.createBitmap(b1, 0, statusBarHeight, width, height
- statusBarHeight);
view.destroyDrawingCache();
savePic(b, "/sdcard/screen_test.png");
return b;
}
这个方法截取静态屏幕是没问题的,但视频部分截出来的趋势黑色一片,根本不能看,主要是surfaceView的实现原理,如下:
SurfaceView通常有两个缓冲区(buffer), 一个front buffer,一个back buffer。而back buffer就是Canvas对应的bitmap。每当frong buffer显示时,back buffer就迅速的绘制,然后front buffer迅速将当前的back buffer显示给用户。由于速度十分快,所以便形成了动画效果。View是在UI的主线程中更新画面,而SurfaceView是在一个新线程中更新画面。这就需要你new一个新线程,在新线程里面进行绘画的工作。 SurfaceView通过SurfaceHolder对象将back buffer画好的内容更新到front buffer。 这也就是为什么我们在使用SurfaceView时需要将canvas进行锁定。 那么说了这么多,SurfaceView和普通View有什么区别? (静态与动态的区别)
主要在于普通View内容draw之后,是静态的,我们不调用invalidate它就放在那, 拿到了Bitmap之后,我们可以获取到里面的内容。但是SurfaceView不同,我们无法拿到back buffer里面的bitmap。所以我们以往常用的截屏方案就不能够达到截下SurfaceView的效果。
获取当前的视频帧图片,代码如下:
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private Bitmap createVideoThumbnail(String url, int width, int height) {
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
int kind = MediaStore.Video.Thumbnails.MINI_KIND;
try {
if (Build.VERSION.SDK_INT >= 14) {
retriever.setDataSource(url, new HashMap<String, String>());
} else {
retriever.setDataSource(url);
}
bitmap = retriever.getFrameAtTime();
} catch (IllegalArgumentException ex) {
// Assume this is a corrupt video file
} catch (RuntimeException ex) {
// Assume this is a corrupt video file.
} finally {
try {
retriever.release();
} catch (RuntimeException ex) {
// Ignore failures while cleaning up.
}
}
if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) {
bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
}
return bitmap;
}
最后根据自己的逻辑(计算好位置、图片的宽高等等),以获取的静态屏幕图片为画布,将获取的帧图片绘制在黑色区域,即可。
查看的关于截屏的文章如下:
http://blog.csdn.net/bboyfeiyu/article/details/12712141
http://blog.csdn.net/hk_256/article/details/7306590
http://blog.csdn.net/yilip/article/details/7745584
http://blog.csdn.net/linyukun6422/article/details/43191333
http://huobengluantiao8.iteye.com/blog/1578117
http://www.tuicool.com/articles/uqmUFfY
http://blog.csdn.net/woshinia/article/details/11520403
listView截屏思路(gridView类似):
listView的截屏就比较简单了,主要是把每个item的view转换为bitmap,然后注意绘制在适当的位置就ok了。参考http://www.darcye.com/article/84263064
<span style="white-space:pre"> </span>public static Bitmap createBitmap(ListView listView, Context context) {
int width, rootHeight = 0;
int yPos = 0;
int listItemNum;
List<View> childViews = null;
width = ViewUtil.getScreenWidth(context);// 宽度等于屏幕宽
ListAdapter listAdapter = listView.getAdapter();
listItemNum = listAdapter.getCount();
childViews = new ArrayList<View>(listItemNum);
View itemView;
// 计算整体高度:
for (int pos = 0; pos < listItemNum; ++pos) {
itemView = listAdapter.getView(pos, null, listView);
// measure过程
itemView.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
childViews.add(itemView);
rootHeight += itemView.getMeasuredHeight();
}
Bitmap bitmap = Bitmap
.createBitmap(width, rootHeight, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Bitmap itemBitmap;
int childHeight;
// 把每个ItemView生成图片,并画到背景画布上
for (int pos = 0; pos < childViews.size(); ++pos) {
itemView = childViews.get(pos);
childHeight = itemView.getMeasuredHeight();
itemBitmap = viewToBitmap(itemView, width, childHeight);
if (itemBitmap != null) {
canvas.drawBitmap(itemBitmap, 0, yPos, null);
}
yPos = childHeight + yPos;
}
canvas.save(Canvas.ALL_SAVE_FLAG);
canvas.restore();
return bitmap;
}
<span style="white-space:pre"> </span>private static Bitmap viewToBitmap(View view, int viewWidth, int viewHeight) {
view.layout(0, 0, viewWidth, viewHeight);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
return bitmap;
}
</pre><pre>