天天看點

轉 Android WebView應用詳解

蛙撲安卓:

WebView是Android中一個非常實用的元件,它和Safai、Chrome一樣都是基于Webkit網頁渲染引擎,可以通過加載HTML資料的方式便捷地展現軟體的界面。使用WebView開發軟體有一下幾個優點:

1.可以打開遠端URL頁面,也可以加載本地HTML資料;

2.可以無縫的在java和javascript之間進行互動操作;

3.高度的定制性,可根據開發者的需要進行多樣性定制。

下面就通過例子來介紹一下WebView的使用方法。

我們先建一個webview項目,項目結構如左圖:

轉 Android WebView應用詳解
轉 Android WebView應用詳解

在這個項目中,我們會先進入MainActivity這個導航界面(上邊右圖),通過點選不同按鈕轉向不同的Activity,下面分别簡單介紹一下這幾個Activity的所要示範的功能:

LoadActivity:主要示範加載網絡頁面和WebView的一些基本設定;

CaptureActivity:主要示範WebView的截圖的功能;

FileActivity:主要示範通路本地檔案的功能;

JSActivity:主要示範WebView對JS的支援以及JS和Java之間的互相調用;

接下來,我們會逐一分析各個Activity的相關資訊:

LoadActivity:

與之對應的布局檔案為load.xml,示範效果如下:

轉 Android WebView應用詳解
轉 Android WebView應用詳解
我們在文本框中輸入URL,然後點選“load”按鈕,然後由WebView加載相應的頁面,在加載過程中,根據加載進度更新視窗的進度條。由于布局相對簡單,我們主要來看一下LoadActivity.java的代碼:
package com.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 extends Activity {
                    
    private WebView webView;
                    
    @Override
    protected void onCreate(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(new WebViewClient() {
            //當點選連結時,希望覆寫而不是打開新視窗
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);  //加載新的url
                return true;    //傳回true,代表事件已處理,事件流到此終止
            }
        });
                        
        //點選後退按鈕,讓WebView後退一頁(也可以覆寫Activity的onKeyDown方法)
        webView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
                        webView.goBack();   //後退
                        return true;    //已處理
                    }
                }
                return false;
            }
        });
                        
        webView.setWebChromeClient(new WebChromeClient() {
            //當WebView進度改變時更新視窗進度
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                //Activity的進度範圍在0到10000之間,是以這裡要乘以100
                LoadActivity.this.setProgress(newProgress * 100);
            }
        });
                        
        final EditText url = (EditText) findViewById(R.id.url);
                        
        Button loadURL = (Button) findViewById(R.id.loadURL);
        loadURL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                webView.loadUrl(url.getText().toString());  //加載url
                webView.requestFocus(); //擷取焦點
            }
        });
    }
}
    
           
可以看到,我們使用loadUrl方法加載一個url頁面,然後重寫WebChromeClient的onProgressChanged方法更新進度條。loadUrl方法除了能加載遠端頁面,還能加載本地的檔案:
//加載assets中的html檔案
webView.loadUrl("file:///android_asset/index.html");
//加載sdcard中的html檔案
webView.loadUrl("file:///mnt/sdcard/index.html");
    
           

這些都會在後邊的示例中使用到。

CaptureActivity:

與之對應的布局檔案為capture.xml,也比較簡單,它的示範效果如下:

轉 Android WebView應用詳解
轉 Android WebView應用詳解
記得在AndroidManifest.xml中加入對sdcard的寫權限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
           
截圖成功後,在/mnt/sdcard目錄下會生成一個目前網頁的截圖,如圖:
轉 Android WebView應用詳解
我們導出一下,看看是不是目前的網頁界面:
轉 Android WebView應用詳解
整個過程操作已經完成了,讓我們來看一下CaptureActivity.java的代碼:
package com.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 extends Activity {
                
    private static final String TAG = "CAPTURE";
                
    private WebView webView;
                
    @Override
    protected void onCreate(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(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //取得android.graphics.Picture執行個體
                Picture picture = webView.capturePicture();
                int width = picture.getWidth();
                int height = picture.getHeight();
                if (width > 0 && height > 0) {
                    //建立指定高寬的Bitmap對象
                    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                    //建立Canvas,并以bitmap為繪制目标
                    Canvas canvas = new Canvas(bitmap);
                    //将WebView影像繪制在Canvas上
                    picture.draw(canvas);
                    try {
                        String fileName = "/sdcard/webview_capture.jpg";
                        FileOutputStream fos = new FileOutputStream(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代碼,并應用剛才生成的網頁截圖,效果如下圖:

轉 Android WebView應用詳解
在這個過程中,我們加載了截圖,并給圖加上了紅色的邊框,CaptureActivity.java代碼如下:
package com.scott.webview;
           
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
           
public class FileActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WebView webView = new WebView(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目錄下,然後用以下方式加載也能達到相同的效果:
webView.loadUrl("file:///mnt/sdcard/index.html");
    
           
接下來是最後一個示例:JSActivity,也是最精彩的示例,示範如何在JS和Java之間通信,我們來看一下示範過程,如圖:
轉 Android WebView應用詳解
轉 Android WebView應用詳解
轉 Android WebView應用詳解
轉 Android WebView應用詳解

然後談談我們的執行過程,我們需要在Java代碼中設定WebView的一些事件的響應,比如alert、confirm以及prompt這些JS事件,讓它們按照我們的要求呈現給使用者,然後我們需要定義一個Java和JS之間的接口對象,當我們加載完一個html文檔後,根據這個對象的接口名稱就可以在文檔的JS代碼中輕松的調用這個接口對象的方法,執行Java代碼,我們也可以在Java端執行html文檔中的JS代碼。下面我們将通過代碼來證明這一過程:

JSActivity.java代碼如下:

package com.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 extends Activity {
             
    private static final String TAG = "JSActivity";
             
    private  WebView webView;
             
    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            int index = msg.arg1;
            JSActivity.this.setProgress(index * 1000);
        };
    };
             
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                 
        getWindow().requestFeature(Window.FEATURE_PROGRESS);
                 
        webView = new WebView(this);
                 
        webView.getSettings().setJavaScriptEnabled(true);
                 
        webView.addJavascriptInterface(new Object() {
            @SuppressWarnings("unused")
            public List<String> getList() {
                List<String> list = new ArrayList<String>();
                for (int i = 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();
                return list;
            }
                     
            public void success() {
                //由Java代碼調用JS函數
                webView.loadUrl("javascript:success('congratulations')");
            }
        }, "bridge");
                 
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                new AlertDialog.Builder(JSActivity.this)
                        .setTitle("alert")
                        .setMessage(message)
                        .setPositiveButton("YES", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //處理結果為确定狀态 同時喚醒WebCore線程
                                result.confirm();
                            }
                        }).create().show();
                return true;    //已處理
            }
         
            @Override
            public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
                new AlertDialog.Builder(JSActivity.this)
                        .setTitle("confirm")
                        .setMessage(message)
                        .setPositiveButton("YES", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Toast.makeText(JSActivity.this, "you clicked yes", 0).show();
                                result.confirm();
                            }
                        })
                        .setNegativeButton("NO", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //處理結果為取消狀态 同時喚醒WebCore線程
                                result.cancel();
                            }
                        }).create().show();
                return true;
            }
                     
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
                    final JsPromptResult result) {
                LayoutInflater inflater = getLayoutInflater();
                View prompt = inflater.inflate(R.layout.prompt, null);
                final EditText text = (EditText) prompt.findViewById(R.id.prompt_input);
                text.setHint(defaultValue);
                         
                new AlertDialog.Builder(JSActivity.this)
                        .setTitle("prompt")
                        .setView(prompt)
                        .setPositiveButton("YES", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //記錄結果
                                result.confirm(text.getText().toString());
                            }
                        })
                        .setNegativeButton("NO", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                result.cancel();
                            }
                        }).create().show();
                return true;
            }
        });
         
        //加載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.
     */
    public final void confirm() {
        mResult = true;
        wakeUp();
    }
        
/**
     * Handle the result if the user cancelled the dialog.
     */
    public final void cancel() {
        mResult = false;
        wakeUp();
    }
    
           
可以看到confirm和cancel方法都涉及到了一個wakeUp方法,這個方法主要作用是喚醒WebCore線程,定義如下:
/* Wake up the WebCore thread. */
    protected final void wakeUp() {
        if (mReady) {
            synchronized (mProxy) {
                mProxy.notify();
            }
        } else {
            mTriedToNotifyBeforeReady = true;
        }
    }
    
           

是以朋友們如果要重寫這幾個方法時要切記處理JsResult這個對象執行個體。

我們在處理onJsPrompt時,使用了自定義的界面,加載的是/res/layout/prompt.xml,定義如下:

<?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代碼,如下:
<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" οnclick="doAlert()"/><br/>
        <input type="button" value="confirm" οnclick="doConfirm()"/><br/>
        <input type="button" value="prompt" οnclick="doPrompt()"/><br/>
        <input type="button" value="getList" οnclick="getList()"/><br/>
        <div id="result"></div>
    </body>
</html>
    
           

轉載于:https://www.cnblogs.com/luckjun/archive/2013/03/22/2976117.html