情景
项目中有一大板块是加载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会遇到各种各样的问题。以上便是我踩过的坑,希望能够帮到遇到同样问题的童鞋。