解決WebView視屏播放問題記錄
情景
專案中有一大板塊是載入web頁面,開始集成了騰訊的X5核心WebView(因為本身集成了視訊播放功能,使用起來比較方便)。但是後來前端大神寫了一個web頁面用到了<canvas>標籤,使用X5載入不出來該標籤的內容,因為我們設定了webView關閉了硬體加速( webview.setLayerType(View.LAYER_TYPE_SOFTWARE,null)),今日仔細查看了X5的官方文件才知道:X5是不建議使用以下兩個api的:
經過考慮,我們決定使用Android原生的WebView,然後自己處理播放網路視訊時的問題,簡單記錄過程中遇到的問題。
問題一:視訊黑屏
我遇到的視訊黑屏是:視訊播放時只有聲音無畫面的情況,原因就是前面我說的我在初始化的時候講webview的硬體加速關閉了,後來查資料說webView播放視訊必須開啟硬體加速:
在AndroidManifest.xml檔案中的application或者webView所在的activity標籤中開啟硬體加速:
然後在程式碼中:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager
.LayoutParams.FLAG_HARDWARE_ACCELERATED);
即可解決黑屏問題。
問題二:全屏問題
一般前端頁面視訊標籤使用的是video標籤,點選全屏按鈕時,會回撥webView的WebChromClient的onShowCustomView方法,點選退出全屏時會回撥webView的WebChromClient的onHideCustomView方法。所以實現全屏的邏輯我們就重寫這兩個方法即可:
為了在其它地方使用webView方便,我就把WebView封裝了一下,首先看CustomWebView的佈局檔案:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none"/> <!--作為視訊全屏播放時的容器--> <FrameLayout android:id="@+id/frame_full_screen" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" android:background="#000000"/> </FrameLayout>
自定義CustomWebView的邏輯程式碼:
public class CustomWebView extends FrameLayout {
private Context context;
private WebView webView;
//全屏播放容器
private FrameLayout fullScreenLayout;
public CustomWebView(Context context, AttributeSet arg1) {
super(context, arg1);
this.context = context;
init(context);
}
private void init(Context context){
LayoutInflater layoutInflater = LayoutInflater.from(context);
View rootView = layoutInflater.inflate(R.layout.custom_web_layout,this,true);
webView = (WebView) rootView.findViewById(R.id.webview);
fullScreenLayout = (FrameLayout) rootView.findViewById(R.id.frame_full_screen);
initWebViewSettings();
}
private void initWebViewSettings() {
WebSettings s = webView.getSettings();
s.setBuiltInZoomControls(true);
s.setPluginState(WebSettings.PluginState.ON);
s.setCacheMode(WebSettings.LOAD_DEFAULT);
s.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
s.setRenderPriority(WebSettings.RenderPriority.HIGH);
s.setAppCacheEnabled(false);
s.setJavaScriptCanOpenWindowsAutomatically(true);
s.setUseWideViewPort(true);
s.setLoadWithOverviewMode(true);
s.setSavePassword(false);
s.setSaveFormData(true);
s.setJavaScriptEnabled(true);
s.setLoadsImagesAutomatically(true);
s.setSupportZoom(false);// ql
s.setBuiltInZoomControls(false);
s.setGeolocationEnabled(true);
s.setGeolocationDatabasePath("http://www.cvbaoli.com/webak/public/showAgreement");
s.setDomStorageEnabled(true);
//如果大於5.0設定混合模式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
s.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
}
public WebView getWebView(){
return webView;
}
public FrameLayout getFullScreenLayout(){
return fullScreenLayout;
}
}
CustomWebChromeClient繼承WebChromeClient
public abstract class CustomWebChromClient extends WebChromeClient {
private FrameLayout fullScreenLayout;
private View customView;
private CustomViewCallback customCallback;
private Context context;
private WebView webView;
public void setCustomWebView(@NotNull CustomWebView customWebView) {
this.webView = customWebView.getWebView();
this.fullScreenLayout = customWebView.getFullScreenLayout();
}
public void setContext(Context context) {
this.context = context;
}
public CustomWebChromClient() {
}
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
super.onShowCustomView(view, callback);
if (webView == null || fullScreenLayout == null) {
init();
}
showCustomView(view, callback);
}
@Override
public void onHideCustomView() {
super.onHideCustomView();
hideCustomView();
}
/**
* 視訊播放全屏
**/
private void showCustomView(View view, CustomViewCallback callback) {
// if a view already exists then immediately terminate the new one
if (customView != null) {
callback.onCustomViewHidden();
return;
}
setStatusBarVisibility(false);
fullScreenLayout.addView(view);
customView = view;
customCallback = callback;
webView.setVisibility(View.GONE);
fullScreenLayout.setVisibility(VISIBLE);
fullScreenLayout.bringToFront();
// 橫屏顯示
((Activity) context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// //設定全屏
// ((Activity)context).getParent()
// .getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
// WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
/**
* 隱藏視訊全屏
*/
public void hideCustomView() {
if (customView == null) {
return;
}
setStatusBarVisibility(true);
customView.setVisibility(GONE);
fullScreenLayout.removeAllViews();
customView = null;
fullScreenLayout.setVisibility(GONE);
try {
customCallback.onCustomViewHidden();
} catch (Exception e) {
e.printStackTrace();
}
webView.setVisibility(VISIBLE);
//換成豎屏
((Activity) context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
private void setStatusBarVisibility(boolean visible) {
int flag = visible ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN;
((Activity) context).getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setBarVisible(visible);
}
protected abstract void setBarVisible(boolean visible);
protected abstract void init();
public boolean webIsFullScreen() {
return customView != null;
}
在Activity中使用
customWebView = (CustomWebView) findViewById(R.id.customWebView);
webview = customWebView.getWebView();
webview.setWebChromeClient(mWebChromeClient);
private CustomWebChromClient mWebChromeClient = new CustomWebChromClient() {
@Override
protected void setBarVisible(boolean visible) {//此方法設定全屏切換時標題欄等view的可見性,這個根據自己的需求自己實現即可
if (titleBar==null){
return;
}
titleBar.setVisibility(visible?View.VISIBLE:View.GONE);
}
@Override
protected void init() {
this.setCustomWebView(customWebView);
this.setContext(WebviewActivity.this);
}
};
以上就是實現視訊播放全屏的邏輯。實測可用。
問題三:在某些機型上退至後臺時聲音不停止
經測試,在大部分機型上是ok的,但是在華為P系列手機上會有問題,當webView的宿主Activity或者Fragment不可見時,視訊聲音不能自動停止。重寫宿主的生命週期,加入以下程式碼解決此Bug:
@Override
public void onPause() {
super.onPause();
if (webview != null) {
String videoJs = "javascript: var v = document.getElementsByTagName('video'); for(var i=0;i<v.length;i++){v[i].pause();} ";
webview.loadUrl(videoJs);//遍歷所有的Vedio標籤,主動呼叫暫停方法
webview.onPause();
webview.pauseTimers();
}
}
@Override
public void onResume() {
super.onResume();
if (webview != null) {
webview.resumeTimers();
webview.onResume();
}
}
WebView在19即4.4之前使用的是WebKit核心,4.4及以後採用了Chrome核心,又由於Androidd的碎片化問題,導致國內Android手機使用WebView會遇到各種各樣的問題。以上便是我踩過的坑,希望能夠幫到遇到同樣問題的童鞋。