Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)

简述: 

  WebView是什么?有什么用途?我们先来看一下官方介绍:

     A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.
    Note that, in order for your Activity to access the Internet and load web pages in a WebView, you must add the INTERNET permissions to your Android Manifest file:
  大致意思就是说WebView(网络视图)能加载显示网页,可以将其视为一个浏览器。在使用的过程中需要加入网络权限。了解了WebView我们接下来学习一下怎么使用,本篇博文主要讲解以下内容:
   ① WebView加载网络资源
   ② WebView加载本地HTML
   ③ WebView中Android调用JS方法
   ④ WebView中JS调用Android方法
   ⑤ HTML5的使用
   ⑥ JS的简单使用

WebView加载网络资源

  加载网络资源的一般步骤如下:
     ①在XML文件中引入WebView控件
     ②在Activity中创建WebView实例
     ③对WebView进行必要的设置(根据需要)
     ④加载网络资源

    WebView简单加载网页

    我们按上述步骤简单地实现加载百度首页,首先创建XML文件,就是简单的在相应Activity页面中引入WebView控件,如下:



    

    在Activity中创建WebView实例、加载网络资源比较简单,我对代码做了比较详细地注释,直接上代码吧。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private WebView webview;
    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn = (Button) findViewById(R.id.btn);
        //创建WebView实例
        webview = (WebView) findViewById(R.id.webview);

        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (R.id.btn != v.getId())
            return;
        //跳转到http://www.baidu.com页面
      webview.loadUrl("http://www.baidu.com");
    }
}
    效果图:
                                         Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第1张图片

    改进一:不再跳转第三方浏览器

        那么这样还是有问题,每次都要启动第三方浏览器来加载网页,我们能不能只在App内部显示而不跳转第三方浏览器呢?Google早以想到这样的情况啦!app内部调用主要利用了方法setWebViewClient(),该方法里相当于在App内部设置一个浏览器客户端,该方法的参数就是一个WebViewClient实例。
        代码如下:
public class WebViewActivity extends Activity {

    private WebView webview_in;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.web_activity);

        webview_in = (WebView) findViewById(R.id.webview_in);
        //web资源
        webview_in.loadUrl("http://www.baidu.com");
        //设置WebViewClient客户端
        webview_in.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
    }
}
        效果图:
                                     Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第2张图片

    改进二:WebView开启JavaScript功能、按返回键网页后退而不是退出浏览器的问题

    从上面的gif动态可以看出这次并没有跳转到第三方浏览器,而是跳转到一个Activity中(该Activity中只包含一个WebView控件)。但是也可以很明显地看出该WebViewClient存在一些问题:
        ①未开启JavaScript功能 :该浏览器未开启JavaScript功能(在动态图中点击“百度知道 ”时可以看出提示);
        ②按返回键网页后退而不是退出浏览器的问题 :当我们在“百度知道 ” 页面点击返回键时并没有回到百度首页,而是跳转出了WebViewActivity。
            ok,我们先来解决第一个问题:为WebView开启JavaScript功能。
            核心代码如下:
/**
* 方法描述:启用支持javascript
*/
private void openJavaScript() {
    WebSettings settings = webview_in.getSettings();
    settings.setJavaScriptEnabled(true);
}
       我们再来看一下第二个问题:网页后退而不是退出浏览器的问题。
        需要用到的API:
             ①WebView.canGoBack():Gets whether this WebView has a back history item.
             ②WebView.goBack(): Goes back in the history of this WebView.
        解决该问题的核心代码:
/**
* 方法描述:改写物理按键——返回的逻辑
*
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        if (webview_in.canGoBack()) {
            webview_in.goBack();//返回上一页面
            return true;
        } else {
            System.exit(0);//退出程序
        }
    }
    return super.onKeyDown(keyCode, event);
}
 现在把改进后的完整代码贴出来:
public class WebViewActivity extends Activity {

    private WebView webview_in;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.web_activity);

        webview_in = (WebView) findViewById(R.id.webview_in);
        openJavaScript();

        //web资源
        webview_in.loadUrl("http://www.baidu.com");
        //设置WebViewClient客户端
        webview_in.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
    }

    /**
    * 方法描述:启用支持javascript
    */
    private void openJavaScript() {
        WebSettings settings = webview_in.getSettings();
        settings.setJavaScriptEnabled(true);
    }

    /**
    * 方法描述:改写物理按键——返回的逻辑
    *
    * @param keyCode
    * @param event
    * @return
    */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (webview_in.canGoBack()) {
                webview_in.goBack();//返回上一页面
                return true;
            } else {
                System.exit(0);//退出程序
            }
        }
        return super.onKeyDown(keyCode, event);
    }
}
    解决完以上两个问题以后我们再来看一下效果图:
                                              Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第3张图片

    改进三:加载进度条

    我先贴出来在模拟器中打开浏览器浏览的情况的一张图:    
                                              Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第4张图片          
   
    上面这幅动态图大家可以看出,每打开一个网页就会在该网页顶端显示一个蓝色的进度条,所以我们对WebView再进一步优化:显示进度条。
     为了在WebView顶部显示进度条我们使用ProgressBar控件。为了显示进度我们需要用到的方法为setWebChromeClient (WebChromeClient client),该方法就是为了主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度等。它与WebViewClient的区别请看这篇章: WebViewClient与WebChromeClient的区别。OK,废话不说,接下来就自定义一个WebView,代码来了!!!
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.webkit.WebView;
import android.widget.ProgressBar;

/**
* Created by lzy on 2016/9/22.
*/

public class ProgressWebView extends WebView {
    private ProgressBar progressbar;

    public ProgressWebView(Context context) {
        super(context);
    }

    public ProgressWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public ProgressWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initProgressBar(context);
        setWebChromeClient(new WebChromeClient());
    }

    private void initProgressBar(Context context) {
        progressbar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal);
        progressbar.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dp2px(context, 3), 0, 0));
        //改变progressbar默认进度条的颜色(深红色)为Color.GREEN
        progressbar.setProgressDrawable(new ClipDrawable(new ColorDrawable(Color.GREEN), Gravity.LEFT, ClipDrawable.HORIZONTAL));
        addView(progressbar);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
    }

    /**
    * 方法描述:根据手机的分辨率从 dp 的单位 转成为 px(像素)
    */
    public int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
    * 类描述:显示WebView加载的进度情况
    */
    public class WebChromeClient extends android.webkit.WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                progressbar.setVisibility(GONE);
            } else {
                if (progressbar.getVisibility() == GONE)
                    progressbar.setVisibility(VISIBLE);

                progressbar.setProgress(newProgress);
            }
            super.onProgressChanged(view, newProgress);
        }
    }
}
        我们在布局文件中引入ProgressWebView:



    
    效果图:
                                              Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第5张图片

效果是不是很强劲很完美。。。。

    封装:

    TinyWebView

     进过对上面对WebView的一步一步改进, 现在我们自定义一个WebView---TinyWebView,一个让其支持App内部显示资源、支持JavaScript、支持显示进度条的WebView。OK,上代码:
package com.lzy.webviewdemo;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;

/**
* Created by lzy on 2016/9/22.
*/

public class TinyWebView extends WebView {
    private ProgressBar progressbar;

    public TinyWebView(Context context) {
        super(context);
    }

    public TinyWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public TinyWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initProgressBar(context);
        openJavaScript();
        setWebViewClient(new WebViewClient());
        setWebChromeClient(new WebChromeClient());
    }

    private void initProgressBar(Context context) {
        progressbar = new ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal);
        progressbar.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dp2px(context, 3), 0, 0));
        //改变progressbar默认进度条的颜色(深红色)为Color.GREEN
        progressbar.setProgressDrawable(new ClipDrawable(new ColorDrawable(Color.GREEN), Gravity.LEFT, ClipDrawable.HORIZONTAL));
        addView(progressbar);
    }

    /**
    * 方法描述:启用支持javascript
    */
    private void openJavaScript() {
        WebSettings settings = getSettings();
        settings.setJavaScriptEnabled(true);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
    }

    /**
    * 方法描述:根据手机的分辨率从 dp 的单位 转成为 px(像素)
    */
    public int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
    * 类描述:显示WebView加载的进度情况
    */
    public class WebChromeClient extends android.webkit.WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 100) {
                progressbar.setVisibility(GONE);
            } else {
                if (progressbar.getVisibility() == GONE)
                    progressbar.setVisibility(VISIBLE);

                progressbar.setProgress(newProgress);
            }
            super.onProgressChanged(view, newProgress);
        }
    }
}

      WebViewActivity      

      当某个事件触发跳转到一个只含有WebView的Activity页面时,我们可以封装一个这样的Activity。该Activity接受的参数可以自己根据情况而定,先给出一个简单的封装类WebViewActivity。其布局文件中包含一个TextView用于显示WebView页面的标题,还有一个WebView控件。比较简单所以布局文件就不再贴出来了!直接看
public class WebViewActivity extends Activity {
    private TextView webviewTitle;
    private TinyWebView progressWebview;
    private String title;
    private String webViewUrl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.web_activity);

        getData();
        initViews();
        loadData();
    }

    /**
    * 方法描述:接收数据
    */
    private void getData() {
        webViewUrl = getIntent().getStringExtra("webview_url");
        title = getIntent().getStringExtra("webview_title");
    }

    /**
    * 方法描述:初始化WebView
    */
    private void initViews() {
        progressWebview = (TinyWebView) findViewById(R.id.progress_webview);
        webviewTitle = (TextView) findViewById(R.id.text_webView_title);
        //web资源
        progressWebview.loadUrl(webViewUrl);
    }

    /**
    * 方法描述:加载数据
    */
    private void loadData() {
        if (!TextUtils.isEmpty(title))
            webviewTitle.setText(title);

        if (TextUtils.isEmpty(webViewUrl))
            progressWebview.loadUrl(webViewUrl);
    }

    /**
    * 方法描述:改写物理按键——返回的逻辑
    */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (progressWebview.canGoBack()) {
                progressWebview.goBack();//返回上一页面
                return true;
            } else {
                System.exit(0);//退出程序
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    /**
    * 方法描述:
    *
    * @param activity    发起跳转的Activity
    * @param webviewUrl  WebView的url
    * @param webviewTitle WebView页面的标题
    */
    public static void skip(Activity activity, String webviewUrl, String webviewTitle) {
        Intent intent = new Intent(activity, WebViewActivity.class);
        intent.putExtra("webview_url", webviewUrl);
        intent.putExtra("webview_title", webviewTitle);
        activity.startActivity(intent);
    }
}
        使用如下:
WebViewActivity.skip(MainActivity.this,"http://www.baidu.com","百度首页");
       感受一下效果:
                                                 Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第6张图片

WebView加载本地HTML

    OK,关于网络资源的加载到此结束,接下来看一下本地HTML资源的加载!
    现在下来加载一个纯纯的本地的HTML文件,这样加载出来的HTML页面不能与Android端进行通信。加载本地HTML文件步骤:
    1、创建HTML文件,并将其assets文件夹(当然assets文件夹也可以存放js文件)
    2、利用WebView加载本地HTML
           ① 如果html文件存于assets:则加前缀:file:///android_asset/
           ② 如果html文件存于sdcard:则加前缀:content://com.android.htmlfileprovider/sdcard/
      注意:content前缀可能导致异常,直接使用file:///sdcard/ or file:/sdcard也可以
  创建一个HTML文件
       我们先创建一个HTML文件命名为myhtmlfile.html(这里使用的是HTML5编写) :该文件只显示一段文本和一张图片。



    
    


打油诗
富了投机倒把的
提了吹牛拍马的
树了弄虚作假的
苦了奉公守法的
    在Activity中加载:
      重点就在这:localWebview.loadUrl("file:///android_asset/myhtmlfile.html");
public class LocalWebViewActivity extends Activity{

    private WebView localWebview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.local_webview_activity);

        localWebview = (WebView) findViewById(R.id.local_webview);
        WebSettings settings = localWebview.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setSupportZoom(true);
        localWebview.loadUrl("file:///android_asset/myhtmlfile.html");
        localWebview.setWebViewClient(new WebViewClient());
    }
}
    看一下效果如何:
                                     Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第7张图片

WebView与JS交互

    仅仅加载本地HTML比较简单,但是这样也不能与Android交互,显得实用性也不是很强呀!!!那么接下来就来讲一下Android与JS的交互。
     上面的实例中我们只是简单地展示了HTML页面,但是HTML页面却不能与操控Android端的代码,那么现在我们来实现以下HTML中的JS代码调用Android端的方法。

    JS调用Android

      1、首先要定义一个接口,这个接口相当于前端JS的服务接口(这里指的是Android端,此时两者的交互有点像CS结构,JS就是客户端,Android是服务端),该接口中定义JS想要操控Android代码的一些方法。拿上面的案例来说,当我点击图片时Android要弹出一个Toast,那么在这个接口中就要为JS准备一个弹出Toast的方法。
      2、要对WebView对象调用addJavascriptInterface()方法,这个方法就是把上一步定义的接口添加到WebView当中,意思就好像"WebView页面中的JS客户端可以调用Android端的方法了";
           addJavascriptInterface()方法中有两个参数这里重点解释一下,第一个参数就是我们第一步中定义的接口,那么第一个方法为String类型,那么它到底是什么意思呢?它又有什么作用呢?第二个参数就是我们第一个参数的“替身”,为什么需要替身呢?JS调用Android代码的时候,在前端JS代码中不可能直接调用“接口名称 .XXX()”,所以这时候就用到替身了。那么JS中调用Android代码(更准确地说是调用我们定义的接口)就可以“替身.XXX()”。
      说了这么多现在赶紧实现一下。我们得实现在上述例子的基础上给图片添加一个点击事件,当用户点击图片的时候JS调用Android接口中的方法弹出一个Toast。
      我先把前端HTML页面展示出来,其实就是在上述的基础上给图片添加了一个“onClick()”事件。



    
    
    


打油诗
富了投机倒把的
提了吹牛拍马的
树了弄虚作假的
苦了奉公守法的
          这里要提醒一下,红色的“qwert”就是我们刚才说的“替身”,这个替身可以随意命名,但是注意要与addJavascriptInterface()中的第二个参数一致。
    我们接下里再来看看Android端代码:
public class LocalWebViewActivity extends Activity{

    private WebView localWebview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.local_webview_activity);

        localWebview = (WebView) findViewById(R.id.local_webview);
        WebSettings settings = localWebview.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setSupportZoom(true);
        //WebView添加JS要调用的接口,注意参数
        localWebview.addJavascriptInterface(new JsInterface(),"qwert");
        localWebview.loadUrl("file:///android_asset/myhtmlfile.html");
        localWebview.setWebViewClient(new WebViewClient());
    }

    /**
    * 接口描述:供JS调用的接口
    */
    public class JsInterface{
        /**
        *  方法描述:弹出一个Toast
        * @param message JS端需要传递的参数(也就是要Toast的内容)
        */
        @JavascriptInterface
        public void showToast(String message) {
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
        }
    }
}
    与上一个案例中的代码变化的地方用红色标记了。现在看一下效果:
                                       Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第8张图片
    JS调用Android中的方法现在已经实现,那么我们接下来看看Android调用JS的方法,Android代用JS可比JS调用Android简单多了哦!!!

    Android调用JS

    步骤:
      1、加载HTML:
webview.loadUrl("file:///android_asset/myhtmlfile.html");
      2、设置JavaScript可用
WebSettings settings = localWebview.getSettings();
settings.setJavaScriptEnabled(true);
      3、调用HTML中用到的JavaScript方法
       通过webView.loadUrl("javascript:xxx")方式就可以调用当前网页中的名称为xxx的javascript方法,如下:
webView.loadUrl("javascript:changeTitle('Android调用JS操控HTML界面')");
    实例:
    我们在之前的HTML页面中再添加一个JS方法,让这个方法改变打油诗的标题,调整后的HTML代码如下:



    
    
    


打油诗
富了投机倒把的
提了吹牛拍马的
树了弄虚作假的
苦了奉公守法的
    为了更能简单地说明问题,我们再建一个Activity类,由于比较简单直接上代码:
/**
* 类描述:Android调用JS
* Created by lzy on 2016/9/22.
*/
public class AndroidInvokeJS extends Activity {

    private Button btn_android_invoke_js;
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.android_invoke_js);
        btn_android_invoke_js = (Button) findViewById(R.id.btn_android_invoke_js);

        webView = (WebView) findViewById(R.id.wv_android_invoke_js);
        // ①加载HTML文件
        webView.loadUrl("file:///android_asset/myhtmlfile.html");
        // ②设置JavaScript可用
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        btn_android_invoke_js.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // ③调用HTML中用到的JavaScript方法
                webView.loadUrl("javascript:changeTitle('Android调用JS操控HTML界面')");
            }
        });

    }
}
    效果图:
                                           Android WebView使用全面解析(加载网络资源、本地HTML,JS交互)_第9张图片
现在既实现了Android调用JS又现在了JS调用Android,那么将两者结合一下即可实现Android与JS的相互调用哦!!!

将代码整理后的下载链接: 源码下载

你可能感兴趣的:(Android)