關於webview的一切

1.基本設置

mWebView.getSettings().setJavaScriptEnabled(false);
表示不支持js,如果想讓java和js交互或者本身希望js完成一定的功能請把false改爲true。
mWebView.getSettings().setSupportZoom(false);

設置是否支持縮放,我這裏爲false,默認爲true。

mWebView.getSettings().setBuiltInZoomControls(false);

設置是否顯示縮放工具,默認爲false。

mWebView.getSettings().setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);

一般很少會用到這個,用WebView組件顯示普通網頁時一般會出現橫向滾動條,這樣會導致頁面查看起來非常不方便。LayoutAlgorithm是一個枚舉,用來控制html的佈局,總共有三種類型:
NORMAL:正常顯示,沒有渲染變化。
SINGLE_COLUMN:把所有內容放到WebView組件等寬的一列中。
NARROW_COLUMNS:可能的話,使所有列的寬度不超過屏幕寬度。


mWebView.getSettings().setDefaultFontSize(18);

設置默認的字體大小,默認爲16,有效值區間在1-72之間。

2.加載內容

(1)加載assets目錄下的本地網頁
一般我們都是把html文件放在assets目錄下, WebView調用assets目錄下的本地網頁和圖片等資源非常方便,使用形如

mWebView.loadUrl("file:///android_asset/html/test1.html");
的調用方法即可。

(2)加載遠程網頁

mWebView.loadUrl("http://www.google.com");
(3)使用 LoadData 或者 loadDataWithBaseURL方法加載內容
有時候我們的webview可能只是html片段,而不是一個完整的網頁,事實上絕大多數時候都是如此,完整的網頁無需做成應用,而直接在瀏覽器訪問。

這種情況我們使用 LoadData 或者 loadDataWithBaseURL方法,後者用的最多:

void loadDataWithBaseURL (String baseUrl, String data, String mimeType, String encoding, String historyUrl)
loadDataWithBaseURL()比loadData()多兩個參數,可以指定HTML代碼片段中相關資源的相對根路徑,也可以指定歷史Url,其餘三個參數相同。
這裏主要注意參數baseUrl,baseUrl指定了你的data參數中數據是以什麼地址爲基準的,因爲data中的數據可能會有超鏈接或者是image元素,而很多網站的地址都是用的相對路徑,如果沒有baseUrl,webview將訪問不到這些資源。
舉個例子:
String body ="示例:這裏有個img標籤,地址是相對路徑<img src='/uploads/allimg/130923/1FP02V7-0.png' />";
mWebView.loadDataWithBaseURL("http://www.jcodecraeer.com", body, "text/html", "utf-8",null);
如果baseUrl沒有指定爲http://www.jcodecraeer.com,那麼這張圖片將顯示不出來。
上面的例子其實演示了loadDataWithBaseURL的用法,我們直接加載一個字符串裏面的html內容,而有些時候這些內容是從assets目錄下的本地網頁文件中讀取,下面我們將html/test1.html中的內容通過LoadData來加載:
String data = "";
try {
    // 讀取assets目錄下的文件需要用到AssetManager對象的Open方法打開文件
    InputStream is = getAssets().open("html/test2.html");
    // loadData()方法需要的是一個字符串數據所以我們需要把文件轉成字符串
    ByteArrayBuffer baf = new ByteArrayBuffer(500);
    int count = 0;
    while ((count = is.read()) != -1) {
        baf.append(count);
    }
    data = EncodingUtils.getString(baf.toByteArray(), "utf-8");
} catch (IOException e) {
    e.printStackTrace();
}
// 下面兩種方法都可以加載成功
mWebView.loadData(data, "text/html", "utf-8");
// wv.loadDataWithBaseURL("", data, "text/html", "utf-8", "");

這種通過讀取文件再用loadData加載其實和mWebView.loadUrl("file:///android_asset/html/test1.html")是一致的,只不過loadData方式因爲沒有指定地址的基準url,html/test1.html文件中一些資源文件或者鏈接地址會失效。
loadDataWithBaseURL和loadData兩個方法加載的HTML代碼片段的不同點在於,loadData()中的html data中不能包含'#', '%', '\', '?'四中特殊字符,在平時測試時,你的數據時,你的數據裏含有這些字符,但不會出問題,當出問題時,你可以替換下。
      %,會報找不到頁面錯誤,頁面全是亂碼。亂碼樣式見符件。
      #,會讓你的goBack失效,但canGoBAck是可以使用的。於是就會產生返回按鈕生效,但不能返回的情況。

3.WebView內容的處理

android 中webView控件 padding不起作用
在一個佈局文件中有一個WebView,想使用padding屬性讓左右向內留出一些空白,
但是padding屬性不起左右,內容照樣貼邊顯示,反而移動了右邊滾動條的位置。
android的bug,用一個外圍的layout包含webview,可以有所改進,但不能完全解決。
其實正確的做法是在webView的加載的css中增加padding,沒必要爲了padding而更改xml佈局文件。   


4.重寫shouldOverrideUrlLoading時指定url

指定只有url裏包含eoe.cn的時候纔在webview裏打開,否則還是啓動瀏覽器打開.

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    LogUtil.i(this, "url="   url);
    if ( url.contains("eoe.cn") == true){
        view.loadUrl(url);
        return true;
    }else{
        Intent in = new Intent (Intent.ACTION_VIEW , Uri.parse(url));
        startActivity(in);
        return true;
    }
}


5.android:scrollbarStyle控制滾動條位置

WebView有一個設置滾動條位置的屬性:android:scrollbarStyle 可以是insideOverlay可以是outsideOverlay,兩個的區別是SCROLLBARS_INSIDE_OVERLAY的樣式是滾動條在整個page裏,類似css中的padding,看代碼下的這個圖吧,很清晰.

mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
6.爲WebView自定義錯誤顯示界面:  

覆寫WebViewClient中的onReceivedError()方法:
 /**
	 * 顯示自定義錯誤提示頁面,用一個View覆蓋在WebView
	 */
	protected void showErrorPage() {
		LinearLayout webParentView = (LinearLayout)mWebView.getParent();
		
		initErrorPage();
		while (webParentView.getChildCount() > 1) {
			webParentView.removeViewAt(0);
		}
		LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
		webParentView.addView(mErrorView, 0, lp);
		mIsErrorPage = true;
	}
	protected void hideErrorPage() {
		LinearLayout webParentView = (LinearLayout)mWebView.getParent();
		
		mIsErrorPage = false;
		while (webParentView.getChildCount() > 1) {
			webParentView.removeViewAt(0);
		}
	}


    protected void initErrorPage() {
		if (mErrorView == null) {
			mErrorView = View.inflate(this, R.layout.online_error, null);
			Button button = (Button)mErrorView.findViewById(R.id.online_error_btn_retry);
			button.setOnClickListener(new OnClickListener() {
				public void onClick(View v) {
					mWebView.reload();
				}
			});
			mErrorView.setOnClickListener(null);
		}
	}


7.WebView cookies清理:

CookieSyncManager.createInstance(this);   
CookieSyncManager.getInstance().startSync();   
CookieManager.getInstance().removeSessionCookie(); 


8.清理cache 和歷史記錄:

webView.clearCache(true);   
webView.clearHistory();  

9.判斷WebView是否已經滾動到頁面底端:
getScrollY()方法返回的是當前可見區域的頂端距整個頁面頂端的距離,也就是當前內容滾動的距離.   
getHeight()或者getBottom()方法都返回當前WebView 這個容器的高度   
getContentHeight 返回的是整個html 的高度,但並不等同於當前整個頁面的高度,因爲WebView 有縮放功能, 所以當前整個頁面的高度實際上應該是原始html 的高度再乘上縮放比例. 因此,更正後的結果,準確的判斷方法應該是:   
if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){ //已經處於底端 }   


10.URL攔截:
Android WebView是攔截不到頁面內的fragment跳轉的。但是url跳轉的話,又會引起頁面刷新,H5頁面的體驗又下降了。只能給WebView注入JS方法了。




 11.處理WebView中的非超鏈接請求(如Ajax請求): 

 有時候需要加上請求頭,但是非超鏈接的請求,沒有辦法再shouldOverrinding中攔截並用webView.loadUrl(String url,HashMap headers)方法添加請求頭
  目前用了一個臨時的辦法解決:
首先需要在url中加特殊標記/協議, 如在onWebViewResource方法中攔截對應的請求,然後將要添加的請求頭,以get形式拼接到url末尾
在shouldInterceptRequest()方法中,可以攔截到所有的網頁中資源請求,比如加載JS,圖片以及Ajax請求等等
Ex:

@SuppressLint("NewApi")  
@Override  
public WebResourceResponse shouldInterceptRequest(WebView view,String url) {  
    // 非超鏈接(如Ajax)請求無法直接添加請求頭,現拼接到url末尾,這裏拼接一個imei作爲示例  
  
    String ajaxUrl = url;  
    // 如標識:req=ajax  
    if (url.contains("req=ajax")) {  
       ajaxUrl += "&imei=" + imei;  
    }  
  
    return super.shouldInterceptRequest(view, ajaxUrl);  
  
}  

12.在頁面中先顯示圖片:

@Override  
public void onLoadResource(WebView view, String url) {  
  mEventListener.onWebViewEvent(CustomWebView.this, OnWebViewEventListener.EVENT_ON_LOAD_RESOURCE, url);  
    if (url.indexOf(".jpg") > 0) {  
     hideProgress(); //請求圖片時即顯示頁面  
     mEventListener.onWebViewEvent(CustomWebView.this, OnWebViewEventListener.EVENT_ON_HIDE_PROGRESS, view.getUrl());  
     }  
    super.onLoadResource(view, url);  
} 

13.屏蔽掉長按事件 因爲webview長按時將會調用系統的複製控件:

mWebView.setOnLongClickListener(new OnLongClickListener() {  
            
          @Override  
          public boolean onLongClick(View v) {  
              return true;  
          }  
      });  
14.在WebView加入 flash支持:

String temp = "<html><body bgcolor=\"" + "black"  
                + "\"> <br/><embed src=\"" + url + "\" width=\"" + "100%"  
                + "\" height=\"" + "90%" + "\" scale=\"" + "noscale"  
                + "\" type=\"" + "application/x-shockwave-flash"  
                + "\"> </embed></body></html>";  
String mimeType = "text/html";  
String encoding = "utf-8";  
web.loadDataWithBaseURL("null", temp, mimeType, encoding, "");  

15WebView保留縮放功能但隱藏縮放控件:

mWebView.getSettings().setSupportZoom(true);  
        mWebView.getSettings().setBuiltInZoomControls(true);  
        if (DeviceUtils.hasHoneycomb())  
              mWebView.getSettings().setDisplayZoomControls(false);  
注意:setDisplayZoomControls是在Android 3.0中新增的API.

16.WebView 在Android4.4的手機上onPageFinished()回調會多調用一次(具體原因待追查)

需要儘量避免在onPageFinished()中做業務操作,否則會導致重複調用,還有可能會引起邏輯上的錯誤

17.需要通過獲取Web頁中的title用來設置自己界面中的title及相關問題:
需要給WebView設置 WebChromeClient,並在onReceiveTitle()回調中獲取

WebChromeClient webChromeClient = new WebChromeClient() {    
            @Override    
            public void onReceivedTitle(WebView view, String title) {    
                super.onReceivedTitle(view, title);    
                 
                txtTitle.setText(title);    
            }    
    
        };    

但是發現在小米3的手機上,當通過webview.goBack()回退的時候,並沒有觸發onReceiveTitle(),這樣會導致標題仍然是之前子頁面的標題,沒有切換回來.
這裏可以分兩種情況去處理:
(1) 可以確定webview中子頁面只有二級頁面,沒有更深的層次,這裏只需要判斷當前頁面是否爲初始的主頁面,可以goBack的話,只要將標題設置回來即可.
(2)webview中可能有多級頁面或者以後可能增加多級頁面,這種情況處理起來要複雜一些:
    因爲正常順序加載的情況onReceiveTitle是一定會觸發的,所以就需要自己來維護webview  loading的一個url棧及url與title的映射關係
   那麼就需要一個ArrayList來保持加載過的url,一個HashMap保存url及對應的title.
   正常順序加載時,將url和對應的title保存起來,webview回退時,移除當前url並取出將要回退到的web 頁的url,找到對應的title進行設置即可.

這裏還要說一點,當加載出錯的時候,比如無網絡,這時onReceiveTitle中獲取的標題爲 找不到該網頁,因此建議當觸發onReceiveError時,不要使用獲取到的title.

18.WebView因addJavaScriptInterface()引起的安全問題.
    這個問題主要是因爲會有惡意的js代碼注入,尤其是在已經獲取root權限的手機上,一些惡意程序可能會利用該漏洞安裝或者卸載應用.
關於詳細的情況可以參考下面這篇文章:
.http://blog.csdn.net/leehong2005/article/details/11808557
  
還有一個開源項目可以參考: https://github.com/pedant/safe-java-js-webview-bridge, 該項目利用onJsPrompt() 替代了addJavaScriptInterface(),(解決方案類似上述參考的博客)同時增加了異步回調,
很好地解決了webview  js注入的安全問題.

19.WebView頁面中播放了音頻,退出Activity後音頻仍然在播放
需要在Activity的onDestory()中調用
webView.destroy();  

但是直接調用可能會引起如下錯誤:
10-10 15:01:11.402: E/ViewRootImpl(7502): sendUserActionEvent() mView == null  
10-10 15:01:26.818: E/webview(7502): java.lang.Throwable: Error: WebView.destroy() called while still attached!  
10-10 15:01:26.818: E/webview(7502):    at android.webkit.WebViewClassic.destroy(WebViewClassic.java:4142)  
10-10 15:01:26.818: E/webview(7502):    at android.webkit.WebView.destroy(WebView.java:707)  
10-10 15:01:26.818: E/webview(7502):    at com.didi.taxi.ui.webview.OperatingWebViewActivity.onDestroy(OperatingWebViewActivity.java:236)  
10-10 15:01:26.818: E/webview(7502):    at android.app.Activity.performDestroy(Activity.java:5543)  
10-10 15:01:26.818: E/webview(7502):    at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1134)  
10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3619)  
10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3654)  
10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.access$1300(ActivityThread.java:159)  
10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)  
10-10 15:01:26.818: E/webview(7502):    at android.os.Handler.dispatchMessage(Handler.java:99)  
10-10 15:01:26.818: E/webview(7502):    at android.os.Looper.loop(Looper.java:137)  
10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.main(ActivityThread.java:5419)  
10-10 15:01:26.818: E/webview(7502):    at java.lang.reflect.Method.invokeNative(Native Method)  
10-10 15:01:26.818: E/webview(7502):    at java.lang.reflect.Method.invoke(Method.java:525)  
10-10 15:01:26.818: E/webview(7502):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)  
10-10 15:01:26.818: E/webview(7502):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)  
10-10 15:01:26.818: E/webview(7502):    at dalvik.system.NativeStart.main(Native Method)  


如上所示,webview調用destory時,webview仍綁定在Activity上.這是由於自定義webview構建時傳入了該Activity的context對象,因此需要先從父容器中移除webview,然後再銷燬webview:
rootLayout.removeView(webView);  
webView.destroy();  

20.WebView長按自定義菜單,實現複製分享相關功能
 這個功能首先可以從兩方面完成:

 (1) 在js中完成:
    處理android.selection.longTouch
(2) 安卓層處理:
     首先使用OnTouchListener實現長按實現監聽,然後實現WebView的Context menu,最後調用webview中的emulateShiftHeld(),爲了適配安卓不同版本,最好使用反射方式調用. 

發佈了51 篇原創文章 · 獲贊 8 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章