去年有一段時(shí)間工作不忙,閑暇之余就自己做了一個(gè)閱讀類的應(yīng)用——《知豆了》,這個(gè)應(yīng)用很簡單,就是通過fiddler抓取豆瓣一刻,知乎日報(bào),一個(gè),每日一文和國家地理等應(yīng)用的網(wǎng)絡(luò)APi,并分析他們的數(shù)據(jù),然后將數(shù)據(jù)應(yīng)用到自己的應(yīng)用中去。
應(yīng)用截圖:

(文章列表)

(文章詳情)

(圖片瀏覽)
做什么?
其實(shí)要做的很簡單,我們可以看到圖1是文章列表,這里的數(shù)據(jù)是豆瓣一刻的Api返回的Json數(shù)據(jù),通過解析該數(shù)據(jù)就可以得到一個(gè)圖文混排的列表數(shù)據(jù)內(nèi)容,之后再利用ListView的Adapter的getItemViewType方法實(shí)現(xiàn)三種不同的item混合調(diào)用即可。
之后,圖2是點(diǎn)擊進(jìn)某個(gè)文章后看到的數(shù)據(jù)內(nèi)容,這部分內(nèi)容是Html數(shù)據(jù),這個(gè)數(shù)據(jù)有Html代碼也有css代碼,利用的就是WebView來加載網(wǎng)頁的方式達(dá)到快速的實(shí)現(xiàn)圖文混排的目的,用原生的Android組件是沒辦法很快速很完美的實(shí)現(xiàn)這個(gè)效果的,所以這是最快的解決方案,但是這個(gè)方案就帶來了一個(gè)問題,那就是如果我想收藏或者單獨(dú)瀏覽這個(gè)網(wǎng)頁中的某張圖片便會很困難,因?yàn)闊o法快速的將圖片數(shù)據(jù)加載到原生的Android組件中去,于是便要想辦法,讓Android原生與Html進(jìn)行交互,從而達(dá)到我們的目的。
其實(shí)說到底,我們只要在用戶點(diǎn)擊圖片的時(shí)候獲得這個(gè)圖片的Url然后將Url告訴我們的安卓圖片組件就可以了,前一步有過web開發(fā)經(jīng)驗(yàn)的朋友應(yīng)該都很容易想到,只要利用JavaScript的onClick監(jiān)聽就可以輕松實(shí)現(xiàn)獲取圖片Url的效果,但是如何將獲得Url告訴Android組件,則需要用到Android與JavaScript交互的相關(guān)知識了。
怎么做?
因?yàn)橛玫搅薟ebView,那么話不多說了,直接要在Activity中初始化WebView組件才是正事:
mWebView=(WebView) this.findViewById(R.id.news_detail_webView);
之后再對WebView進(jìn)行初始化設(shè)置:
mWebView.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setRenderPriority(RenderPriority.HIGH);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //設(shè)置 緩存模式
// 開啟 DOM storage API 功能
mWebView.getSettings().setDomStorageEnabled(true);
//開啟 database storage API 功能
mWebView.getSettings().setDatabaseEnabled(true);
//開啟 Application Caches 功能
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setBlockNetworkImage(false);
mWebView.getSettings().setLoadsImagesAutomatically(true); //自動(dòng)加載圖片
mWebView.getSettings().setPluginState(PluginState.OFF);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
設(shè)置的具體內(nèi)容看代碼都能看懂,注意一定要加上setJavaScriptEnabled(true);這個(gè)設(shè)置,不然的話JavaScript在WebView中都不起作用,還談什么交互呢?
然后在Oncreate方法中繼續(xù)加入如下兩行代碼:
mWebView.addJavascriptInterface(new JavascriptInterface(this), "imagelistner");
mWebView.setWebViewClient(new MyWebViewClient());
注意了,這兩行代碼就是Android與JavaScript交互的關(guān)鍵。
我們先看第一行代碼:
mWebView.addJavascriptInterface(new JavascriptInterface(this), "imagelistner");
這行代碼的意思就是,給WebView組件加上javaScript的接口,至于接口的內(nèi)容是什么,接口叫什么名字,方法中的兩個(gè)參數(shù)就是了。
先看第一個(gè)參數(shù),看到可以new就應(yīng)該知道,這個(gè)應(yīng)該是一個(gè)類了,你可以把這個(gè)類寫成一個(gè)內(nèi)部類,也可以把它提取成一個(gè)獨(dú)立的類,現(xiàn)在來看看這個(gè)類的內(nèi)容:
// js通信接口
public class JavascriptInterface {
private Context context;
public JavascriptInterface(Context context) {
this.context = context;
}
public void openImage(String img) {
//
Intent intent = new Intent();
intent.putExtra("image", img);
intent.setClass(context, ShowWebImageActivity.class);
context.startActivity(intent);
}
}
這段代碼就是用來實(shí)現(xiàn)程序在點(diǎn)擊后應(yīng)該執(zhí)行什么操作的,看代碼可以知道,我是將得到的圖片路徑當(dāng)作參數(shù)傳遞給ShowWebImageActivity的,這個(gè)Activity負(fù)責(zé)的就是根據(jù)圖片的Url地址加載圖片。
你可能著急了,這里的代碼也只是交互完畢后的工作內(nèi)容啊,具體怎么交互啊?
別急,我們來看剛才OnCreate方法的最后一行代碼:
mWebView.setWebViewClient(new MyWebViewClient());
這個(gè)方法是用來自定義WebView加載過程中的操作的,具體看 MyWebViewClient 這個(gè)類:
// 監(jiān)聽
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
@Override
public void onPageFinished(WebView view, String url) {
view.getSettings().setJavaScriptEnabled(true);
super.onPageFinished(view, url);
// html加載完成之后,添加監(jiān)聽圖片的點(diǎn)擊js函數(shù)
addImageClickListner();
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
view.getSettings().setJavaScriptEnabled(true);
super.onPageStarted(view, url, favicon);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
}
}
可以看到,大部分都是調(diào)用的父類的方法,只是在OnPageFinished這個(gè)方法里加入了一個(gè)自己的方法,addImageClickListener()方法,這里其實(shí)就是交互的關(guān)鍵了,我們繼續(xù)看這個(gè)方法:
// 注入js函數(shù)監(jiān)聽
private void addImageClickListner() {
// 這段js函數(shù)的功能就是,遍歷所有的img幾點(diǎn),并添加onclick函數(shù),
//函數(shù)的功能是在圖片點(diǎn)擊的時(shí)候調(diào)用本地java接口并傳遞url過去
mWebView.loadUrl("javascript:(function(){" +
"var objs = document.getElementsByTagName(\"img\"); " +
"for(var i=0;i<objs.length;i++) " +
"{"
+ " objs[i].onclick=function() " +
" { "
+ " window.imagelistner.openImage(this.src); " +
" } " +
"}" +
"})()");
}
好了,這個(gè)就是注入的關(guān)鍵了,首先利用WebView的方法loadUrl("javascript:xxx")來實(shí)現(xiàn)注入,之后就可以看注入的方法內(nèi)容了,學(xué)過JS的應(yīng)該很容易理解,其實(shí)就是遍歷當(dāng)前的Html,然后將Html中的Img組件全部獲取到后,再給每個(gè)IMG組件加入onClick事件,該事件的方法體就是:
window.imagelistner.openImage(this.src);
注意看這個(gè)imagelistenr其實(shí)就是 mWebView.addJavascriptInterface(new JavascriptInterface(this), "imagelistner"); 定義的方法名,而openImage就是我們自定義的JavaScriptInterface中的openImage方法。
至此,Android與JavaScript的交互就已經(jīng)完畢了,點(diǎn)擊WebView中的圖片就可以調(diào)用我們原生的Android組件來加載了,想縮放,想下載就隨你啦!