前言
不知道大家有沒(méi)有遇到過(guò)這種需求止吁,用WebView加載某個(gè)網(wǎng)頁(yè)佳窑,之后點(diǎn)擊網(wǎng)頁(yè)上面的鏈接淋硝,跳轉(zhuǎn)到另一個(gè)頁(yè)面或者打開(kāi)瀏覽器雹熬。特別是當(dāng)WebView只是某個(gè)頁(yè)面的一部分的時(shí)候,這種需求也是很合理的奖地。
遇到問(wèn)題
上面的需求很容易實(shí)現(xiàn):
- 用WebView加載某個(gè)網(wǎng)頁(yè)
webView.loadUrl(url);
- 點(diǎn)擊WebView的某個(gè)鏈接跳轉(zhuǎn)下一個(gè)頁(yè)面
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
view.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
return true;
}
});
但是這樣做了之后會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題橄唬,如果webView第一次加載的url重定向到了另一個(gè)地址,此時(shí)也會(huì)走shouldOverrideUrlLoading的回調(diào)参歹。這樣一來(lái)仰楚,出現(xiàn)的現(xiàn)象就是WebView是空的,直接打開(kāi)了瀏覽器犬庇。
解決問(wèn)題
要解決這個(gè)問(wèn)題僧界,很容易想到的解決方案是找找WebView有沒(méi)有對(duì)重定向的判斷方法,如果有的話臭挽,我們就可以對(duì)重定向的回調(diào)另外處理捂襟。
很不幸,WebView并沒(méi)有提供相應(yīng)的方法欢峰。是不是就沒(méi)辦法處理了呢葬荷?當(dāng)然不是。
解決方案一
這個(gè)方案是一個(gè)CSDN一個(gè)博主提供的:
WebView有一個(gè)getHitTestResult():返回的是一個(gè)HitTestResult纽帖,一般會(huì)根據(jù)打開(kāi)的鏈接的類(lèi)型宠漩,返回一個(gè)extra的信息,如果打開(kāi)鏈接不是一個(gè)url懊直,或者打開(kāi)的鏈接是JavaScript的url扒吁,他的類(lèi)型是UNKNOWN_TYPE,這個(gè)url就會(huì)通過(guò)requestFocusNodeHref(Message)異步重定向室囊。返回的extra為null雕崩,或者沒(méi)有返回extra。根據(jù)此方法的返回值融撞,判斷是否為null盼铁,可以用于解決網(wǎng)頁(yè)重定向。
詳見(jiàn)該博主原文懦铺,整理成代碼就是:
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//判斷重定向的方式一
WebView.HitTestResult hitTestResult = view.getHitTestResult();
if(hitTestResult == null) {
return false;
}
if(hitTestResult.getType() == WebView.HitTestResult.UNKNOWN_TYPE) {
return false;
}
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
view.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
return true;
}
});
該方案有個(gè)缺陷是捉貌,如果遇到的需求是前言描述的那樣,二正好點(diǎn)擊的鏈接發(fā)生了重定向,就不會(huì)在另一個(gè)頁(yè)面打開(kāi)趁窃,而是直接在當(dāng)前的WebView里了牧挣。
為了避免該缺陷,產(chǎn)生了方案二醒陆。
解決方案二
WebView在加載一個(gè)頁(yè)面開(kāi)始的時(shí)候會(huì)回調(diào)onPageStarted方法瀑构,在該頁(yè)面加載完成之后會(huì)回調(diào)onPageFinished方法。而如果該鏈接發(fā)生了重定向刨摩,回調(diào)shouldOverrideUrlLoading會(huì)在回調(diào)onPageFinished之前寺晌。
有了這個(gè)前提,我們就可以加一個(gè)mIsPageLoading的標(biāo)記澡刹,在onPageStarted回調(diào)的時(shí)候置為true呻征,在onPageFinished回調(diào)的時(shí)候置為false。在shouldOverrideUrlLoading里面就可以判斷該標(biāo)記罢浇,如果為true陆赋,則表示該回調(diào)是重定向,否則直接打開(kāi)瀏覽器嚷闭。代碼如下:
private boolean mIsPageLoading;
//代碼省略
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//判斷重定向的方式二
if(mIsPageLoading) {
return false;
}
if(url != null && url.startsWith("http")) {
webView.loadUrl(url);
return true;
} else {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
view.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
return true;
}
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
mIsPageLoading= true;
Log.d(TAG, "onPageStarted");
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mIsPageLoading= false;
Log.d(TAG, "onPageFinished");
}
});
沒(méi)有十全十美的方案攒岛,該方案也會(huì)產(chǎn)生另一個(gè)問(wèn)題:當(dāng)頁(yè)面沒(méi)有全部加載完之前,加載出來(lái)的部分頁(yè)面的鏈接也是可以點(diǎn)擊的胞锰。這樣一來(lái)在shouldOverrideUrlLoading里面本來(lái)是對(duì)鏈接的點(diǎn)擊也會(huì)被當(dāng)成重定向鏈接在當(dāng)前的WebView里面打開(kāi)灾锯。
要避免這種情況,只能在頁(yè)面完全加載出來(lái)之前禁止WebView的點(diǎn)擊嗅榕。
webView.setOnTouchListener(new WebViewTouchListener());
//代碼省略
private class WebViewTouchListener implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
return !mIsLoading;
}
}
結(jié)語(yǔ)
上面的兩種方案都能解決部分問(wèn)題顺饮,但是都有缺陷。只能說(shuō)沒(méi)有完美的方案凌那,只有符合需求的方案拒秘。
江湖規(guī)矩您市,源碼見(jiàn) github