Android之WebView加载网页与JS交互使用

前言:我在做WebView加载网页时,可能会出现未知的各种错误情况,所以在xml布局时,是不会直接把WebView控件写在xml布局中的额,为了防止对webview的引用无法断开,所以我在布局写一个布局容器,比如:LinearLayout ,然后在代码中动态的创建WebView控件,然后添加到容器布局LinearLayout中,然后在activity的生命周期中onDestory()  方法中把WebView移除销毁。不知道这样做是不是会更好一些。目前我是这么做的。

1、首先先定义全局控件:

private WebView webView;
private WebSettings webSettings;
private LinearLayout linearLayout;
private ProgressBar progress;  //加一个原生的加载框,当网页重新加载的时候显示,加载完隐藏
ConnectivityManager connectivityManager;  // 网络连接
private ValueCallback mUploadMessage; // 打开相册
private ValueCallback mUploadMessageForAndroid5; // 打开相册
private int FILECHOOSER_RESULTCODE = 111;  
private int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 115;
private int RESULT_OK = 0xA1;
private WebViewInterFace webViewInterFace;

2、在onCreate方法中初始化控件,但是WebView是没有的,动态创建添加

progress = findViewById(R.id.progress);
linearLayout = findViewById(R.id.linearLayout);

// 动态创建WebView 控件,设置宽高为MATCH_PARENT,添加webview到父布局的linearLayout中,
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);

webView = new WebView(this);
webView.setLayoutParams(layoutParams);
linearLayout.addView(webView);

3、下面开始给WebView设置WebSetting了,这里设置的比较多,应为项目里面基本所有的功能都是以webview显示处理的

        webSettings = webView.getSettings();
        webView.setWebContentsDebuggingEnabled(true);
        webSettings.setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
        webView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET );
        // 加载初始化登录的url页面
        webView.loadUrl(webUrl);
        webView.getSettings().setDomStorageEnabled(true); //设置DOM Storage缓存
        webSettings.setJavaScriptEnabled(true); // 支持js脚本
        webView.getSettings().setAppCacheMaxSize(1024*1024*8);    // ----
        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();//  ---
        webView.getSettings().setAppCachePath(appCachePath);  // ---
        String ua = webSettings.getUserAgentString();
        // 设置在本WebView中加载网页时添加网页的UserAgent的标识,用于在js中判断处理
        webSettings.setUserAgentString(ua + ";operationApp");
        webSettings.setAllowContentAccess(true); // 允许在WebView中访问内容URL
        webSettings.setNeedInitialFocus(false); // 调用时是否需要设置节点以获得焦点
        webSettings.setCacheMode(android.webkit.WebSettings.LOAD_NO_CACHE);  // 设置缓存
        webSettings.setPluginState(android.webkit.WebSettings.PluginState.ON);
        webSettings.setAllowFileAccess(true);  // 启用文件访问
        webSettings.setAllowFileAccessFromFileURLs(false);  // 允许js访问其他url的内容
        webSettings.setAllowUniversalAccessFromFileURLs(false);
        webSettings.setSavePassword(false);
        webView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
        webView.requestFocusFromTouch();
        webView.requestFocus();
        webView.setFocusable(true);
        webView.setLongClickable(true);
        webSettings.setAppCacheEnabled(true); // 应用缓存API是否可用  结合setAppCachePath(String)使用
        //是否使用内置的缩放机制
        // 缩放机制包括屏幕上的缩放控件(浮于WebView内容之上)和缩放手势的运用。通过setDisplayZoomControls(boolean)可以控制是否显示这些控件
        webSettings.setBuiltInZoomControls(true);
        webSettings.setUseWideViewPort(true); // 是否支持HTML的“viewport”标签或者使用wide viewport
        webSettings.setLoadWithOverviewMode(true); // 是否允许WebView度超出以概览的方式载入页面
        webSettings.setDatabaseEnabled(true);  // 设置是否启用数据库存储API
        webSettings.setGeolocationEnabled(true);  // 设置是否启用地理位置
        webView.setWebChromeClient(new android.webkit.WebChromeClient());  // 处理解析,渲染网页等
        // 创建和js交互的接口类,这里我把和js交互的接口类单独领出来写在一个类里面了,然后加了回调处理
        webViewInterFace = new WebViewInterFace(this);
        // 设置js和Android调用的绑定,js端通过  window.android. 加上Android端的接口类中方法即可调用
        webView.addJavascriptInterface(webViewInterFace,"android");
        //这里是一个处理webview的回调
        webViewChromeClient();
        webViewInterFace.setActionListener(this);
        // 加快HTML网页加载完成速度
        if (Build.VERSION.SDK_INT >= 19) {
            webSettings.setLoadsImagesAutomatically(true);
        } else {
            webSettings.setLoadsImagesAutomatically(false);
        }

4、webViewChromeClient() 方法

    /**
     * 网页加载情况处理
     */
    private void webViewChromeClient() {
        webView.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onProgressChanged(android.webkit.WebView view, int newProgress) {
                progress.setProgress(newProgress*100);
                if (newProgress == 100){
                    // 当newProgress为100时表示当前页面加完完成,隐藏即可
                    progress.setVisibility(View.GONE);
                }
                super.onProgressChanged(view, newProgress);
            }

            @Override
            public void onReceivedTitle(android.webkit.WebView view, String title) {
                super.onReceivedTitle(view, title);
                // 获取当前当前网页的头部标题 title ,判断是否网页正常加载出来,加载失败时显示无内容界面,
                // selectUrl 是一个定义的成员String 空字符串,为了处理js中类似跳转简写方式时,只写一个javscript 在这里会出问题。
                if (selectUrl.equals("javscript")){
                    return;
                }else {
                    selectUrl = "";
                    if (title.contains(getString(R.string.no_netWork))){
                        Util.showToast(getString(R.string.no_internet));
                        webView.setVisibility(View.GONE);
                        ll_noNetWork.setVisibility(View.VISIBLE);
                    }else {
                        webView.setVisibility(View.VISIBLE);
                        ll_noNetWork.setVisibility(View.GONE);
                    }
                }

            }

            //For Android  >= 4.1
            public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) {
                openFileChooserImpl(uploadMsg);
            }
            // For Android > 5.0
            @Override
            public boolean onShowFileChooser(android.webkit.WebView webView, ValueCallback uploadMsg, android.webkit.WebChromeClient.FileChooserParams fileChooserParams) {
                openFileChooserImplForAndroid5(uploadMsg);
                return true;
            }


        });
    }

5、监听webview的加载状态 setWebViewLoad(); 这个方法是在websetting中设置完了之后直接就初始化这个方法

    /**
     * 设置webview加载状态
     */
    private void setWebViewLoad() {
        webView.setWebViewClient(new WebViewClient(){
            /**
             * 表示页面中的所有url点击跳转操作都由自己处理,不用手机系统自带的浏览器处理
             * @
             * view
             * @param url
             * @return
             */
            @Override
            public boolean shouldOverrideUrlLoading(android.webkit.WebView view, String url) {
                if (url.equals("javscript:;")){
                    selectUrl = "javscript";

                }else
                    view.loadUrl(url);

                return true;
            }

            /**
             * 加载页面之前调用
             * @param view
             * @param url
             * @param favicon
             */
            @Override
            public void onPageStarted(android.webkit.WebView view, String url, Bitmap favicon) {
                // 在这里进行显示正在加载的Dialog
                progress.setVisibility(View.VISIBLE);
                // 判断当前是否有可用网络,有网络时从网络获取,无网络时
                final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

                if (networkInfo == null || !networkInfo.isAvailable()){
                    // 表示无网络  根据cache-control决定是否从网络上取数据
                    webView.getSettings().setCacheMode(android.webkit.WebSettings.LOAD_DEFAULT);
                    Util.showToast(getString(R.string.no_internet));
                }else {
                    // 表示有网络   只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据
                    webView.getSettings().setCacheMode(android.webkit.WebSettings.LOAD_CACHE_ELSE_NETWORK);
                }
                super.onPageStarted(view, url, favicon);
            }

            /**
             * 页面加载结束调用
             * @param view
             * @param url
             */
            @Override
            public void onPageFinished(android.webkit.WebView view, String url) {
                // 在这里进行隐藏正在加载的Dialog
                progress.setVisibility(View.GONE);
                super.onPageFinished(view, url);
            }

            /**
             * 请求网络资源的时候都会调用
             * @param view
             * @param url
             */
            @Override
            public void onLoadResource(android.webkit.WebView view, String url) {
                super.onLoadResource(view, url);
            }

        });
    }

6、openFileChooserImpl(uploadMsg)  方法和 openFileChooserImplForAndroid5(uploadMsg); 这是js里面有打开手机相册,获取照片时需要的

private void openFileChooserImpl(ValueCallback uploadMsg) {
        mUploadMessage = uploadMsg;
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
    }
    // 跳转手机相册
    private void openFileChooserImplForAndroid5(ValueCallback uploadMsg) {
        mUploadMessageForAndroid5 = uploadMsg;

        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE_FOR_ANDROID_5);

    }

7、对js获取手机照片的回调

    /**
     * 对获取照片结果的回调
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // 选择图片后的回调
        if (requestCode == FILECHOOSER_RESULTCODE) {
            if (null == mUploadMessage)
                return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;

        } else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
            // 选择图片后一般执行此条件
            if (null == mUploadMessageForAndroid5)
                return;
            // 获取返回的照片uri地址
            Uri result ;
            if (data != null && data.getData() != null){
                result = data.getData();
            }else {
                return;
            }

            if (data != null) {
                mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result});
            } else {
                mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
            }
            mUploadMessageForAndroid5 = null;
        }
    }

8、重写返回键事件,用于捕获在网页上返回时,直接关闭页面的情况,

    /**
     * 重写onKeyDown 处理手机返回键按下事件
     * 如何按下返回键时webview可以返回上一个页面就执行返回上一个页面
     * 当回到主界面时,点击一次返回提示,点击两次退出程序
     * @param keyCode
     * @param event
     * @return
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
            webView.goBack();// 返回前一个页面
            return true;
        }else {
             // clickNum 初始化为 0 ,就是为了判断点击返回两次,就关闭当前activity, 
            clickNum ++ ;
            if (clickNum < 2){
                Toast.makeText(YWWebAllActivity.this,getString(R.string.quit),Toast.LENGTH_SHORT).show();
                return true;
            }
            return super.onKeyDown(keyCode, event);
        }
    }

9、onDestory() 处理webview

    /**
     * 运行销毁onDestroy时清除webview所占的内存
     */
    @Override
    protected void onDestroy() {
        if (webView != null){
            ViewParent parent = webView.getParent();
            if(parent != null){
                ((ViewGroup)parent).removeView(webView);
            }
            webView.stopLoading();
            webView.getSettings().setJavaScriptEnabled(false);
            webView.clearHistory();
            webView.clearView();
            webView.removeAllViews();
            webView.destroy();

        }
        super.onDestroy();
    }

10、和js交互的接口类,单独写在一个类里面,避免activity写太多了,这里需要加什么调用的方法,可以随便加,方法上加上@JavascriptInterface就行了

/**
 * WebView 和App端接口类
 */
public class WebViewInterFace {
    private Context context;
    public WebViewInterFace(Context context) {
        this.context = context;
    }

    /**
     * 提供给js调用获取用户登录信息方法
     * @return
     */
    @JavascriptInterface
    public String getUserInfo(){
        final String userInfo = MySharedPreferenceUtil.getUserLoginInfoJson();
        return userInfo;
    }

    /**
     * 提供给js调用,获取token
     * @return
     */
    @JavascriptInterface
    public String getToken(){
        String token = MySharedPreferenceUtil.getWebToken();
        return token;
    }

    /**
     *  提供给js保存登录信息方法
     */
    @JavascriptInterface
    public void saveInfo(String data,int status) {
        // 保存js调用传递过来的数据data,
    }
}

在多加一个,上面介绍js调用android,这里加一个android调用js方法:

//androidCallBack() 为js中写的function方法。前面的javascript: 为固定写法,如果想向js传递数据,可在androidCallBack()括号中添加参数,用字符串拼接的方法。
// 比如:webView.loadUrl("javascript:androidCallBack('"+ 参数 +"')");
webView.loadUrl("javascript:androidCallBack()");

好了上面就是Android中WebView和js交互的情况了。

你可能感兴趣的:(WebView,JavaScript,原生和网页交互,Android开发,Android和JS交互)