1. 程式人生 > >談談WebView的使用-(從零開始搭建android框架系列(5))

談談WebView的使用-(從零開始搭建android框架系列(5))

本篇文章專案github地址:MVPCommon
本文章原地址:簡書部落格
專案效果

1 前言

這篇文章將從webview的基礎,介紹到專案中的真實使用。以及怎麼樣通過注入js指令碼的方式來改變網頁內容,從而在本地展示新聞體育列表。製作出一個像模像樣的app。
嚴正宣告,網頁資料來自 虎撲體育,僅作學習用途,請勿在任何商業用途中使用。

2 webview基礎

WebView是手機中內建了一款高效能 webkit 核心瀏覽器,在 SDK 中封裝的一個元件。沒有提供位址列和導航欄,WebView只是單純的展示一個網頁介面。在開發中經常都會用到。

2.1 新增webview到應用中

直接將webview新增到layout檔案中

<?xml version="1.0" encoding="utf-8"?>
<WebView  xmlns:android="http://schemas.android.com/apk/res/android"    
android:id="@+id/webview"   
 android:layout_width="fill_parent"    
android:layout_height="fill_parent"/>

使用webview載入url使用loadUrl方法:

WebView myWebView = (WebView) findViewById(R.id
.webview); myWebView.loadUrl("http://www.example.com");

AndroidManifest.xml 中必須新增訪問網路許可權。

<manifest ... >   
 <uses-permission android:name="android.permission.INTERNET" />  
  ...
</manifest>

 #####2.2 支援javascript
如果訪問的頁面中有 Javascript,則 WebView 初始化的時候必須設定支援 Javascript。(WebSetting中提供了很多有用的api,

點選連結檢視)

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

通過建立JavaScript的介面可以讓JavaScript和安卓客戶端進行互動。比如說JavaScript可以呼叫本地的安卓程式碼展示一個Dialog而不是使用JavaScript的alter()方法。通過addJavascriptInterface()方法建立介面。
示例:
定義一個“介面”類

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

在addJavascriptInterface方法中建立介面

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

在HTML的JavaScript中呼叫WebView顯示toast訊息,

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

注意:
1 addJavascriptInterface方法中要繫結的Java物件及方法執行在另外的執行緒中,而不是執行在構造他的執行緒中。
2 使用addJavascriptInterface將會使javaScript可以操控安卓原生代碼。因此有可能導致安全性問題,請確保所有HTML程式碼都是你所知道的。
3 如果targetSdkVersion是17或者更高(android 4.2以上),必須使用 @JavascriptInterface 註解,才能讓網頁訪問到原生代碼。

2.3 webview頁面處理

當用戶點選webview中的網頁連結的時候,安卓系統預設會啟動一個新的應用專門成立url的跳轉。如果希望點選連結繼續在當前webview中響應,而不是新開Android的系統browser中響應該連結,必須覆蓋 WebView的WebViewClient物件. 並重寫shouldOverrideUrlLoading方法。
“`
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient.(new WebViewClient();
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals(“www.example.com”)) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}


#####2.4 webview的頁面導航處理 
當webview的url進行跳轉的時候,自動回記錄進入歷史介面,可以使用goBack()方法和goForward()方法,進行返回和前進。
webview的canGoBack方法將會判斷歷史記錄中是否還有頁面,如果還有上一級頁面,將會返回true

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Check if the key event was the Back button and if there’s history
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
myWebView.goBack();
return true;
}
// If it wasn’t the Back key or there’s no web page history, bubble up to the default
// system behavior (probably exit the activity)
return super.onKeyDown(keyCode, event);
}


###3 webview在專案中使用
上面介紹了WebView的基礎知識,內容是我對[Building Web Apps in WebView(官網)](http://developer.android.com/guide/webapps/webview.html)
的大致翻譯內容。如果還需要了解更多,推薦4 參考連結中的深入講解Webview(上,下)。
這裡接著介紹WebView在我專案中的使用。
#####3.1 需求是什麼?
當然在我專案中,最開始想做的一個體育新聞資訊,但是苦於自己沒有伺服器,所以一開始都是模擬的資料。網上的開源api也是將所有新聞資料糅合在一起的,怎麼分開呢?比如nba,英超,歐冠,各種不同的新聞資料在哪裡獲取呢?作為一個體育愛好者,經常會去的虎撲體育,然後找到了手機版的 [虎撲體育](http://www.jianshu.com/m.hupu.com)。這不就是我想要的體育新聞資料嘛,O(∩_∩)O哈哈~。
但是我不想要這個頭部和底部呢 ,接下來分析怎麼處理,很簡單的實現。
![](http://upload-images.jianshu.io/upload_images/1833901-6509df5ac9cb479b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/1833901-baae3d25c3979653.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#####3.2 效果怎麼樣?
看看執行效果,還是有模有樣的
![](http://upload-images.jianshu.io/upload_images/1833901-7067542d7d9e7d5a.gif?imageMogr2/auto-orient/strip)

#####3.3 怎麼做?
主要就是進行了新聞網頁的展示,並且去掉了頭部和底部。

public class WebViewFragment extends AbsTitleFragment {
……

    R.layout.fragment_web_view;

……

@Override
protected void initViewsAndEvents(View rootView) {
    mWebView.setVisibility(View.INVISIBLE);
    toggleShowLoading(true, "loading");

    setWebViewOption(mWebView);
    if (getFragmentUrl() != null) {
        mWebView.loadUrl(getFragmentUrl());
    }
}

private void setWebViewOption(WebView webview) {
 ......
    //設定是否支援執行JavaScript,僅在需要時開啟
    webview.getSettings().setJavaScriptEnabled(true);

    ......
    //設定WebViewClient
    webview.setWebViewClient(new MyWebViewClient());
    webview.setWebChromeClient(new MyWebChromeClient());
}

private class MyWebViewClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {

// view.loadUrl(url);
Intent intent = new Intent(mContext, WebViewActivity.class);
intent.putExtra(WebViewActivity.WEB_VIEW_URL, url);
startActivity(intent);
return true;
}
……

    @Override
    public void onPageFinished(WebView view, String url) {

        super.onPageFinished(view, url);
        toggleShowLoading(false, "");
        if (view.getVisibility() == View.INVISIBLE) {
            view.setVisibility(View.VISIBLE);
        }
    }

……
}

private class MyWebChromeClient extends WebChromeClient {
......
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        if (newProgress > 25) {
            injectJS(view);
        }

        super.onProgressChanged(view, newProgress);
    }
}


private void injectJS(WebView webview) {
    webview.loadUrl("javascript:(function() " +
            "{ " +
            "document.getElementsByClassName('m-top-bar')[0].style.display='none'; " +
            "document.getElementsByClassName('m-footer')[0].style.display='none';" +
            "document.getElementsByClassName('m-page')[0].style.display='none';" +
            "})()");
}

}
“`
可以看到WebView的初始化在setWebViewOption方法中進行。其中需要關注的有下面三行程式碼

上面在WebSetting方法中WebViewClient的初始化,這裡主要是點選新聞進行頁面跳轉的處理。
提供WebViewClient並重寫shouldOverrideUrlLoading方法,並返回true
webview的返回鍵必須做處理,如果當前webview支援返回(不是點進來的第一個webview,而是再次跳轉的),則返回。如果不支援返回(第一個webview介面),則finish當前的頁面。

初始化WebView的時候也進行了WebChromeClient的初始化。其中主要是在當進度超過百分之25的時候注入了一段javaScript程式碼。通過javascript程式碼去掉了當前網頁的頭部和底部。

3.4 為什麼這麼做?

上面就是通過對網頁資訊注入js的形式,隱藏了相應的屬性。
如圖,去掉標題欄,在網頁中通過F12開啟開發者工具,發現頂部欄的class的名字是m-top-bar,所以通過注入指令碼的時候直接呼叫了這個class並且將display置為none。對這方面不瞭解的可以學習一下Html5相關的知識。

至於為什麼是在進度條進行25之後進行注入,這裡主要是在介面載入完成後再進行注入js就會有一定的卡頓閃屏現象。可以自己實踐一下。
本篇文章專案github地址:MVPCommon

4 參考連結