解决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会遇到各种各样的问题。以上便是我踩过的坑,希望能够帮到遇到同样问题的童鞋。



猜你喜欢

转载自blog.csdn.net/anyanyan07/article/details/79537792