Android之webview

参考:https://blog.csdn.net/qq_36252044/article/details/77993297

https://blog.csdn.net/lowprofile_coding/article/details/77928614

https://blog.csdn.net/carson_ho/article/details/64904691/   个人认为写的最好的一个

1.WebView介绍

Android WebView在Android平台上是一个特殊的View, 基于webkit引擎、展现web页面的控件,这个类可以被用来在你的app中仅仅显示一张在线的网页,还可以用来开发浏览器。WebView内部实现是采用渲染引擎来展示view的内容,提供网页前进后退,网页放大,缩小,搜索。Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。

现在很多APP都内置了Web网页,比如说很多电商平台,淘宝、京东、聚划算等等。WebView比较灵活,不需要升级客户端,只需要修改网页代码即可。一些经常变化的页面可以用WebView这种方式去加载网页。例如中秋节跟国庆节打开的页面不一样,如果是用WebView显示的话,只修改修改html页面就行,而不需要升级客户端。

Webview功能强大,可以直接使用html文件(本地sdcard/assets目录),还可以直接加载url,使用JavaScript可以html跟原生APP互调。

2.加载html四种方式

webView.loadUrl("http://139.196.35.30:8080/OkHttpTest/apppackage/test.html");//加载url

webView.loadUrl("file:///android_asset/test.html");//加载asset文件夹下html

//方式3:加载手机sdcard上的html页面
webView.loadUrl("content://com.ansen.webview/sdcard/test.html");

//方式4 使用webview显示html代码
webView.loadDataWithBaseURL(null,"<html><head><title> 欢迎您 </title></head>" +
        "<body><h2>使用webview显示 html代码</h2></body></html>

 

3.WebSettings的一些配置

WebSettings settings = wv.getSettings();

        //允许使用js
        settings.setJavaScriptEnabled(true);

        //允许JS弹窗
        settings.setJavaScriptCanOpenWindowsAutomatically(true);

        //支持屏幕缩放
        settings.setSupportZoom(true);
        settings.setBuiltInZoomControls(true);
        //不显示缩放按钮
        settings.setDisplayZoomControls(false);

        //设置自适应屏幕,两者合用
        //将图片调整到适合webview的大小
        settings.setUseWideViewPort(true);
        // 缩放至屏幕的大小
        settings.setLoadWithOverviewMode(true);

        /**
         * LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
         * LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
         * LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
         * LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
         */
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

        //设置可以访问文件
        settings.setAllowFileAccess(true);

        //支持自动加载图片
        settings.setLoadsImagesAutomatically(true);

        //设置编码格式
        settings.setDefaultTextEncodingName("utf-8");

4.我的代码

Android的:

package com.ysl.myandroidbase.activity;

import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.Toast;

import com.ysl.myandroidbase.R;

public class WebViewActivity extends AppCompatActivity {
    public static final String TAG = "WebViewActivity";
    private WebView wv;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        button = findViewById(R.id.b);
        wv = findViewById(R.id.wv);

        WebSettings settings = wv.getSettings();

        //允许使用js
        settings.setJavaScriptEnabled(true);

        //允许JS弹窗
        settings.setJavaScriptCanOpenWindowsAutomatically(true);

        //支持屏幕缩放
        settings.setSupportZoom(true);
        settings.setBuiltInZoomControls(true);
        //不显示缩放按钮
        settings.setDisplayZoomControls(false);

        //设置自适应屏幕,两者合用
        //将图片调整到适合webview的大小
        settings.setUseWideViewPort(true);
        // 缩放至屏幕的大小
        settings.setLoadWithOverviewMode(true);

        /**
         * LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
         * LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
         * LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
         * LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
         */
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

        //设置可以访问文件
        settings.setAllowFileAccess(true);

        //支持自动加载图片
        settings.setLoadsImagesAutomatically(true);

        //设置编码格式
        settings.setDefaultTextEncodingName("utf-8");

        wv.loadUrl("http://www.baidu.com");

        //加载asset文件夹下html
        // 格式规定为:file:///android_asset/文件名.html
//        wv.loadUrl("file:///android_asset/testJS.html");

        //使用webview显示html代码
//        wv.loadDataWithBaseURL(null,"<html><head><title> 欢迎您 </title></head>" +
//                "<body><h2>使用webview显示 html代码</h2></body></html>", "text/html" , "utf-8", null);

        //添加js监听 这样html就能调用客户端
        // 通过addJavascriptInterface()将Java对象映射到JS对象
        //参数1:Javascript要调用的Java对象
        //参数2:Java对象名,这个名字可以任意起,但要和js调用的地方一致
        wv.addJavascriptInterface(this,"android");

        //WebViewClient主要帮助WebView处理各种通知、请求事件
        wv.setWebViewClient(new WebViewClient(){
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                Log.d(TAG,"onPageStarted--->开始加载了--->"+url);
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                Log.d(TAG,"onPageFinished--->加载结束了--->"+url);
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Log.d(TAG,"shouldOverrideUrlLoading--->url--->"+url);
                if(url.equals("http://www.google.com/")){
                    Toast.makeText(WebViewActivity.this,"国内不能访问google,拦截该url",Toast.LENGTH_LONG).show();
                    return true;//表示我已经处理过了
                }
                //true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器。默认false。
                try {
                    if(url.startsWith("http:") || url.startsWith("https:") ) {
                        view.loadUrl(url);
                    }else{
                        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                        startActivity(intent);
                    }
                    return true;
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
            }
        });

        //WebChromeClient主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度等
        wv.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onReceivedTitle(WebView view, String title) {
                super.onReceivedTitle(view, title);
                Log.d(TAG,"onReceivedTitle--->获取到网页的标题--->"+title);
            }

            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Log.d(TAG,"onJsAlert--->获取到JS--->"+message);
                AlertDialog.Builder localBuilder = new AlertDialog.Builder(wv.getContext());
                localBuilder.setTitle("Alert")
                        .setMessage(message)
                        .setNegativeButton("取消",null)
                        .setPositiveButton("确定",null)
                        .setCancelable(false)
                        .create()
                        .show();

                //注意:
                //必须要这一句代码:result.confirm()表示:
                //处理结果为确定状态同时唤醒WebCore线程
                //否则不能继续点击按钮
                result.confirm();
                //返回true,拦截系统的alert提示;
                return true;
            }

            @Override
            public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
                return super.onJsConfirm(view, url, message, result);
            }

            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                return super.onJsPrompt(view, url, message, defaultValue, result);
            }

            @Override
            public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
                return super.onJsBeforeUnload(view, url, message, result);
            }

            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                Log.d(TAG,"onProgressChanged--->正在加载中--->"+newProgress);
            }
        });

        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 注意调用的JS方法名要对应上
                // 调用javascript的callJS()方法
                if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
                    wv.loadUrl("javascript:callJS()");
                } else {
                    wv.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
                        @Override
                        public void onReceiveValue(String value) {
                            System.out.println("value -----------------》 "+value);
                            Toast.makeText(WebViewActivity.this, "evaluateJavascript:"+value, Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
        });
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d(TAG,"是否有上一个页面:"+wv.canGoBack());
        if (wv.canGoBack() && keyCode == KeyEvent.KEYCODE_BACK){//点击返回按钮的时候判断有没有上一页
            wv.goBack(); // goBack()表示返回webView的上一页面
            return true;
        }
        return super.onKeyDown(keyCode,event);
    }
    @Override
    protected void onDestroy() {
        if(wv != null){
            wv.clearHistory();
            ((ViewGroup)wv.getParent()).removeView(wv);
            wv.destroy();
            wv = null;
        }
        super.onDestroy();
    }

    /**
     * JS调用android的方法
     */
    @JavascriptInterface
    public void  getClient(String str){
        Log.d(TAG,"html调用客户端:"+str);
        Toast.makeText(WebViewActivity.this, str, Toast.LENGTH_SHORT).show();
    }
}

html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>这是标题啊</title>
    <style type="text/css">
        dd {
            margin-top:30px; /* 上外边距30像素 */
        }
    </style>
</head>
<body>
<div id="wrap">
    <div id="header"><h1>Webview简单使用</h1>
    </div>
    <div id="main">
        <dl>
            <dd><a href="http://www.baidu.com">点击跳转到百度</a></dd>
            <dd><a href="https://www.jd.com">点击跳转到google</a></dd>
            <!--<dd>
                <button id='callback_client1' onclick="callBackClient1()" type="button">用js调用alert
                </button>
            </dd>-->
            <dd>
                <button id='callback_client2' onclick="callAndroid()" type="button">用js调用android
                </button>
            </dd>
        </dl>
    </div>
</div>
</body>

<script>
function callJS(){
    alert("alert android调用js");
    return "返回android调用js";
}
function callAndroid(){
    javascript:android.getClient("js调用android");
}
</script>
</html>

5.遇到的错误:

错误一:

如上图错误,加载不了任何的网页。解决需要在清单文件application标签下添加:android:usesCleartextTraffic="true"

错误二:

这是由于webview只能识别简单的scheme,如“http”和“https”。像自定义的这些是不能加载的,需要我们重新去定义url.

在设置的WebViewClient的回调方法shouldOverrideUrlLoading方法中可以来做一些修改。

@Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Log.d(TAG,"shouldOverrideUrlLoading--->url--->"+url);
                //true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器。默认false。
                try {
                    if(url.startsWith("http:") || url.startsWith("https:") ) {
                        view.loadUrl(url);
                    }else{
                        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                        startActivity(intent);
                    }
                    return true;
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
            }

错误三:

出现了两个弹窗,第一个在上面,第二个在下面。这是由于在WebChromeClient的回调方法onJsAlert中没有返回true或false,而是使用了父类的返回值。修改:

@Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Log.d(TAG,"onJsAlert--->获取到JS--->"+message);
                AlertDialog.Builder localBuilder = new AlertDialog.Builder(wv.getContext());
                localBuilder.setTitle("Alert")
                        .setMessage(message)
                        .setNegativeButton("取消",null)
                        .setPositiveButton("确定",null)
                        .setCancelable(false)
                        .create()
                        .show();

                //注意:
                //必须要这一句代码:result.confirm()表示:
                //处理结果为确定状态同时唤醒WebCore线程
                //否则不能继续点击按钮
                result.confirm();
                //返回true,拦截系统的alert提示;
                return true;
            }

6.注意点

对于Android调用JS代码的方法有2种:
1. 通过WebViewloadUrl()  JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用。
2. 通过WebViewevaluateJavascript()  Android 4.4 后才可使用

所以,最终的写法是这样的:

// 注意调用的JS方法名要对应上
                // 调用javascript的callJS()方法
                if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
                    wv.loadUrl("javascript:callJS()");
                } else {
                    wv.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
                        @Override
                        public void onReceiveValue(String value) {
                            System.out.println("value -----------------》 "+value);
                            Toast.makeText(WebViewActivity.this, "evaluateJavascript:"+value, Toast.LENGTH_SHORT).show();
                        }
                    });
                }

对于JS调用Android代码的方法有3种: 
1. 通过WebView的addJavascriptInterface()进行对象映射        

注意点:(存在严重的漏洞问题,具体请看文章:你不知道的 Android WebView 使用漏洞

//添加js监听 这样html就能调用客户端
        // 通过addJavascriptInterface()将Java对象映射到JS对象
        //参数1:Javascript要调用的Java对象
        //参数2:Java对象名,这个名字可以任意起,但要和js调用的地方一致
        wv.addJavascriptInterface(this,"android");


2. 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url 
3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

后两种方式请参考:Android:你要的WebView与 JS 交互方式 都在这里了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值