android WebView截长图实现

1、先简单介绍下webview截屏,看代码:

//开启缓存
webview.setDrawingCacheEnabled(true);
webview.buildDrawingCache();

Bitmap bm = webview.getDrawingCache();

...
保存图片代码部分已省略
...

//关闭缓存
webview.setDrawingCacheEnabled(false);

思路就是强制webview构建当前可视区域的缓存,然后读取缓存。

 

2、上边说的是截屏,下边来看下如何截长图,截长图的思路跟截屏类似,通过滚动webview滚动条来拼接视图缓存,具体的实现需要借助Canvas进行绘制。在绘制前,我们需要先获取当前html文档的真实宽度、高度,高度webview已经提供了方法:getContentHeight(),宽度比较复杂,我们需要借助js来获取。这里,我使用DSBridge框架实现JS交互,其他框架类似,我们的目的是获取当前文档的真实宽度,我们先注册一个JS回调接口供Native调用:

//注册一个横向滚动条宽度接口供Native调用
dsBridge.register('getScrollInfo',function(){
	 return {scrollWidth:document.body.scrollWidth};
});

这样,我们就能获得真实内容宽度了,下边的代码是Java 调用JS回调接口的逻辑:

            //先获取内容宽度
            webview.callHandler("getScrollInfo", null, new OnReturnValue<JSONObject>() {
                @Override
                public void onValue(final JSONObject retData) {


                    float scale = webview.getScale();

                    HashMap<String,Object> map = JsonUtils.parseJsonToMap(retData.toString());

                    float scrollWidth = Float.valueOf(map.get("scrollWidth").toString());

                    //真实内容宽度
                    final int contentWidth = (int)(scrollWidth * scale);
                    //真实内容高度
                    final int contentHeight = (int) (webview.getContentHeight() * scale);

                    final int oldScrollLeft = webview.getScrollX();
                    final int oldScrollTop = webview.getScrollY();

                    final int webviewWidth = webview.getWidth();
                    final int webviewHeight = webview.getHeight();

                    new Thread(new Runnable() {
                        @Override
                        public void run() {

                            int contentWidths = contentWidth;
                            int contentHeights = contentHeight;

                            //创建一张与WebView内容大小相同的位图
                            Bitmap bm = Bitmap.createBitmap(contentWidth, contentHeight, Bitmap.Config.ARGB_8888);

                            Canvas canvas = new Canvas(bm);

                            int heightTmp = contentHeights;
                            int widthTmp = contentWidths;

                            while (widthTmp > 0 ){

                                if (contentWidth < webviewWidth) {
                                    contentWidths = 0;
                                } else {

                                    if(contentWidths > webviewWidth)
                                    {
                                        contentWidths -= webviewWidth;
                                    }

                                    widthTmp -= webviewWidth;
                                }

                                heightTmp = contentHeights;

                                while (heightTmp > 0) {

                                    if (contentHeight < webviewHeight) {
                                        contentHeights = 0;
                                    } else {

                                        if (contentHeights > webviewHeight)
                                        {
                                            contentHeights -= webviewHeight;
                                        }

                                        heightTmp -= webviewHeight;
                                    }

                                    //保存状态
                                    canvas.save();

                                    //小于0说明快结束了,调整剪辑区域为剩余区域,避免被覆盖
                                    if (widthTmp <= 0)
                                    {
                                        if (heightTmp <= 0)
                                        {
                                            //设置剪辑区域
                                            canvas.clipRect(0, 0, contentWidths, contentHeights);
                                            webview.scrollTo(0, 0);
                                        }else
                                        {
                                            //设置剪辑区域
                                            canvas.clipRect(0, contentHeights, contentWidths, contentHeights + webviewHeight);
                                            webview.scrollTo(0, contentHeights);
                                        }
                                    }else
                                    {
                                        if (heightTmp <= 0)
                                        {
                                            //设置剪辑区域
                                            canvas.clipRect(contentWidths, 0, contentWidths+webviewWidth, contentHeights);
                                            webview.scrollTo(contentWidths, 0);
                                        }else
                                        {
                                            //设置剪辑区域
                                            canvas.clipRect(contentWidths, contentHeights, contentWidths+webviewWidth, contentHeights + webviewHeight);
                                            webview.scrollTo(contentWidths, contentHeights);
                                        }
                                    }

                                    webview.draw(canvas);

                                    canvas.restore();
                                }

                                contentHeights = contentHeight;

                            }

                            webview.scrollTo(oldScrollLeft,oldScrollTop);

                            ...
                            已忽略保存图片部分代码
                            ...

                        }
                    }).start();


                }
            });

上边的代码,我们使用了2个while循环,外边的负责滚动横向滚动条,里边的负责滚动纵向滚动条。当然了,比较耗时的绘图操作我们放在了一个线程里头完成,最终将会形成一张真实缩放比例的大图,缩放比例越大,图片越大。

 

关于OOM问题:

图片过大一般会触发OOM,即内存不够用了,因为我们这个是合成图片,还是大图,所以内存消耗不可避免。简单快捷的办法是在AndroidManifest.xml文件中application节点添加属性android:largeHeap="true",这样能拿到App能用的最大内存,一般能拿到物理内存的1/8大小,比如4G内存的手机,我们一般能拿到500MB左右,具体大小也跟手机系统有关系。

为了防止App崩溃我们还需要增加内存使用量的检测代码,如果检测到内存不足,就停止相关操作即可,可参考下边的代码(对应上边的代码):

//先计算图片占用多少内存,避免OOM导致App崩溃,ARGB_8888格式每像素占用4字节
BigInteger big_W = BigInteger.valueOf(contentWidth);
BigInteger big_H = BigInteger.valueOf(contentHeight);
BigInteger big_P = BigInteger.valueOf(4);
big_W = big_W.multiply(big_H);
big_W = big_W.multiply(big_P);

//得到图片需要的内存
long needMemory = big_W.longValue();

//获取可用内存
Runtime runtime = Runtime.getRuntime();
//App可用的最大内存
long maxMemory = runtime.maxMemory();
//App已用的内存
long totalMemory = runtime.totalMemory();

if(needMemory > maxMemory - totalMemory)
{
    callback.onCallBack(false,"亲,内存不够了哟~缩小点吧!");
    return;
}

 

这段代码应该在绘图之前检测,这样App就不会触发OOM导致崩溃了。

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值