您的位置:首页 > 移动开发 > Android开发

Android实现截屏和截长图功能的几种方法

2018-03-23 18:56 851 查看
一般情况下各种型号的手机都会有自带的截屏功能,也会有诸如“开关机键+音量键”的截屏快捷键,只要手机是亮屏状态,都会将手机屏幕的可视区域(包含状态栏)全部截取下来。
如果开发中想要调取系统的截屏功能,理论上讲是可以的,需要在APK中调用“adb shell screencap -pfilepath” 命令,但是需要获取root权限,调用系统的隐藏API。这就很麻烦了,感兴趣的可以自己研究一下,由于实践性很差,这里就不再赘述了。

下面说几种常用截屏和截长图的方法:
1、截屏:
即截取屏幕可视区域内的内容。
(1)截取屏幕的整个可视区域(不包含状态栏)。 /**
* 截取除了导航栏之外的整个屏幕
*/
private Bitmap screenShotWholeScreen() {
View dView = getWindow().getDecorView();
dView.setDrawingCacheEnabled(true);
dView.buildDrawingCache();
Bitmap bitmap = Bitmap.createBitmap(dView.getDrawingCache());
return bitmap;
}截图示例如下:


       


(2)截取某个View在屏幕可视区域内的部分,在可视区域外的部分无法截取到。
原理:利用View的缓存功能。Android为了提供滑动等情况下的性能,可以为每一个View创建缓存,这个缓存实际上就是一个Bitmap对象。
注意:要截取的区域一定要有背景色的设置,比如设置为白色(#ffffff),否则会出现截取后查看图片黑屏,或者分享到微信(QQ、纷享销客等)后黑屏的问题。 /**
* 使用View的缓存功能,截取指定区域的View
*/
private Bitmap screenShotView(View view) {
//开启缓存功能
view.setDrawingCacheEnabled(true);
//创建缓存
view.buildDrawingCache();
//获取缓存Bitmap
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
return bitmap;
}截图示例如下:



2、截长图。
比如:当LinearLayout、ScrollView、ListView、WebView的高度超出屏幕高度时,想要截取整个LinearLayout、ScrollView、ListView、WebView的内容。
(1)LinearLayout
原理比较简单,将LinearLayout中的子控件的高度一个个获取到,然后在Canvas上利用LinearLayout的draw()方法绘制出来即可。
注意:LinearLayout容器中不能有诸如ListView、WebView这样的高度可变的控件,否则请参照下面专门针对ListView、WebView的方法。 /**
* 截取LinearLayout
**/
public static Bitmap getLinearLayoutBitmap(LinearLayout linearLayout) {
int h = 0;
Bitmap bitmap;
for (int i = 0; i < linearLayout.getChildCount(); i++) {
h += linearLayout.getChildAt(i).getHeight();
}
// 创建对应大小的bitmap
bitmap = Bitmap.createBitmap(linearLayout.getWidth(), h,
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
linearLayout.draw(canvas);
return bitmap;
}(2)ScrollView
各位比对代码会发现,原理及实现方式与上述的LinearLayout一模一样。 /**
* 截取scrollview的屏幕
**/
public static Bitmap getScrollViewBitmap(ScrollView scrollView) {
int h = 0;
Bitmap bitmap;
for (int i = 0; i < scrollView.getChildCount(); i++) {
h += scrollView.getChildAt(i).getHeight();
}
// 创建对应大小的bitmap
bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
scrollView.draw(canvas);
return bitmap;
}(3)ListView
网上很多帖子中提到的都是下面这个方法,原理和实现代码与上述的LinearLayout和ScrollView一样,然而,实践中发现对于ListView来说,这个方法不好用。 /**
* 截图listview
**/
public static Bitmap getListViewBitmap(ListView listView) {
int h = 0;
Bitmap bitmap;
// 获取listView实际高度
for (int i = 0; i < listView.getChildCount(); i++) {
h += listView.getChildAt(i).getHeight();
}
Log.d(TAG, "实际高度:" + h);
Log.d(TAG, "list 高度:" + listView.getHeight());
// 创建对应大小的bitmap
bitmap = Bitmap.createBitmap(listView.getWidth(), h,
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
listView.draw(canvas);
return bitmap;
}如果ListView的高度没有超出屏幕可视区域,这个方法可行;
如果ListView的高度没有超出屏幕可视区域,截出来的长图只能看到可视区域内的listitem,不可见区域的listitem由于不可见高度也是0。所以截的图还是只有可视区域的部分。示例如下:



这就鸡肋了。
至于原因,ListView会回收和重用其item,并且只会绘制可视区域内的item,如果发生滑动,一旦之前可见的item变为不可见,就会被ListView回收,以备重用。这就是不可见区域的item呈现为白屏的原因。(当然,如果你的ListView的背景色是红色,那就是红屏,是绿色就是绿屏)
那可以用的ListView截取长图的方法呢?
下面这个就是,来源于stackoverflow,原理就是将listitem一个个绘制为小bitmap,然后再将所有小bitmap按顺序组合成大bitmap,即长图。 /**
* http://stackoverflow.com/questions/12742343/android-get-screenshot-of-all-listview-items */
public static Bitmap shotListView(ListView listview) {

ListAdapter adapter = listview.getAdapter();
int itemscount = adapter.getCount();
int allitemsheight = 0;
List<Bitmap> bmps = new ArrayList<Bitmap>();

for (int i = 0; i < itemscount; i++) {
View childView = adapter.getView(i, null, listview);

childView.measure(
View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
childView.setDrawingCacheEnabled(true);
childView.buildDrawingCache();

bmps.add(childView.getDrawingCache());
allitemsheight += childView.getMeasuredHeight();
}
int w = listview.getMeasuredWidth();
Bitmap bigbitmap = Bitmap.createBitmap(w, allitemsheight, Bitmap.Config.ARGB_8888);
Canvas bigcanvas = new Canvas(bigbitmap);

Paint paint = new Paint();
int iHeight = 0;

for (int i = 0; i < bmps.size(); i++) {
Bitmap bmp = bmps.get(i);
bigcanvas.drawBitmap(bmp, 0, iHeight, paint);
iHeight += bmp.getHeight();

bmp.recycle();
bmp = null;
}
return bigbitmap;
}长图示例:



(3)WebView
这里提供两种方法。
方法一: /**
* 截取WebView的屏幕,
*
* webView.capturePicture()方法在android 4.4(19)版本被废弃,
*
* 官方建议通过onDraw(Canvas)截屏
*
* @param webView
* @return
*/
private static Bitmap captureWebView(WebView webView) {
Picture snapShot = webView.capturePicture();
Bitmap bitmap = Bitmap.createBitmap(snapShot.getWidth(),
snapShot.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
snapShot.draw(canvas);
return bitmap;
}方法二: /**
* 截取WebView的屏幕,
*
* webView.getScale()方法在android 4.4.2(17)版本被废弃,
*
* 官方建议通过 onScaleChanged(WebView, float, float)回调方法获取缩放率
*
* @param webView
* @return
*/
private static Bitmap getWebViewBitmap(WebView webView) {
//获取webview缩放率
float scale = webView.getScale();
//得到缩放后webview内容的高度
int webViewHeight = (int) (webView.getContentHeight() * scale);
Bitmap bitmap = Bitmap.createBitmap(webView.getWidth(), webViewHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
//绘制
webView.draw(canvas);
return bitmap;
}两种方法都可用,但也都有同样的问题:
在android 5.0以下版本的系统中,截WebView长图都可用;
在android 5.0及5.0以上版本的系统中,截出来的长图都是不完整的,只有屏幕可视区域内的内容,可视区域外的是白屏,示例如下:



原因在于在5.0及以上版本,Android为了降低WebView的内存消耗,对WebView进行了优化:可视区域内的HTML页面进行渲染,不可视区域的HTML显示为白屏,待需要显示时再进行渲染。
解决方法有两个:
方法一:
在设置布局文件的setContentView()方法前调用WebView.enableSlowWholeDocumentDraw(),作用在于禁止Android对WebView进行优化。
不过,这个方法要求项目支持的系统最低版本为5.0,即设置minSdkVersion >= 21。
但是,设置minSdkVersion >= 21 会有很严重的问题,将导致系统版本为5.0以下的手机无法安装自己的APP,要权衡。
方法二:
设置targetSdkVersion < 21,即设置目标SDK的版本小于5.0。
但是,也会有问题,Android在5.0及5.0以后提供的新功能和新特性在APP中将不可使用,如5.0的Meterial Design、6.0的权限管理、7.0的多窗口支持等,当然肯定也包括Android在5.0对WebView所做的性能优化。
关于minSdkVersion 、targetSdkVersion、compileSdkVersion的区别,以及Andoird各版本新增的功能及特性,稍后也总结一下。

这样,WebView的截长图也大功告成了:



网页中图片加载失败,是测试服务器的图片资源发生了移动,与Android 代码无关。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息