
image.png
內(nèi)容:介紹webview的使用方法,介紹WebViewClient、WebChromeClient,H5網(wǎng)頁(yè)視頻全屏播放,網(wǎng)頁(yè)跳轉(zhuǎn)空白問(wèn)題
最近做項(xiàng)目老愛(ài)和H5打交道,遇到了很多問(wèn)題也踩了許多坑,今天在這兒總結(jié)下,方便后人乘涼。
關(guān)于安卓和H5交互可參考我之前的文章:原生與H5交互介紹
WebView基礎(chǔ)設(shè)置
private void initWebView() {
mWebView.setWebViewClient(new MyWebViewClient()); //設(shè)置在WebView中打開鏈接,不設(shè)置則調(diào)用自帶瀏覽器。主要針對(duì)View進(jìn)行攔截處理
mWebView.setWebChromeClient(new MyWebChromeClient());
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true); //支持JS
webSettings.setDomStorageEnabled(true); //啟用dom內(nèi)存,防止js加載失敗
webSettings.setAllowFileAccess(true); //允許訪問(wèn)文件
webSettings.setSupportZoom(true); //支持縮放
webSettings.setLoadWithOverviewMode(true); //是否啟動(dòng)概述模式瀏覽界面,當(dāng)頁(yè)面寬度超過(guò)WebView顯示寬度時(shí),縮小頁(yè)面適應(yīng)WebView。默認(rèn)false
webSettings.setGeolocationEnabled(false); //是否允許定位
webSettings.setLoadsImagesAutomatically(true); //是否加載圖片
// webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); //設(shè)置緩存模式
// webSettings.setDefaultTextEncodingName("UTF-8"); //設(shè)置頁(yè)面的編碼格式,默認(rèn)UTF-8
}
WebViewClient主要是對(duì)view一系列操作進(jìn)行監(jiān)聽攔截。包括:網(wǎng)頁(yè)加載開始、網(wǎng)頁(yè)加載完成、錯(cuò)誤攔截處理。(代碼中注釋會(huì)詳解,再次不多做介紹)
WebChromeClient主要是對(duì)瀏覽器進(jìn)行監(jiān)聽。例如彈窗、是否顯示支持全屏播放等。(代碼中注釋會(huì)詳解,再次不多做介紹)
網(wǎng)頁(yè)加載空白問(wèn)題,我只在7.0及以上遇到,貌似是說(shuō)證書錯(cuò)誤,可能越往上安全性越高吧,按照下面處理下就行了
/**
* HTTPS通信的網(wǎng)址(以https://開頭的網(wǎng)站)出現(xiàn)錯(cuò)誤時(shí)
* 證書錯(cuò)誤攔截處理
* 安卓7.0需要
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if(error.getPrimaryError() == android.net.http.SslError.SSL_INVALID ){// 校驗(yàn)過(guò)程遇到了bug
handler.proceed(); //忽略錯(cuò)誤繼續(xù)加載
}else{
handler.cancel(); //取消加載
}
}
視頻全屏播放:安卓不像IOS一樣可以直接全屏播放,需要在WebChromeClient對(duì)其進(jìn)行設(shè)置,相當(dāng)于是new 一個(gè)Fragment讓其來(lái)進(jìn)行全屏播放
/*** 視頻播放相關(guān)的方法 **/
@Override
public View getVideoLoadingProgressView() {
FrameLayout frameLayout = new FrameLayout(MainActivity.this);
frameLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return frameLayout;
}
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
showCustomView(view, callback);
}
@Override
public void onHideCustomView() {
hideCustomView();
}

image.png
代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_load"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="LOAD"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</WebView>
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
</RelativeLayout>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn_load)
Button mBtnLoad;
@BindView(R.id.web_view)
WebView mWebView;
@BindView(R.id.pb_loading)
ProgressBar mPbLoading;
/** 視頻全屏參數(shù) */
protected static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
private View customView;
private FrameLayout fullscreenContainer;
private WebChromeClient.CustomViewCallback customViewCallback;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initWebView();
}
private void initWebView() {
mWebView.setWebViewClient(new MyWebViewClient()); //設(shè)置在WebView中打開鏈接,不設(shè)置則調(diào)用自帶瀏覽器。主要針對(duì)View進(jìn)行攔截處理
mWebView.setWebChromeClient(new MyWebChromeClient());
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true); //支持JS
webSettings.setDomStorageEnabled(true); //啟用dom內(nèi)存,防止js加載失敗
webSettings.setAllowFileAccess(true); //允許訪問(wèn)文件
webSettings.setSupportZoom(true); //支持縮放
webSettings.setLoadWithOverviewMode(true); //是否啟動(dòng)概述模式瀏覽界面,當(dāng)頁(yè)面寬度超過(guò)WebView顯示寬度時(shí),縮小頁(yè)面適應(yīng)WebView。默認(rèn)false
webSettings.setGeolocationEnabled(false); //是否允許定位
webSettings.setLoadsImagesAutomatically(true); //是否加載圖片
// webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); //設(shè)置緩存模式
// webSettings.setDefaultTextEncodingName("UTF-8"); //設(shè)置頁(yè)面的編碼格式,默認(rèn)UTF-8
}
@OnClick(R.id.btn_load)
public void onViewClicked() {
mWebView.loadUrl("http://www.baidu.com");
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
//優(yōu)先退出全屏播放
if (customView != null) {
hideCustomView();
return true;
} else {
if (mWebView.canGoBack()) {
mWebView.goBack(); //返回上個(gè)頁(yè)面
return true;
} else {
System.exit(0); //退出程序
}
}
}
return super.onKeyDown(keyCode, event);
}
/**
* 針對(duì)網(wǎng)頁(yè)進(jìn)行攔截處理
*/
public class MyWebViewClient extends WebViewClient{
/**
*可以實(shí)現(xiàn)對(duì)網(wǎng)頁(yè)中超鏈接的攔截
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//例:攔截電話網(wǎng)址,直接調(diào)用本地電話
if (url.contains("tel:")){
startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
return true;
}
if (url.startsWith("http:") || url.startsWith("https:")) {
view.loadUrl(url);
return true;
}
/* WebView.HitTestResult hitTestResult = view.getHitTestResult();
//hitTestResult==null解決重定向問(wèn)題
if (!TextUtils.isEmpty(url) && hitTestResult == null) {
view.loadUrl(url);
return true;
}*/
return super.shouldOverrideUrlLoading(view, url);
}
/**
*開始加載
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
mPbLoading.setVisibility(View.VISIBLE);
}
/**
* 結(jié)束加載
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mPbLoading.setVisibility(View.GONE);
}
/**
* 加載錯(cuò)誤的時(shí)候會(huì)產(chǎn)生這個(gè)回調(diào)
*/
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
//TODO
}
/**
* HTTPS通信的網(wǎng)址(以https://開頭的網(wǎng)站)出現(xiàn)錯(cuò)誤時(shí)
* 證書錯(cuò)誤攔截處理
* 安卓7.0需要
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
if(error.getPrimaryError() == android.net.http.SslError.SSL_INVALID ){// 校驗(yàn)過(guò)程遇到了bug
handler.proceed(); //忽略錯(cuò)誤繼續(xù)加載
}else{
handler.cancel(); //取消加載
}
}
}
/**
* 針對(duì)瀏覽器攔截處理
*/
public class MyWebChromeClient extends WebChromeClient{
/**
* 彈窗攔截
*/
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
/**
* 彈窗攔截
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
/**
* 彈窗攔截
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
/**
* 加載進(jìn)度攔截
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
/**
* 文件選擇
*/
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
selectImage();
return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
/*** 視頻播放相關(guān)的方法 **/
@Override
public View getVideoLoadingProgressView() {
FrameLayout frameLayout = new FrameLayout(MainActivity.this);
frameLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return frameLayout;
}
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
showCustomView(view, callback);
}
@Override
public void onHideCustomView() {
hideCustomView();
}
}
/**
* 圖片選擇
*/
private void selectImage() {
//TODO
}
/**
* 視頻播放全屏
*/
private void showCustomView(View view, WebChromeClient.CustomViewCallback callback) {
// if a view already exists then immediately terminate the new one
if (customView != null) {
callback.onCustomViewHidden();
return;
}
getWindow().getDecorView();
//獲取虛擬按鍵高度,防止遮擋
if(ScreenUtils.hasNavBar(this)){
COVER_SCREEN_PARAMS.setMargins(0,0,0,ScreenUtils.getNavigationBarHeight(this));
}
FrameLayout decor = (FrameLayout) getWindow().getDecorView();
fullscreenContainer = new FullscreenHolder(this);
fullscreenContainer.addView(view, COVER_SCREEN_PARAMS);
decor.addView(fullscreenContainer, COVER_SCREEN_PARAMS);
customView = view;
setStatusBarVisibility(false);
customViewCallback = callback;
}
/**
* 隱藏視頻全屏
*/
private void hideCustomView() {
if (customView == null) {
return;
}
setStatusBarVisibility(true);
FrameLayout decor = (FrameLayout) getWindow().getDecorView();
decor.removeView(fullscreenContainer);
fullscreenContainer = null;
customView = null;
customViewCallback.onCustomViewHidden();
mWebView.setVisibility(View.VISIBLE);
}
/**
* 全屏容器界面
*/
static class FullscreenHolder extends FrameLayout {
public FullscreenHolder(Context ctx) {
super(ctx);
setBackgroundColor(ctx.getResources().getColor(android.R.color.black));
}
@Override
public boolean onTouchEvent(MotionEvent evt) {
return true;
}
}
private void setStatusBarVisibility(boolean visible) {
int flag = visible ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
贈(zèng)人玫瑰,手有余香。您的支持是我創(chuàng)作最大的動(dòng)力!