zoukankan      html  css  js  c++  java
  • Android开发学习笔记:WebView 二

    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的代码:

    [java] view plaincopy
     
    1. package com.scott.webview;  
    2.   
    3. import android.app.Activity;  
    4. import android.os.Bundle;  
    5. import android.view.KeyEvent;  
    6. import android.view.View;  
    7. import android.view.Window;  
    8. import android.webkit.WebChromeClient;  
    9. import android.webkit.WebSettings;  
    10. import android.webkit.WebView;  
    11. import android.webkit.WebViewClient;  
    12. import android.widget.Button;  
    13. import android.widget.EditText;  
    14.   
    15. public class LoadActivity extends Activity {  
    16.       
    17.     private WebView webView;  
    18.       
    19.     @Override  
    20.     protected void onCreate(Bundle savedInstanceState) {  
    21.         super.onCreate(savedInstanceState);  
    22.           
    23.         //设置窗口风格为进度条  
    24.         getWindow().requestFeature(Window.FEATURE_PROGRESS);  
    25.           
    26.         setContentView(R.layout.load);  
    27.           
    28.         webView = (WebView) findViewById(R.id.webView);  
    29.           
    30.         WebSettings settings = webView.getSettings();  
    31.         settings.setSupportZoom(true);          //支持缩放  
    32.         settings.setBuiltInZoomControls(true);  //启用内置缩放装置  
    33.         settings.setJavaScriptEnabled(true);    //启用JS脚本  
    34.           
    35.         webView.setWebViewClient(new WebViewClient() {  
    36.             //当点击链接时,希望覆盖而不是打开新窗口  
    37.             @Override  
    38.             public boolean shouldOverrideUrlLoading(WebView view, String url) {  
    39.                 view.loadUrl(url);  //加载新的url  
    40.                 return true;    //返回true,代表事件已处理,事件流到此终止  
    41.             }  
    42.         });  
    43.           
    44.         //点击后退按钮,让WebView后退一页(也可以覆写Activity的onKeyDown方法)  
    45.         webView.setOnKeyListener(new View.OnKeyListener() {  
    46.             @Override  
    47.             public boolean onKey(View v, int keyCode, KeyEvent event) {  
    48.                 if (event.getAction() == KeyEvent.ACTION_DOWN) {  
    49.                     if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {  
    50.                         webView.goBack();   //后退  
    51.                         return true;    //已处理  
    52.                     }  
    53.                 }  
    54.                 return false;  
    55.             }  
    56.         });  
    57.           
    58.         webView.setWebChromeClient(new WebChromeClient() {  
    59.             //当WebView进度改变时更新窗口进度  
    60.             @Override  
    61.             public void onProgressChanged(WebView view, int newProgress) {  
    62.                 //Activity的进度范围在0到10000之间,所以这里要乘以100  
    63.                 LoadActivity.this.setProgress(newProgress * 100);  
    64.             }  
    65.         });  
    66.           
    67.         final EditText url = (EditText) findViewById(R.id.url);  
    68.           
    69.         Button loadURL = (Button) findViewById(R.id.loadURL);  
    70.         loadURL.setOnClickListener(new View.OnClickListener() {  
    71.             @Override  
    72.             public void onClick(View v) {  
    73.                 webView.loadUrl(url.getText().toString());  //加载url  
    74.                 webView.requestFocus(); //获取焦点  
    75.             }  
    76.         });  
    77.     }  
    78. }  

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

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

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

    CaptureActivity:

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

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

    [xhtml] view plaincopy
     
    1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  

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

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

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

    [java] view plaincopy
     
    1. package com.scott.webview;  
    2.   
    3. import java.io.FileOutputStream;  
    4.   
    5. import android.app.Activity;  
    6. import android.graphics.Bitmap;  
    7. import android.graphics.Canvas;  
    8. import android.graphics.Picture;  
    9. import android.os.Bundle;  
    10. import android.util.Log;  
    11. import android.view.View;  
    12. import android.webkit.WebView;  
    13. import android.widget.Button;  
    14. import android.widget.Toast;  
    15.   
    16. public class CaptureActivity extends Activity {  
    17.       
    18.     private static final String TAG = "CAPTURE";  
    19.       
    20.     private WebView webView;  
    21.       
    22.     @Override  
    23.     protected void onCreate(Bundle savedInstanceState) {  
    24.         super.onCreate(savedInstanceState);  
    25.           
    26.         setContentView(R.layout.capture);  
    27.           
    28.         webView = (WebView) findViewById(R.id.webView);  
    29.         webView.loadUrl("http://www.baidu.com");  
    30.           
    31.         Button capture = (Button) findViewById(R.id.capture);  
    32.         capture.setOnClickListener(new View.OnClickListener() {  
    33.             @Override  
    34.             public void onClick(View v) {  
    35.                 //取得android.graphics.Picture实例  
    36.                 Picture picture = webView.capturePicture();  
    37.                 int width = picture.getWidth();  
    38.                 int height = picture.getHeight();  
    39.                 if (width > 0 && height > 0) {  
    40.                     //创建指定高宽的Bitmap对象  
    41.                     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);  
    42.                     //创建Canvas,并以bitmap为绘制目标  
    43.                     Canvas canvas = new Canvas(bitmap);  
    44.                     //将WebView影像绘制在Canvas上  
    45.                     picture.draw(canvas);  
    46.                     try {  
    47.                         String fileName = "/sdcard/webview_capture.jpg";  
    48.                         FileOutputStream fos = new FileOutputStream(fileName);  
    49.                         //压缩bitmap到输出流中  
    50.                         bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);  
    51.                         fos.close();  
    52.                         Toast.makeText(CaptureActivity.this, "CAPTURE SUCCESS", Toast.LENGTH_LONG).show();  
    53.                     } catch (Exception e) {  
    54.                         Log.e(TAG, e.getMessage());  
    55.                     }  
    56.                 }  
    57.             }  
    58.         });  
    59.     }  
    60. }  

    FileActivity:

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

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

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

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

    [java] view plaincopy
     
    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代码如下:

    [java] view plaincopy
     
    1. package com.scott.webview;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.List;  
    5.   
    6. import android.app.Activity;  
    7. import android.app.AlertDialog;  
    8. import android.content.DialogInterface;  
    9. import android.os.Bundle;  
    10. import android.os.Handler;  
    11. import android.os.Message;  
    12. import android.util.Log;  
    13. import android.view.LayoutInflater;  
    14. import android.view.View;  
    15. import android.view.Window;  
    16. import android.webkit.JsPromptResult;  
    17. import android.webkit.JsResult;  
    18. import android.webkit.WebChromeClient;  
    19. import android.webkit.WebView;  
    20. import android.widget.EditText;  
    21. import android.widget.Toast;  
    22.   
    23. public class JSActivity extends Activity {  
    24.       
    25.     private static final String TAG = "JSActivity";  
    26.       
    27.     private  WebView webView;  
    28.       
    29.     private Handler handler = new Handler() {  
    30.         public void handleMessage(android.os.Message msg) {  
    31.             int index = msg.arg1;  
    32.             JSActivity.this.setProgress(index * 1000);  
    33.         };  
    34.     };  
    35.       
    36.     @Override  
    37.     public void onCreate(Bundle savedInstanceState) {  
    38.         super.onCreate(savedInstanceState);  
    39.           
    40.         getWindow().requestFeature(Window.FEATURE_PROGRESS);  
    41.           
    42.         webView = new WebView(this);  
    43.           
    44.         webView.getSettings().setJavaScriptEnabled(true);  
    45.           
    46.         webView.addJavascriptInterface(new Object() {  
    47.             @SuppressWarnings("unused")  
    48.             public List<String> getList() {  
    49.                 List<String> list = new ArrayList<String>();  
    50.                 for (int i = 0; i <= 10; i++) {  
    51.                     try {  
    52.                         Thread.sleep(200);  
    53.                     } catch (InterruptedException e) {  
    54.                         Log.e(TAG, "error:" + e.getMessage());  
    55.                     }  
    56.                     list.add("current index is: " + i);  
    57.                       
    58.                     //不能在此直接调用Activity.setProgress,否则会报以下错误  
    59.                     //Only the original thread that created a view hierarchy can touch its views.  
    60.                     Message msg = handler.obtainMessage();  
    61.                     msg.arg1 = i;  
    62.                     handler.sendMessage(msg);  
    63.                 }  
    64.                 success();  
    65.                 return list;  
    66.             }  
    67.               
    68.             public void success() {  
    69.                 //由Java代码调用JS函数  
    70.                 webView.loadUrl("javascript:success('congratulations')");  
    71.             }  
    72.         }, "bridge");  
    73.           
    74.         webView.setWebChromeClient(new WebChromeClient() {  
    75.             @Override  
    76.             public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {  
    77.                 new AlertDialog.Builder(JSActivity.this)  
    78.                         .setTitle("alert")  
    79.                         .setMessage(message)  
    80.                         .setPositiveButton("YES", new DialogInterface.OnClickListener() {  
    81.                             @Override  
    82.                             public void onClick(DialogInterface dialog, int which) {  
    83.                                 //处理结果为确定状态 同时唤醒WebCore线程  
    84.                                 result.confirm();  
    85.                             }  
    86.                         }).create().show();  
    87.                 return true;    //已处理  
    88.             }  
    89.   
    90.             @Override  
    91.             public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {  
    92.                 new AlertDialog.Builder(JSActivity.this)  
    93.                         .setTitle("confirm")  
    94.                         .setMessage(message)  
    95.                         .setPositiveButton("YES", new DialogInterface.OnClickListener() {  
    96.                             @Override  
    97.                             public void onClick(DialogInterface dialog, int which) {  
    98.                                 Toast.makeText(JSActivity.this, "you clicked yes", 0).show();  
    99.                                 result.confirm();  
    100.                             }  
    101.                         })  
    102.                         .setNegativeButton("NO", new DialogInterface.OnClickListener() {  
    103.                             @Override  
    104.                             public void onClick(DialogInterface dialog, int which) {  
    105.                                 //处理结果为取消状态 同时唤醒WebCore线程  
    106.                                 result.cancel();  
    107.                             }  
    108.                         }).create().show();  
    109.                 return true;  
    110.             }  
    111.               
    112.             @Override  
    113.             public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,  
    114.                     final JsPromptResult result) {  
    115.                 LayoutInflater inflater = getLayoutInflater();  
    116.                 View prompt = inflater.inflate(R.layout.prompt, null);  
    117.                 final EditText text = (EditText) prompt.findViewById(R.id.prompt_input);  
    118.                 text.setHint(defaultValue);  
    119.                   
    120.                 new AlertDialog.Builder(JSActivity.this)  
    121.                         .setTitle("prompt")  
    122.                         .setView(prompt)  
    123.                         .setPositiveButton("YES", new DialogInterface.OnClickListener() {  
    124.                             @Override  
    125.                             public void onClick(DialogInterface dialog, int which) {  
    126.                                 //记录结果  
    127.                                 result.confirm(text.getText().toString());  
    128.                             }  
    129.                         })  
    130.                         .setNegativeButton("NO", new DialogInterface.OnClickListener() {  
    131.                             @Override  
    132.                             public void onClick(DialogInterface dialog, int which) {  
    133.                                 result.cancel();  
    134.                             }  
    135.                         }).create().show();  
    136.                 return true;  
    137.             }  
    138.         });  
    139.   
    140.         //加载assets中的html文件  
    141.         webView.loadUrl("file:///android_asset/index.html");  
    142.           
    143.         setContentView(webView);  
    144.     }  
    145. }  

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

    [c-sharp] view plaincopy
     
    1. /** 
    2.      * Handle a confirmation response from the user. 
    3.      */  
    4.     public final void confirm() {  
    5.         mResult = true;  
    6.         wakeUp();  
    7.     }  
    8.   
    9. /** 
    10.      * Handle the result if the user cancelled the dialog. 
    11.      */  
    12.     public final void cancel() {  
    13.         mResult = false;  
    14.         wakeUp();  
    15.     }  

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

    [c-sharp] view plaincopy
     
    1. /* Wake up the WebCore thread. */  
    2.     protected final void wakeUp() {  
    3.         if (mReady) {  
    4.             synchronized (mProxy) {  
    5.                 mProxy.notify();  
    6.             }  
    7.         } else {  
    8.             mTriedToNotifyBeforeReady = true;  
    9.         }  
    10.     }  

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

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

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

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

    [xhtml] view plaincopy
     
    1. <html>  
    2.     <head>  
    3.         <script type="text/javascript">  
    4.             function doAlert() {  
    5.                 alert("hello!");  
    6.             }  
    7.   
    8.             function doConfirm() {  
    9.                 confirm("are you sure?");  
    10.             }  
    11.   
    12.             function doPrompt() {  
    13.                 var val = prompt("what's your name?");  
    14.                 if (val) {  
    15.                     alert("your name is:" + val);  
    16.                 }  
    17.             }  
    18.   
    19.             function getList() {  
    20.                 //使用java和javascript的接口bridge的方法获取集合  
    21.                 var list = window.bridge.getList();  
    22.                 var result = document.getElementById("result");  
    23.                 for (var i = 0; i list.size(); i++) {  
    24.                     var div = document.createElement("div");  
    25.                     div.innerHTML = list.get(i).toString();  
    26.                     result.appendChild(div);  
    27.                 }  
    28.             }  
    29.   
    30.             function success(msg) {  
    31.                 alert(msg);  
    32.             }  
    33.         </script>  
    34.     </head>  
    35.     <body background="black">  
    36.         <input type="button" value="alert" onclick="doAlert()"/><br/>  
    37.         <input type="button" value="confirm" onclick="doConfirm()"/><br/>  
    38.         <input type="button" value="prompt" onclick="doPrompt()"/><br/>  
    39.         <input type="button" value="getList" onclick="getList()"/><br/>  
    40.         <div id="result"></div>  
    41.     </body>  
    42. </html>  

    WebView的优点还不止这些,希望在今后的时间里,再和大家分享WebView的相关技术,也希望这篇文章能够对初识WebView的朋友们带来帮助。

  • 相关阅读:
    react的CSS中 :global的含义
    TypeScript中的问号 ? 与感叹号 ! 的含义
    移动端1px问题的解决方案
    原生js实现call,apply以及bind
    哪些场景不能使用箭头函数
    线性渐变、径向渐变以及圆锥渐变
    vue 开发中实现provide和inject实现依赖注入
    inline-block元素去除间隙
    clientWidth、offsetWidth、scrollWidth的区别
    session、token和cookie
  • 原文地址:https://www.cnblogs.com/tonglingqijie/p/4692748.html
Copyright © 2011-2022 走看看