安卓WebView的一点总结

安卓WebView的一点总结

最近的项目有一个页面类似于网易新闻的详情页,刚开始想用原生的activity来开发,后来发现里面的内容样式是要变化的,后台的MM不定时的更新里面的内容,那么如果用activity去网络请求数据的方法,在页面的样式编辑上就不好做,可能这一个页面需要有图片排版,下一个页面就没有图片了,或者多出个链接..这些需求很常见,所以就想着用html的形式来加载.
切入正题,webview的简单加载url就不多说了,这里给一下常见的初始化操作

 //直接显示.
        WebSettings settings = webView.getSettings();
        settings.setDefaultTextEncodingName("utf-8");
        settings.setSupportZoom(true);//设置缩放
        settings.setBuiltInZoomControls(true);//缩放控制
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setJavaScriptEnabled(true);
        if (NetWorkUtils.isNetWorkEnable(HaotouguApp.getInstance())) {
            settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        } else {
            settings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
        }
        settings.setDomStorageEnabled(true);
        settings.setUseWideViewPort(true); //将图片调整到适合webview的大小
        settings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
        settings.setDatabaseEnabled(true);   //开启 database storage API 功能
        settings.setAppCacheEnabled(true);//开启 Application Caches 功能
        webView.setWebViewClient(new MyWebViewClient());
        webView.setWebChromeClient(new MyWebChromeClient());
        webView.clearCache(true);
        webView.addJavascriptInterface(new WebAppInterfaceForHtml(), "WebAppInterfaceForHtml");
        mPresenter.setHtml(id, time);
        //监听返回键
        webView.setOnKeyListener((v, keyCode, event) -> {
            if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack() && KeyEvent.ACTION_UP == event.getAction()) {
                webView.goBack();
                return true;
            }
            return false;
        });

这里面的有些不常用的也都有注释,一看便知,这是初始化webview的操作,那么webview的两个比较重要的管理类,不得不提一下
WebViewClient和WebChromeClient这两个方法的区别不懂可以谷歌,那么WebViewClient中复写的方法,有以下这几个常见的
1.主要是拦截webview请求的html中的资源,如css,js,图片等

@Override
        public WebResourceResponse shouldInterceptRequest(WebView webView, String s) {
            return super.shouldInterceptRequest(webView, s);
        }

2.这个有注释,主要是让html的跳转在当前app中,有的机型,如果不复写这个方法,可能就会在webview中再请求一个html会跳转到手机的浏览器中

 //指定链接在当前app中打开,否则在浏览器中打开
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            return true;
        }

3.html开始加载的回调,我在这里面做了个简单的webview超时操作,用的rxjava的timer操作符,当加载开始之后,如果进度不到100%且没有走缓存,那么我就判断其超时,关闭本次加载,然后显示重新加载的按钮

 @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            KLog.e(TAG,"开始加载!!!");
            av_load.show();
            webView.setEnabled(true);
            ll_reload.setVisibility(View.GONE);
            mSubscribe = Observable.timer(10 * 1000, TimeUnit.MILLISECONDS)
                    .observeOn(AndroidSchedulers.mainThread())
                    .unsubscribeOn(AndroidSchedulers.mainThread())
                    .subscribe(call -> {
                        KLog.e(TAG,"达到超时时间限制,加载进度:"+webView.getProgress());
                        if (webView.getProgress() < 100 && !mPresenter.hasCache()) {
                           showNetError();
                        }
                    });
        }

4.页面加载完成,可以展示了,不多说

 @Override
        public void onPageFinished(WebView view, String url) {

        }

5.这是两个回调,请求出错的时候的回调,一个http的,一个https的

 @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            if (!mPresenter.hasCache()) {
                showNetError();
            }
        }


        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            super.onReceivedError(view, request, error);
            if (!mPresenter.hasCache()) {
                showNetError();
            }
        }

WebChromeClient有以下的方法,其实这个实现类比较常用,里面的一些实现方法可以自行谷歌看看,还是比较全面的
1.我只用了这一个方法,就是加载进度,可以在这里获取到当前webview页面的加载进度

 @Override
        public void onProgressChanged(WebView view, int newProgress) {
            super.onProgressChanged(view, newProgress);
            if (newProgress == 100) {
                if (null != mPresenter) {
                    mPresenter.getImage();
                    mTextJs = mPresenter.getTextJs();
                    mClickJs = mPresenter.getClickJs();
                    mShareClickJs = mPresenter.getShareClickJs();
                    mItemClickJs = mPresenter.getItemClickJs();
                }
                if (null != mTextJs && null != mClickJs) {
                    //注入js
                    webView.loadUrl("javascript:" + mTextJs);
                    webView.loadUrl("javascript:" + mClickJs);
                    //回调js的onClick
                    String injectImgClick = "javascript:injectImgClick()";
                    webView.loadUrl(injectImgClick);
                }
                //注入分享按钮的js
                if (null != mShareClickJs) {
                    webView.loadUrl("javascript:" + mShareClickJs);
                }
                //注入item点击的js
                if (null != mItemClickJs) {
                    webView.loadUrl("javascript:" + mItemClickJs);
                }
                //缓存当前页数据
                mPresenter.saveTimeCache(time, id);
            }
        }

以上就是初始化的一些代码和必要的配置,下面说一下我做这个页面的思路:
最核心的思路就是将这个html页面的相关资源保存到本地(html,css,js,固定的图片如:分享按钮图片等等),在启动这个webview页面的时候,请求接口只拿里面的文章和图片的链接,而且文章通过接口请求一次之后,缓存到本地,图片链接下载下来之后也缓存到本地,那么这样第二次加载速度就特别快,几乎接近原生的页面
这个思路也是现在的一些新闻类APP的详情页面的用的比较多的,可能有大神觉得我这样的方法比较low,有更好的,那就当我没说 ,哈哈,至少我目前想到的就这些,希望能有大神支招,告诉更好的方法
webview如何加载本地资源(html,css,js,图片等),搜一下,发现webview加载的资源,只要符合file:///android_asset/xxx就可以,也就是说webview.load(“file:///android_asset/xxx”)就能加载一个本地存储的html页面,那么有了这个,就好说了;
我将这个详情页面的html,css,js,分享的icon全部放在main/assets下面了,在当前webview初始化完之后,就去请求接口,我的presenter实现类里面做了如下判断:
拿着该条详情页的时间戳和id(主要做缓存,后面说)去数据库中查询,如果有,说明之前请求过接口,数据已经缓存到本地了(我指的是文字内容和图片链接,有了链接也就相当于有了图片),我将时间戳的字符串存在了数据库,文本和链接存到了sp里面,因为数据库里面不想存储大量的文本,不知道会不会影响速度,个人理解可能会;有缓存就好办,直接去加载就可以了,这里又用了另一个加载方法 webView.loadDataWithBaseURL("file:///android_asset/index.html", html, "text/html", "UTF-8", null);
需要了解各参数的意思的,自行google
那么如果没找到时间戳,说明就没有缓存,这时候就去请求网络接口,请求网络接口就有点小复杂,第一,请求回来的只是文字内容和图片链接,我们需要自己将文字内容和图片链接拼接到本地放着的html中去,拼成完整的html文本形式,放到webview中加载,其实单独用webview加载一个url也是这样,webview会拿着这个html从网络请求完整的html页面,把html解析出来,显示到屏幕上.那么怎么拼呢,说着简单,做起来不简单,首先要读取本地html,
这有个工具,直接拿着用!

public static String getStringToAsset(InputStream inputStream) {
        InputStreamReader inputStreamReader = null;
        try {
            inputStreamReader = new InputStreamReader(inputStream, "utf-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        BufferedReader reader = new BufferedReader(inputStreamReader);
        StringBuilder sb = new StringBuilder("");
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line);
                sb.append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
String html = FileUtils.getStringToAsset(mView.context().getAssets().open("index.html"));

这样就将assets中放得index.html以字符串的形式读取出来了,这个html就是我们全部index.html页面的内容,接下来就是拼接图片和显示占位图的时候了,比较恶心
这里我用了js回调的方法,就是当接口返回来文章和图片链接的时候,先不加载图片,因为加载图片很耗时,需要拿着这个图片url去下载,先使用一张占位图去占位,就像网易新闻详情页中那样,当图片下载完成之后,使用js去刷新一下页面的占位图,替换成我下载好的图片就OK了..完整的借用了网易新闻,哈哈
还要说一下,由于图片都是后台编辑的,给多大尺寸都是不一定了,所以这里就需要适配一下,传图片的时候默认计算出图片的宽和高一起通过接口传过来,在拿图片url的时候,一起拿到了这个宽和高,将占位图直接设置好

int w = (int) DPUtils.px2dp(mView.context(), AppUtils.getScreenWidth(mView.context()));
 //如果图片宽大于屏幕宽,则等比例缩放图片
                    if (w < StringUtils.String2int(imgBean.getWidth())) {
                        imageWidth = w - 24;
                        imageHight = imageWidth * StringUtils.String2int(imgBean.getHeight()) / StringUtils.String2int(imgBean.getWidth());
                    } else {
                        imageWidth = StringUtils.String2int(imgBean.getWidth());
                        imageHight = StringUtils.String2int(imgBean.getHeight());
                    }

好了,设置好图片的大小,就要拼图片链接了,注意千万别拼错了,拼错了就不会显示了

String newImageChar = "<img width=" + "\"" + imageWidth + "px\"" + " height=" + "\"" + imageHight + "px\"" + " src= " + "file:///android_asset/gray_holder.png" + " asrc= " + "\"" + imgBean.getSrc() + "\"" + " />";

这里之所以有src和asrc是因为需要有对应关系,js需要这个对应关系,去刷新下载之后的图片;图片下载好之后是在缓存中,根据url有对应的缓存图片,相当于key,value的关系,拿着这两个url(原图url和缓存地址url)调用js的方法,就可以刷新这个图片,将占位图替换成新下载的图片,这是比较好的一种方法,解决了webview加载html不能缓存图片的问题!!!
拼接文字啥的就好说多了,将返回来的文本以heml中body的标签一样,拼接进去就好了

好了.基本拼接就这样,还有一个比较重要的,就是图片点击和链接跳转,这里就涉及到js和android交互了


对于安卓中响应js的方法回调很简单,只需要做如下操作:

webView.addJavascriptInterface(new WebAppInterfaceForHtml(), "WebAppInterfaceForHtml");

这个WebAppInterfaceForHtml的标志是跟js的工程师商量好的,js中需要这样写

function onOtherNewsClick(url) {
    console.log(url);
    window.WebAppInterfaceForHtml.onOtherNewsClick(url);
}

这是固定的写法,js中只有这样写,安卓中才能响应js中的function,然后安卓代码这样写


 public class WebAppInterfaceForHtml {
        //js加载图片替换的回调
        @JavascriptInterface
        public void repleaseImage(String pOldUrl, String pNewUrl) {
        }

        //js图片点击回调
        @JavascriptInterface
        public void getImageInfo(int x, int y, int width, int height, String src) {
            if (null != src) {
                webView.post(() -> {
                    ZoomImageActivity_.intent(mContext).extra("src", src).start();
                });
            }
        }

        //js图片点击回调
        @JavascriptInterface
        public void doShareClick(int type) {
            webView.post(() -> {
                mPresenter.doShare(type);
            });
        }

        //item点击回调
        @JavascriptInterface
        public void onOtherNewsClick(String url) {
            KLog.e(TAG, "item点击:url" + url);
            webView.post(() -> {
                UrlUtils.resolvingUrl(url, mContext);
            });
        }
    }

注意这个里面有个webView.post(),不这样写不会执行,具体google

另外一个重点就是,我们的js是放在本地的,这里面图片的刷新,图片的点击都需要js去实现,那么就有js注入了,google一番,webview支持在html加载之后注入js,webview官方说需要在onPageFinished或者onProgressChande加载到100%之后注入js,这个也比较好理解,注入完成之后就可以响应js的点击和刷新等function了

好了,我能想到的就这么多,如果有错误,请指正

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值