Android WebView 运用

1、webview 1.0版本 ---最简单的加载网页

 mWebView.loadUrl("http://blog.csdn.net/");

这样基本上是可以显示网页的,不过显示的内容可能和你想象的不大一样,因为没有配置各种setting。比如JS的使用,缩放,是否可以访问文件等等。

2、webview 1.1版本 -- 设置各种参数


        // HTML5支持的配置
        WebSettings webSettings = webView.getSettings();
      
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDefaultTextEncodingName("utf-8");
        webSettings.setBuiltInZoomControls(true);
        webSettings.setSupportZoom(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setAllowFileAccess(true);  //设置可以访问文件
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
        webSettings.setLoadsImagesAutomatically(true);  //支持自动加载图片
        webSettings.setUseWideViewPort(true);  //将图片调整到适合webview的大小
            webSettings.setUserAgentString("userAgent");
        // pc网页屏幕自动适配
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);
        //支持多窗口模式
        webSettings.supportMultipleWindows();
        //html的支持
        webSettings.setSavePassword(false);
        webSettings.setSaveFormData(false);
        // 启用数据库
        webSettings.setDatabaseEnabled(true);
        String dir = context.getApplicationContext()
                .getDir("database", Context.MODE_PRIVATE).getPath();
        // 设置数据库路径
        webSettings.setDatabasePath(dir);
        // 使用localStorage则必须打开
        webSettings.setDomStorageEnabled(true);
        // 启用地理定位
        webSettings.setGeolocationEnabled(true);
        // 设置定位的数据库路径
        webSettings.setGeolocationDatabasePath(dir);

根据我们的需求合理设置WebSettings。这样我们基本上可以打开自己想要的页面,也有了和页面相应的交互了。但是这个可能还满足不了工作上的需求。比如我要在Webview内部跳转,加上进度条,对某些链接进行特殊处理等等,而不是点击调到系统的浏览器里面。那么我们开始下一个版本。

3、webview 1.2版本
我们需要认识一下 两个新朋友

  • WebViewClient类:处理各种通知 & 请求事件
  • WebChromeClient :辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。


    全部方法.png

    首先来看一下WebViewClient 中的shouldOverrideUrlLoading方法的描述

Give the host application a chance to take over the control when a new
     * url is about to be loaded in the current WebView. If WebViewClient is not
     * provided, by default WebView will ask Activity Manager to choose the
     * proper handler for the url. If WebViewClient is provided, return true
     * means the host application handles the url, while return false means the
     * current WebView handles the url.
     * This method is not called for requests using the POST "method".

中文意思大概是:当一个新的URL加载到这个WebView里的时候,主程序拥有一个接管控制权的机会,如歌这个类没有使用,默认是询问Activity Manager 选择一个恰当的处理程序给这个webview。如果我们使用这个WebViewClient,如果返回true,那么意味着交给主程序去处理这个URL,如果返回false,那么就是当前webview处理这个url。该方法不使用 POST的请求方式。翻译的很烂,英语好的可以替我翻译一下。

当我看到这个的时候也是一知半解,啥意思啊,主程序处理和webview的处理区别在哪里呢?那我们就做个demo来看一下吧。

  mWebView.setWebViewClient(new MyWebViewClient());

public class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        boolean isOverride = super.shouldOverrideUrlLoading(view, url);
        Log.i("webview","url shouldOverrideUrlLoading:"+isOverride+"");
        return isOverride;
    }
}

如果我们设置了setWebViewClient,就说明以后的URL处理交给了这个webview,这个也是webviewclient的含义,然后我们来看返回值得区别。通过看源码或者英文描述,默认返回false,就是说明我们不会重写URl的loading,也就是说一切的点击都这在这个webview里面,相当于我们自己的浏览器。
如果返回true就说明你劫持了这个url,你想做什么自己决定,可以劫持这个url我们重新打开一个WebActivity。这样我们就可以对特定的url做处理了。
比如其他的onLoadResource、onReceivedHttpError、onPageStarted、onPageFinished就很简单了,根据名字就知道大概的功能了,就不一一讲解了。大家可以测试一下。
如果我们想要给我们的webview加上进度条又该怎么办呢?那就需要WebChromeClient大兄弟了。

 @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
    }

    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
    }

    @Override
    public void onReceivedIcon(WebView view, Bitmap icon) {
        super.onReceivedIcon(view, icon);
    }

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

    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
    }

这几个是比较常用的方法。
onProgressChanged可以监听到打开页面的进度。
onReceivedTitle可以获取页面的title
onReceivedIcon可以获取页面的Icon
onShowFileChooser webview调用系统的文件系统
重点和难点是 webview如何如何能打开比如图库,摄像头等功能。
因为不同android版本的打开方法不一样所以我们要

   //For Android 3.0+
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
        mOpenFileChooserCallBack.openFileChooserCallBack(uploadMsg, acceptType);
    }


    // For Android < 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        openFileChooser(uploadMsg, "");
    }


    // For Android  > 4.1.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        openFileChooser(uploadMsg, acceptType);
    }


    // For Android > 5.0
    @Override
    public boolean onShowFileChooser(WebView webView, com.tencent.smtt.sdk.ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
        mOpenFileChooserCallBack.openFileChooserCallBackAndroid5(valueCallback, "");
        return true;
    }

根据不同api来调用我们的处理函数。
既然js会调用这些方法中的一个,其实也就是分为5.0以上和以下两个回调方法。那么我们也相应的让activity回调webview的这个方法。

public interface OpenFileChooserCallBack {
        void openFileChooserCallBack(ValueCallback<Uri> uploadMsg, String acceptType);

        void openFileChooserCallBackAndroid5(ValueCallback<Uri[]> uploadMsg, String acceptType);
    }

然后就是打开图库或者摄像头

  @Override
    public void openFileChooserCallBack(ValueCallback<Uri> uploadMsg, String acceptType) {
        mValueCallback = uploadMsg;
        if (checkRights()) {
            showOptions();
        }
    }

    @Override
    public void openFileChooserCallBackAndroid5(ValueCallback<Uri[]> uploadMsg, String acceptType) {
        mValueCallbackAndroid5 = uploadMsg;
        if (checkRights()) {
            showOptions();
        }
    }
 //检车读写文件 拍照像个权限
    private boolean checkRights() {
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED
                || ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            //申请WRITE_EXTERNAL_STORAGE权限
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA},
                    WRITE_EXTERNAL_STORAGE_REQUEST_CODE);
            return false;
        }
        return true;
    }

    public void showOptions() {
        android.app.AlertDialog.Builder alertDialog = new android.app.AlertDialog.Builder(this);
        alertDialog.setOnCancelListener(new ReOnCancelListener());
        alertDialog.setTitle("选择图片");
        alertDialog.setItems(new String[]{"相机选取", "拍照", "取消"}, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (which == 0) {
                            mSourceIntent = ImageUtil.choosePicture();
                            startActivityForResult(mSourceIntent, REQUEST_CODE_PICK_IMAGE);
                            overridePendingTransition(R.anim.in_from_left, R.anim.out_to_right);
                        } else if (which == 1) {
                            mSourceIntent = ImageUtil.takeBigPicture();
                            startActivityForResult(mSourceIntent, REQUEST_CODE_IMAGE_CAPTURE);
                            overridePendingTransition(R.anim.in_from_left, R.anim.out_to_right);
                        } else {
                            dialog.dismiss();
                            dialog.cancel();
                        }
                    }
                }
        );
        alertDialog.show();
    }

那么怎么把图片的信息交给js呢?

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {

        Log.i("webview", "onActivityResult");
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        switch (requestCode) {

            case REQUEST_CODE_IMAGE_CAPTURE:
            case REQUEST_CODE_PICK_IMAGE: {
                try {
                    if (mValueCallback == null && mValueCallbackAndroid5 == null) {
                        return;
                    }
                    String sourcePath = ImageUtil.retrievePath(this, mSourceIntent, data);
                    if (TextUtils.isEmpty(sourcePath) || !new File(sourcePath).exists()) {
                        Log.i("webview", "sourcePath empty or not exists.");
                        break;
                    }
                    Uri uri = Uri.fromFile(new File(sourcePath));
                    if (mValueCallback != null) {
                        mValueCallback.onReceiveValue(uri);
                        Log.i("webview", "   mValueCallback.onReceiveValue(uri)"+uri);
                    }

                    if (mValueCallbackAndroid5 != null) {
                        mValueCallbackAndroid5.onReceiveValue(new Uri[]{uri});
                        Log.i("webview", "   mValueCallbackAndroid5.onReceiveValue(uri)"+uri);
                    }

                    //通知更新图库
                    if (requestCode == REQUEST_CODE_IMAGE_CAPTURE) {
                        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }

我们回调方法里面ValueCallback<Uri> mValueCallback,ValueCallback<Uri[]> mValueCallbackAndroid5 负责把消息带给js,如果没有url那么记得 =null,不然下次回调方法不会调用。我猜测是因为 有一个值得判断,如果为空才会相应,不为空就不相应。感兴趣的朋友可以看看源码,我的水平有限,希望各位朋友不吝指教。
对于交互http://blog.csdn.net/carson_ho/article/details/52693322大神写的很好,可以参考这个。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,738评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,377评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,774评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,032评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,015评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,239评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,724评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,374评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,410评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,457评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,132评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,733评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,804评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,022评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,515评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,116评论 2 341

推荐阅读更多精彩内容