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

本文介绍在Android开发中实现截屏和截长图的多种方法,包括截取屏幕可视区域、特定View区域,以及针对LinearLayout、ScrollView、ListView和WebView的长图截取技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原创

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.youkuaiyun.com/hailong0529/article/details/88677358
            </div>
                                                <!--一个博主专栏付费入口-->
         
         <!--一个博主专栏付费入口结束-->
        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
                                    <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
            <div class="htmledit_views" id="content_views">
                                        <p>&nbsp;</p>

一般情况下各种型号的手机都会有自带的截屏功能,也会有诸如“开关机键+音量键”的截屏快捷键,只要手机是亮屏状态,都会将手机屏幕的可视区域(包含状态栏)全部截取下来。

如果开发中想要调取系统的截屏功能,理论上讲是可以的,需要在APK中调用“adb shell screencap -pfilepath” 命令,但是需要获取root权限,调用系统的隐藏API。这就很麻烦了,感兴趣的可以自己研究一下,由于实践性很差,这里就不再赘述了。


下面说几种常用截屏和截长图的方法:

一、截屏:

即截取屏幕可视区域内的内容。

1、截取屏幕的整个可视区域(不包含状态栏)


 
  1. /**
  2. * 截取除了导航栏之外的整个屏幕
  3. */
  4. private Bitmap screenShotWholeScreen() {
  5. View dView = getWindow().getDecorView();
  6. dView.setDrawingCacheEnabled( true);
  7. dView.buildDrawingCache();
  8. Bitmap bitmap = Bitmap.createBitmap(dView.getDrawingCache());
  9. return bitmap;
  10. }

效果图:

2、截取某个View在屏幕可视区域内的部分,在可视区域外的部分无法截取到。

原理:利用View的缓存功能。Android为了提供滑动等情况下的性能,可以为每一个View创建缓存,这个缓存实际上就是一个Bitmap对象。

注意:要截取的区域一定要有背景色的设置,比如设置为白色(#ffffff),否则会出现截取后查看图片黑屏,或者分享到微信(QQ、纷享销客等)后黑屏的问题。


 
  1. /**
  2. * 使用View的缓存功能,截取指定区域的View
  3. */
  4. private Bitmap screenShotView(View view) {
  5. //开启缓存功能
  6. view.setDrawingCacheEnabled( true);
  7. //创建缓存
  8. view.buildDrawingCache();
  9. //获取缓存Bitmap
  10. Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
  11. return bitmap;
  12. }

效果图:

二、截长图。

比如:当LinearLayout、ScrollView、ListView、WebView的高度超出屏幕高度时,想要截取整个LinearLayout、ScrollView、ListView、WebView的内容。

1、LinearLayout

原理比较简单,将LinearLayout中的子控件的高度一个个获取到,然后在Canvas上利用LinearLayout的draw()方法绘制出来即可。

注意:LinearLayout容器中不能有诸如ListView、WebView这样的高度可变的控件,否则请参照下面专门针对ListView、WebView的方法。


 
  1. /**
  2. * 截取LinearLayout
  3. **/
  4. public static Bitmap getLinearLayoutBitmap(LinearLayout linearLayout) {
  5. int h = 0;
  6. Bitmap bitmap;
  7. for ( int i = 0; i < linearLayout.getChildCount(); i++) {
  8. h += linearLayout.getChildAt(i).getHeight();
  9. }
  10. // 创建对应大小的bitmap
  11. bitmap = Bitmap.createBitmap(linearLayout.getWidth(), h,
  12. Bitmap.Config.ARGB_8888);
  13. final Canvas canvas = new Canvas(bitmap);
  14. linearLayout.draw(canvas);
  15. return bitmap;
  16. }

2、ScrollView

各位比对代码会发现,原理及实现方式与上述的LinearLayout一模一样


 
  1. /**
  2. * 截取scrollview的屏幕
  3. **/
  4. public static Bitmap getScrollViewBitmap(ScrollView scrollView) {
  5. int h = 0;
  6. Bitmap bitmap;
  7. for ( int i = 0; i < scrollView.getChildCount(); i++) {
  8. h += scrollView.getChildAt(i).getHeight();
  9. }
  10. // 创建对应大小的bitmap
  11. bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
  12. Bitmap.Config.ARGB_8888);
  13. final Canvas canvas = new Canvas(bitmap);
  14. scrollView.draw(canvas);
  15. return bitmap;
  16. }

3、ListView

网上很多帖子中提到的都是下面这个方法,原理和实现代码与上述的LinearLayout和ScrollView一样,然而,实践中发现对于ListView来说,这个方法不好用(好用的方法在后面)。


 
  1. /**
  2. * 截图listview
  3. **/
  4. public static Bitmap getListViewBitmap(ListView listView) {
  5. int h = 0;
  6. Bitmap bitmap;
  7. // 获取listView实际高度
  8. for ( int i = 0; i < listView.getChildCount(); i++) {
  9. h += listView.getChildAt(i).getHeight();
  10. }
  11. Log.d(TAG, "实际高度:" + h);
  12. Log.d(TAG, "list 高度:" + listView.getHeight());
  13. // 创建对应大小的bitmap
  14. bitmap = Bitmap.createBitmap(listView.getWidth(), h,
  15. Bitmap.Config.ARGB_8888);
  16. final Canvas canvas = new Canvas(bitmap);
  17. listView.draw(canvas);
  18. return bitmap;
  19. }

如果ListView的高度没有超出屏幕可视区域,这个方法可行;

如果ListView的高度没有超出屏幕可视区域,截出来的长图只能看到可视区域内的listitem,不可见区域的listitem由于不可见高度也是0。所以截的图还是只有可视区域的部分。如下图,条目15就被截断了:

原因在于,ListView会回收和重用其item,并且只会绘制可视区域内的item,如果发生滑动,一旦之前可见的item变为不可见,就会被ListView回收,以备重用。这就是不可见区域的item呈现为白屏的原因。(当然,如果你的ListView的背景色是红色,那就是红屏)

那好用的ListView截取长图的方法呢?

代码如下,来源于stackoverflow,原理就是将listitem一个个绘制为小bitmap,然后再将所有小bitmap按顺序组合成大bitmap,即长图。


 
  1. /**
  2. * http://stackoverflow.com/questions/12742343/android-get-screenshot-of-all-listview-items
  3. */
  4. public static Bitmap shotListView(ListView listview) {
  5. ListAdapter adapter = listview.getAdapter();
  6. int itemscount = adapter.getCount();
  7. int allitemsheight = 0;
  8. List<Bitmap> bmps = new ArrayList<Bitmap>();
  9. for ( int i = 0; i < itemscount; i++) {
  10. View childView = adapter.getView(i, null, listview);
  11. childView.measure(
  12. View.MeasureSpec.makeMeasureSpec(listview.getWidth(), View.MeasureSpec.EXACTLY),
  13. View.MeasureSpec.makeMeasureSpec( 0, View.MeasureSpec.UNSPECIFIED));
  14. childView.layout( 0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
  15. childView.setDrawingCacheEnabled( true);
  16. childView.buildDrawingCache();
  17. bmps.add(childView.getDrawingCache());
  18. allitemsheight += childView.getMeasuredHeight();
  19. }
  20. int w = listview.getMeasuredWidth();
  21. Bitmap bigbitmap = Bitmap.createBitmap(w, allitemsheight, Bitmap.Config.ARGB_8888);
  22. Canvas bigcanvas = new Canvas(bigbitmap);
  23. Paint paint = new Paint();
  24. int iHeight = 0;
  25. for ( int i = 0; i < bmps.size(); i++) {
  26. Bitmap bmp = bmps.get(i);
  27. bigcanvas.drawBitmap(bmp, 0, iHeight, paint);
  28. iHeight += bmp.getHeight();
  29. bmp.recycle();
  30. bmp = null;
  31. }
  32. return bigbitmap;
  33. }

看下面的效果图,item没有出现被截断的情况:

4、WebView

这里提供两种方法(两种方法都不完美,怎样变完美请往后面看)。

方法一:


 
  1. /**
  2. * 截取WebView的屏幕,
  3. *
  4. * webView.capturePicture()方法在android 4.4(19)版本被废弃,
  5. *
  6. * 官方建议通过onDraw(Canvas)截屏
  7. *
  8. * @param webView
  9. * @return
  10. */
  11. private static Bitmap captureWebView(WebView webView) {
  12. Picture snapShot = webView.capturePicture();
  13. Bitmap bitmap = Bitmap.createBitmap(snapShot.getWidth(),
  14. snapShot.getHeight(), Bitmap.Config.ARGB_8888);
  15. Canvas canvas = new Canvas(bitmap);
  16. snapShot.draw(canvas);
  17. return bitmap;
  18. }

方法二:


 
  1. /**
  2. * 截取WebView的屏幕,
  3. *
  4. * webView.getScale()方法在android 4.4.2(17)版本被废弃,
  5. *
  6. * 官方建议通过 onScaleChanged(WebView, float, float)回调方法获取缩放率
  7. *
  8. * @param webView
  9. * @return
  10. */
  11. private static Bitmap getWebViewBitmap(WebView webView) {
  12. //获取webview缩放率
  13. float scale = webView.getScale();
  14. //得到缩放后webview内容的高度
  15. int webViewHeight = ( int) (webView.getContentHeight() * scale);
  16. Bitmap bitmap = Bitmap.createBitmap(webView.getWidth(), webViewHeight, Bitmap.Config.ARGB_8888);
  17. Canvas canvas = new Canvas(bitmap);
  18. //绘制
  19. webView.draw(canvas);
  20. return bitmap;
  21. }

然而,两种方法都不能兼容Android 5.0及5.0以上系统:

A、在android 5.0以下版本的系统中,截WebView长图都可用;

B、在android 5.0及5.0以上版本的系统中,截出来的长图都是不完整的,只有屏幕可视区域内的内容,可视区域外的是白屏。效像下面图示这样:

原因在于,在5.0及以上版本,Android为了降低WebView的内存消耗,对WebView进行了优化:可视区域内的HTML页面进行渲染,不可视区域的HTML显示为白屏,待需要显示时再进行渲染。

解决办法是在设置布局文件的setContentView()方法前调用WebView.enableSlowWholeDocumentDraw(),其作用是禁止Android对WebView进行优化。


 
  1. //系统版本不小于5.0
  2. if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
  3. WebView.enableSlowWholeDocumentDraw();
  4. }

如果项目的minSdkVersion<21,上述方法会报异常 @TargetApi(apiVersion) : Call requires API level 21 (current min is 16)

很好解决,请看我的另一篇博客:Android @TargetApi(apiVersion) : Call requires API level 21 (current min is 16)

至此,WebView的截长图功能也实现了。至于其他未测试到的容器,方法大体相同,请各位自行测试吧!

附一张WebView截长图成功的示例图:

至于网页中图片加载失败,是测试服务器的图片资源发生了移动,与Android代码无关。

 

文章最后发布于: 2019-03-20 00:29:16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值