首页 » FastDev4Android » 正文

重写WebView网页加载以及JavaScript注入详解


(一).前言:
今天我们来学习一下重写WebView组件来实现网页的加载,以及我们平时APP开发中经常使用的JS注入,js和java相互调用的问题来重点讲解一下。如果大家都WebView加载还不是太熟悉的话,这边我之前专门写了一个WebView的专题,其他包含基本使用和js注入的问题。(点击进入WebView进阶专题)
FastDev4Android框架项目地址:https://github.com/jiangqqlmj/FastDev4Android
(二).重写WebView:
2.1首先我们来看一下实现的效果:

2.2.重写WebView:创建一个HTML5CustomWebView类集成自WebView,然后就是一下几个步骤实现:

  •   布局文件
  •   WebSettings初始化相关设置
  •  设置重写WebChromeClient进行相关处理
  • 设置重写WebViewClient进行相关处理即可

简单的来说就是以上这几步就可以了
2.3.WebView布局文件主要定义导航栏,网页加载进度,以及WebView容器布局,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
< ?xml version="1.0" encoding="utf-8"?>
<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white">
    <relativelayout android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <framelayout android:id="@+id/fullscreen_custom_content"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@color/white"
            android:visibility="gone" />
        <linearlayout android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" >
            <!-- 自定义顶部导航功能条 -->
            <include layout="@layout/common_top_bar_layout" />
            <!-- 中间显示内容 -->
            <framelayout android:id="@+id/main_content"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:visibility="gone" />
            <!-- 网页加载进度显示 -->
            <framelayout android:id="@+id/frame_progress"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:visibility="visible" >
                <linearlayout android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:orientation="vertical" >

                    <progressbar style="@android:style/Widget.ProgressBar.Small"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center_horizontal"
                        android:indeterminate="false"
                        android:indeterminateDrawable="@drawable/loading_small" />

                    <textview android:id="@+id/webview_tv_progress"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="3dip"
                        android:text="正在加载,已完成0%..."
                        android:textSize="12sp" />
                </linearlayout>
            </framelayout>
        </linearlayout>
    </relativelayout>
</framelayout>

2.4.WebSettings初始化设置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
WebSettings webSettings = this.getSettings();
webSettings.setJavaScriptEnabled(true);  //开启javascript
webSettings.setDomStorageEnabled(true);  //开启DOM
webSettings.setDefaultTextEncodingName("utf-8"); //设置编码
// // web页面处理
webSettings.setAllowFileAccess(true);// 支持文件流
// webSettings.setSupportZoom(true);// 支持缩放
// webSettings.setBuiltInZoomControls(true);// 支持缩放
webSettings.setUseWideViewPort(true);// 调整到适合webview大小
webSettings.setLoadWithOverviewMode(true);// 调整到适合webview大小
webSettings.setDefaultZoom(ZoomDensity.FAR);// 屏幕自适应网页,如果没有这个,在低分辨率的手机上显示可能会异常
webSettings.setRenderPriority(RenderPriority.HIGH);
//提高网页加载速度,暂时阻塞图片加载,然后网页加载好了,在进行加载图片
webSettings.setBlockNetworkImage(true);
//开启缓存机制
webSettings.setAppCacheEnabled(true);
//根据当前网页连接状态
if(StrUtils.getAPNType(context)== StrUtils.WIFI){
//设置无缓存
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
}else{
//设置缓存
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}

2.5.WebChromeClient进行相关设置处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
private class MyWebChromeClient extends WebChromeClient {
                private Bitmap mDefaultVideoPoster;
                @Override
                public void onShowCustomView(View view,
                                CustomViewCallback callback) {
                        super.onShowCustomView(view, callback);
                        HTML5CustomWebView.this.setVisibility(View.GONE);
                        if (mCustomView != null) {
                                callback.onCustomViewHidden();
                                return;
                        }
                        mCustomViewContainer.addView(view);
                        mCustomView = view;
                        mCustomViewCallback = callback;
                        mCustomViewContainer.setVisibility(View.VISIBLE);
                }

                @Override
                public void onHideCustomView() {
                        if (mCustomView == null) {
                                return;
                        }
                        mCustomView.setVisibility(View.GONE);
                        mCustomViewContainer.removeView(mCustomView);
                        mCustomView = null;
                        mCustomViewContainer.setVisibility(View.GONE);
                        mCustomViewCallback.onCustomViewHidden();
                        HTML5CustomWebView.this.setVisibility(View.VISIBLE);
                        super.onHideCustomView();
                }

                /**
                 * 网页加载标题回调
                 * @param view
                 * @param title
                 */

                @Override
                public void onReceivedTitle(WebView view, String title) {
                        Log.d("zttjiangqq", "当前网页标题为:" + title);
                        wv_tv_title.setText(title);
                }

                /**
                 * 网页加载进度回调
                 * @param view
                 * @param newProgress
                 */

                @Override
                public void onProgressChanged(WebView view, int newProgress) {
                        // 设置进行进度
                        ((Activity) mContext).getWindow().setFeatureInt(
                                        Window.FEATURE_PROGRESS, newProgress * 100);
                        webview_tv_progress.setText("正在加载,已完成" + newProgress + "%...");
                        webview_tv_progress.postInvalidate(); // 刷新UI
                        Log.d("zttjiangqq", "进度为:" + newProgress);
                }

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

                }
        }

2.6.WebViewClient进行相关设置处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
private class MyWebViewClient extends WebViewClient {
                /**
                 * 加载过程中 拦截加载的地址url
                 * @param view
                 * @param url  被拦截的url
                 * @return
                 */

                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                        Log.i("zttjiangqq", "-------->shouldOverrideUrlLoading url:" + url);
                        //这边因为考虑到之前项目的问题,这边拦截的url过滤掉了zttmall://开头的地址
                        //在其他项目中 大家可以根据实际情况选择不拦截任何地址,或者有选择性拦截
                        if(!url.startsWith("zttmall://")){
                        Uri mUri = Uri.parse(url);
                        List<string> browerList = new ArrayList</string><string>();
                        browerList.add("http");
                        browerList.add("https");
                        browerList.add("about");
                        browerList.add("javascript");
                        if (browerList.contains(mUri.getScheme())) {
                                return false;
                        } else {
                                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                                intent.addCategory(Intent.CATEGORY_BROWSABLE);
                                //如果另外的应用程序WebView,我们可以进行重用
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                intent.putExtra(Browser.EXTRA_APPLICATION_ID,
                                                FDApplication.getInstance()
                                                                .getApplicationContext().getPackageName());
                                try {
                                        FDApplication.getInstance().startActivity(intent);
                                        return true;
                                } catch (ActivityNotFoundException ex) {
                                }
                        }
                                return false;
                        }else {
                                return true;
                        }
                }
                /**
                 * 页面加载过程中,加载资源回调的方法
                 * @param view
                 * @param url
                 */

                @Override
                public void onLoadResource(WebView view, String url) {
                        super.onLoadResource(view, url);
                        Log.i("zttjiangqq", "-------->onLoadResource url:" + url);
                }
                /**
                 * 页面加载完成回调的方法
                 * @param view
                 * @param url
                 */

                @Override
                public void onPageFinished(WebView view, String url) {
                        super.onPageFinished(view, url);
                        Log.i("zttjiangqq", "-------->onPageFinished url:" + url);
                        if (isRefresh) {
                                isRefresh = false;
                        }
                        // 加载完成隐藏进度界面,显示WebView内容
                        frame_progress.setVisibility(View.GONE);
                        mContentView.setVisibility(View.VISIBLE);
                        // 关闭图片加载阻塞
                        view.getSettings().setBlockNetworkImage(false);

                }
                /**
                 * 页面开始加载调用的方法
                 * @param view
                 * @param url
                 * @param favicon
                 */

                @Override
                public void onPageStarted(WebView view, String url, Bitmap favicon) {
                        Log.d("zttjiangqq", "onPageStarted:-----------"+url);
                        super.onPageStarted(view, url, favicon);
                }

                @Override
                public void onReceivedError(WebView view, int errorCode,
                                String description, String failingUrl) {
                        super.onReceivedError(view, errorCode, description, failingUrl);
                }

                @Override
                public void onScaleChanged(WebView view, float oldScale, float newScale) {
                        super.onScaleChanged(view, oldScale, newScale);
                        HTML5CustomWebView.this.requestFocus();
                        HTML5CustomWebView.this.requestFocusFromTouch();
                }
        }

以上一般使用到得方法已经做了相关的注释。
最后一步就是使用了,使用起来很简单,创建一个Activity,然后把我们定义的WebView加入到布局然后加载网页即可。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package com.chinaztt.fda.html5;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.webkit.DownloadListener;
import android.webkit.JavascriptInterface;
/**
 * 当前类注释:
 * 项目名:FastDev4Android
 * 包名:com.chinaztt.fda.html5
 * 作者:江清清 on 15/11/06 08:59
 * 邮箱:jiangqqlmj@163.com
 * QQ: 781931404
 * 公司:江苏中天科技软件技术有限公司
 */

import com.chinaztt.fda.ui.base.BaseActivity;

public class HTML5WebViewCustomAD extends BaseActivity {
        private HTML5CustomWebView mWebView;
        //http://www.zttmall.com/Wapshop/Topic.aspx?TopicId=18
        private String ad_url = "http://www.baidu.com/";
        private String title="百度一下你就知道";
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                mWebView = new HTML5CustomWebView(this, HTML5WebViewCustomAD.this,title,ad_url);
                mWebView.setDownloadListener(new DownloadListener() {
                        @Override
                        public void onDownloadStart(String url, String userAgent,
                                        String contentDisposition, String mimetype,
                                        long contentLength) {
                                Uri uri = Uri.parse(url);
                                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                startActivity(intent);
                        }
                });
        //准备javascript注入
                mWebView.addJavascriptInterface(
                                new Js2JavaInterface(),"Js2JavaInterface");
                if (savedInstanceState != null) {
                        mWebView.restoreState(savedInstanceState);
                } else {
                        if (ad_url != null) {
                                mWebView.loadUrl(ad_url);
                        }
                }
                setContentView(mWebView.getLayout());
               
        }
        @Override
        public void onSaveInstanceState(Bundle outState) {
                super.onSaveInstanceState(outState);
                if (mWebView != null) {
                        mWebView.saveState(outState);
                }
        }

        @Override
        protected void onResume() {
                super.onResume();
                if (mWebView != null) {
                        mWebView.onResume();
                }
        }

        @Override
        public void onStop() {
                super.onStop();
                if (mWebView != null) {
                        mWebView.stopLoading();
                }
        }
        @Override
        protected void onPause() {
                super.onPause();
                if (mWebView != null) {
                        mWebView.onPause();
                }
        }

        @Override
        protected void onDestroy() {
                super.onDestroy();
                if (mWebView != null) {
                        mWebView.doDestroy();
                }
        }

        @Override
        public void onConfigurationChanged(Configuration newConfig) {
                super.onConfigurationChanged(newConfig);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
                return super.onTouchEvent(event);
        }

        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
                return super.onKeyDown(keyCode, event);
        }
        @Override
        public void onBackPressed() {
                if (mWebView != null) {
                        if(mWebView.canGoBack()){
                                mWebView.goBack();
                        }else{
                                mWebView.releaseCustomview();
                        }
                }
                super.onBackPressed();
        }
        /**
         * JavaScript注入回调
         */

        public class Js2JavaInterface {
                private Context context;
                private String TAG = "Js2JavaInterface";
                @JavascriptInterface
                public void showProduct(String productId){
                        if(productId!=null){
                                //进行跳转商品详情
                                showToastMsgShort("点击的商品的ID为:" + productId);
                        }else {
                                showToastMsgShort("商品ID为空!");
                        }
                }
        }
}

(三).js注入方法:
仔细查看上面的代码可能大家会发现这样两块地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mWebView.addJavascriptInterface(
                new Js2JavaInterface(),"Js2JavaInterface");
public class Js2JavaInterface {
        private Context context;
        private String TAG = "Js2JavaInterface";
        @JavascriptInterface
        public void showProduct(String productId){
                if(productId!=null){
                        //进行跳转商品详情
                        showToastMsgShort("点击的商品的ID为:" + productId);
                }else {
                        showToastMsgShort("商品ID为空!");
                }
        }
}

这边就是js注入回调处理的方法,在这边的实例中是使用http://www.zttmall.com/Wapshop/Topic.aspx?TopicId=18这个地址进行加载网页的时候才会生效,因为这边点击网页图片的时候,html代码中加载js方法,
我们来看一下网页的源代码:
查看源代码我们就知道

1
2
mWebView.addJavascriptInterface(
new Js2JavaInterface(),"Js2JavaInterface");

第二个参数就是在js方法中调用的对象名字,然后注入对象中回调的方法和js方法中的方法一样即可。这样就完成了一次注入点击回调工作,我们的html就可以和原生java代码发生交互了。使用这种方式非常有助于我们的混合开发。
好了到此重写WebView实现以及js注入的基本使用就讲完了,具体全部代码已经上传到FastDev4Android项目中了。同时欢迎大家去Github站点进行clone或者下载浏览:https://github.com/jiangqqlmj/FastDev4Android 同时欢迎大家star和fork整个开源快速开发框架项目~
尊重原创,转载请注明:From Sky丶清(http://www.lcode.org) 侵权必究!
关注我的订阅号(codedev123),每天分享移动开发技术(Android/IOS),项目管理以及博客文章!(欢迎关注,第一时间推送精彩文章)
关注我的微博,可以获得更多精彩内容