Android WebView应用详解

来源:互联网 发布:ubuntu配置jdk1.8 编辑:程序博客网 时间:2024/06/11 08:45

蛙扑安卓:

WebView是Android中一个非常实用的组件,它和Safai、Chrome一样都是基于Webkit网页渲染引擎,可以通过加载HTML数据的方式便捷地展现软件的界面。使用WebView开发软件有一下几个优点:

1.可以打开远程URL页面,也可以加载本地HTML数据;

2.可以无缝的在java和javascript之间进行交互操作;

3.高度的定制性,可根据开发者的需要进行多样性定制。

下面就通过例子来介绍一下WebView的使用方法。

我们先建一个webview项目,项目结构如左图:

在这个项目中,我们会先进入MainActivity这个导航界面(上边右图),通过点击不同按钮转向不同的Activity,下面分别简单介绍一下这几个Activity的所要演示的功能:

LoadActivity:主要演示加载网络页面和WebView的一些基本设置;

CaptureActivity:主要演示WebView的截图的功能;

FileActivity:主要演示访问本地文件的功能;

JSActivity:主要演示WebView对JS的支持以及JS和Java之间的互相调用;

接下来,我们会逐一分析各个Activity的相关信息:

LoadActivity:

与之对应的布局文件为load.xml,演示效果如下:

我们在文本框中输入URL,然后点击“load”按钮,然后由WebView加载相应的页面,在加载过程中,根据加载进度更新窗口的进度条。由于布局相对简单,我们主要来看一下LoadActivity.java的代码:

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
        packagecom.scott.webview;
                 
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
                 
public class LoadActivity extendsActivity {
                     
    privateWebView webView;
                     
    @Override
    protectedvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                         
        //设置窗口风格为进度条
        getWindow().requestFeature(Window.FEATURE_PROGRESS);
                         
        setContentView(R.layout.load);
                         
        webView = (WebView) findViewById(R.id.webView);
                         
        WebSettings settings = webView.getSettings();
        settings.setSupportZoom(true);         //支持缩放
        settings.setBuiltInZoomControls(true); //启用内置缩放装置
        settings.setJavaScriptEnabled(true);   //启用JS脚本
                         
        webView.setWebViewClient(newWebViewClient() {
            //当点击链接时,希望覆盖而不是打开新窗口
            @Override
            publicbooleanshouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url); //加载新的url
                returntrue;   //返回true,代表事件已处理,事件流到此终止
            }
        });
                         
        //点击后退按钮,让WebView后退一页(也可以覆写Activity的onKeyDown方法)
        webView.setOnKeyListener(newView.OnKeyListener() {
            @Override
            publicbooleanonKey(View v, intkeyCode, KeyEvent event) {
                if(event.getAction() == KeyEvent.ACTION_DOWN) {
                    if(keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
                        webView.goBack();  //后退
                        returntrue;   //已处理
                    }
                }
                returnfalse;
            }
        });
                         
        webView.setWebChromeClient(newWebChromeClient() {
            //当WebView进度改变时更新窗口进度
            @Override
            publicvoidonProgressChanged(WebView view,intnewProgress) {
                //Activity的进度范围在0到10000之间,所以这里要乘以100
                LoadActivity.this.setProgress(newProgress *100);
            }
        });
                         
        finalEditText url = (EditText) findViewById(R.id.url);
                         
        Button loadURL = (Button) findViewById(R.id.loadURL);
        loadURL.setOnClickListener(newView.OnClickListener() {
            @Override
            publicvoidonClick(View v) {
                webView.loadUrl(url.getText().toString()); //加载url
                webView.requestFocus();//获取焦点
            }
        });
    }
}

可以看到,我们使用loadUrl方法加载一个url页面,然后重写WebChromeClient的onProgressChanged方法更新进度条。loadUrl方法除了能加载远程页面,还能加载本地的文件:

1
2
3
4
   //加载assets中的html文件
webView.loadUrl("file:///android_asset/index.html");
//加载sdcard中的html文件
webView.loadUrl("file:///mnt/sdcard/index.html");

这些都会在后边的示例中使用到。

CaptureActivity:

与之对应的布局文件为capture.xml,也比较简单,它的演示效果如下:

记得在AndroidManifest.xml中加入对sdcard的写权限:

1
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

截图成功后,在/mnt/sdcard目录下会生成一个当前网页的截图,如图:

我们导出一下,看看是不是当前的网页界面:

整个过程操作已经完成了,让我们来看一下CaptureActivity.java的代码:

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
        packagecom.scott.webview;
             
import java.io.FileOutputStream;
             
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Picture;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.Toast;
             
public class CaptureActivity extendsActivity {
                 
    privatestaticfinal String TAG = "CAPTURE";
                 
    privateWebView webView;
                 
    @Override
    protectedvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                     
        setContentView(R.layout.capture);
                     
        webView = (WebView) findViewById(R.id.webView);
        webView.loadUrl("http://www.baidu.com");
                     
        Button capture = (Button) findViewById(R.id.capture);
        capture.setOnClickListener(newView.OnClickListener() {
            @Override
            publicvoidonClick(View v) {
                //取得android.graphics.Picture实例
                Picture picture = webView.capturePicture();
                intwidth = picture.getWidth();
                intheight = picture.getHeight();
                if(width >0 && height > 0) {
                    //创建指定高宽的Bitmap对象
                    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                    //创建Canvas,并以bitmap为绘制目标
                    Canvas canvas =newCanvas(bitmap);
                    //将WebView影像绘制在Canvas上
                    picture.draw(canvas);
                    try{
                        String fileName ="/sdcard/webview_capture.jpg";
                        FileOutputStream fos =newFileOutputStream(fileName);
                        //压缩bitmap到输出流中
                        bitmap.compress(Bitmap.CompressFormat.PNG,90, fos);
                        fos.close();
                        Toast.makeText(CaptureActivity.this,"CAPTURE SUCCESS", Toast.LENGTH_LONG).show();
                    }catch(Exception e) {
                        Log.e(TAG, e.getMessage());
                    }
                }
            }
        });
    }
}

FileActivity:

这个界面没有布局文件,在代码中完成,它将演示如何加载一段html代码,并应用刚才生成的网页截图,效果如下图:

在这个过程中,我们加载了截图,并给图加上了红色的边框,CaptureActivity.java代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        packagecom.scott.webview;
            
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
            
public class FileActivity extendsActivity {
    @Override
    protectedvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WebView webView =newWebView(this);
        webView.getSettings().setAllowFileAccess(true);//默认就是启用的,这里只是强调一下
        String baseURL ="file:///mnt/sdcard/";         //根URL
        String html ="<html><body>"
                    +"<h3>image from sdcard:<h3><br/>"
                    +"<img src='webview_capture.jpg' style='border:2px solid #FF0000;'/>"
                    +"</body></html>";
        //加载相对于根URL下的数据,historyUrl设为null即可
        webView.loadDataWithBaseURL(baseURL, html,"text/html","utf-8", null);
                    
        setContentView(webView);
    }
}

如果将html文本保存成.html文件,放于/mnt/sdcard目录下,然后用以下方式加载也能达到相同的效果:

1
webView.loadUrl("file:///mnt/sdcard/index.html");

接下来是最后一个示例:JSActivity,也是最精彩的示例,演示如何在JS和Java之间通信,我们来看一下演示过程,如图:

然后谈谈我们的执行过程,我们需要在Java代码中设置WebView的一些事件的响应,比如alert、confirm以及prompt这些JS事件,让它们按照我们的要求呈现给用户,然后我们需要定义一个Java和JS之间的接口对象,当我们加载完一个html文档后,根据这个对象的接口名称就可以在文档的JS代码中轻松的调用这个接口对象的方法,执行Java代码,我们也可以在Java端执行html文档中的JS代码。下面我们将通过代码来证实这一过程:

JSActivity.java代码如下:

 
 packagecom.scott.webview;
          
import java.util.ArrayList;
import java.util.List;
          
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.EditText;
import android.widget.Toast;
          
public class JSActivity extendsActivity {
              
    privatestaticfinal String TAG = "JSActivity";
              
    private WebView webView;
              
    privateHandler handler =newHandler() {
        publicvoidhandleMessage(android.os.Message msg) {
            intindex = msg.arg1;
            JSActivity.this.setProgress(index *1000);
        };
    };
              
    @Override
    publicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                  
        getWindow().requestFeature(Window.FEATURE_PROGRESS);
                  
        webView =newWebView(this);
                  
        webView.getSettings().setJavaScriptEnabled(true);
                  
        webView.addJavascriptInterface(newObject() {
            @SuppressWarnings("unused")
            publicList<String> getList() {
                List<String> list =newArrayList<String>();
                for(inti =0; i <=10; i++) {
                    try{
                        Thread.sleep(200);
                    }catch(InterruptedException e) {
                        Log.e(TAG,"error:"+ e.getMessage());
                    }
                    list.add("current index is: "+ i);
                              
                    //不能在此直接调用Activity.setProgress,否则会报以下错误
                    //Only the original thread that created a view hierarchy can touch its views.
                    Message msg = handler.obtainMessage();
                    msg.arg1 = i;
                    handler.sendMessage(msg);
                }
                success();
                returnlist;
            }
                      
            publicvoidsuccess() {
                //由Java代码调用JS函数
                webView.loadUrl("javascript:success('congratulations')");
            }
        },"bridge");
                  
        webView.setWebChromeClient(newWebChromeClient() {
            @Override
            publicbooleanonJsAlert(WebView view, String url, String message,finalJsResult result) {
                newAlertDialog.Builder(JSActivity.this)
                        .setTitle("alert")
                        .setMessage(message)
                        .setPositiveButton("YES",newDialogInterface.OnClickListener() {
                            @Override
                            publicvoidonClick(DialogInterface dialog,intwhich) {
                                //处理结果为确定状态 同时唤醒WebCore线程
                                result.confirm();
                            }
                        }).create().show();
                returntrue;   //已处理
            }
          
            @Override
            publicboolean onJsConfirm(WebView view, String url, String message,finalJsResult result) {
                newAlertDialog.Builder(JSActivity.this)
                        .setTitle("confirm")
                        .setMessage(message)
                        .setPositiveButton("YES",newDialogInterface.OnClickListener() {
                            @Override
                            publicvoidonClick(DialogInterface dialog,intwhich) {
                                Toast.makeText(JSActivity.this,"you clicked yes",0).show();
                                result.confirm();
                            }
                        })
                        .setNegativeButton("NO",newDialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,intwhich) {
                                //处理结果为取消状态 同时唤醒WebCore线程
                                result.cancel();
                            }
                        }).create().show();
                returntrue;
            }
                      
            @Override
            publicbooleanonJsPrompt(WebView view, String url, String message, String defaultValue,
                    finalJsPromptResult result) {
                LayoutInflater inflater = getLayoutInflater();
                View prompt = inflater.inflate(R.layout.prompt,null);
                finalEditText text = (EditText) prompt.findViewById(R.id.prompt_input);
                text.setHint(defaultValue);
                          
                newAlertDialog.Builder(JSActivity.this)
                        .setTitle("prompt")
                        .setView(prompt)
                        .setPositiveButton("YES",newDialogInterface.OnClickListener() {
                            @Override
                            publicvoidonClick(DialogInterface dialog,intwhich) {
                                //记录结果
                                result.confirm(text.getText().toString());
                            }
                        })
                        .setNegativeButton("NO",newDialogInterface.OnClickListener() {
                            @Override
                            publicvoidonClick(DialogInterface dialog,intwhich) {
                                result.cancel();
                            }
                        }).create().show();
                returntrue;
            }
        });
          
        //加载assets中的html文件
        webView.loadUrl("file:///android_asset/index.html");
                  
        setContentView(webView);
    }
}

需要注意的是,在重写onJsAlert、onJsConfirm、onJsPrompt这几个方法中,不要忘了用JsResult.confirm()或JsResult.cancel()处理结果,否则页面就不能再响应接下的事件了,关于这一点,我们可以看一下JsResult的代码:


        /**
     * Handle a confirmation response from the user.
     */
    publicfinalvoidconfirm() {
        mResult =true;
        wakeUp();
    }
         
/**
     * Handle the result if the user cancelled the dialog.
     */
    publicfinalvoidcancel() {
        mResult =false;
        wakeUp();
    }

可以看到confirm和cancel方法都涉及到了一个wakeUp方法,这个方法主要作用是唤醒WebCore线程,定义如下:

1
2
3
4
5
6
7
8
9
10
    /* Wake up the WebCore thread. */
protectedfinalvoidwakeUp() {
    if(mReady) {
        synchronized(mProxy) {
            mProxy.notify();
        }
    }else{
        mTriedToNotifyBeforeReady =true;
    }
}

所以朋友们如果要重写这几个方法时要切记处理JsResult这个对象实例。

我们在处理onJsPrompt时,使用了自定义的界面,加载的是/res/layout/prompt.xml,定义如下:

1
2
3
4
5
6
7
8
9
10
        <?xml version="1.0"encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content">
  <EditText
        android:id="@+id/prompt_input"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

在JSActivity.java代码中,我们注意到WebView组件加载了assets中的index.html,它包含与Java交互的JS代码,如下:

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
        <html>
    <head>
        <script type="text/javascript">
            function doAlert() {
                alert("hello!");
            }
      
            function doConfirm() {
                confirm("are you sure?");
            }
      
            function doPrompt() {
                var val = prompt("what's your name?");
                if(val) {
                    alert("your name is:"+ val);
                }
            }
      
            function getList() {
                //使用java和javascript的接口bridge的方法获取集合
                var list = window.bridge.getList();
                var result = document.getElementById("result");
                for(var i =0; i < list.size(); i++) {
                    var div = document.createElement("div");
                    div.innerHTML = list.get(i).toString();
                    result.appendChild(div);
                }
            }
      
            function success(msg) {
                alert(msg);
            }
        </script>
    </head>
    <body background="black">
        <input type="button"value="alert"onclick="doAlert()"/><br/>
        <input type="button"value="confirm"onclick="doConfirm()"/><br/>
        <input type="button"value="prompt"onclick="doPrompt()"/><br/>
        <input type="button"value="getList"onclick="getList()"/><br/>
        <div id="result"></div>
    </body>
</html>
0 0
原创粉丝点击