Android WebView实现全屏播放的一种方法

用过WebView的开发者们肯定都知道这里面的坑数不胜数,加载缓慢,内存泄露,文件选择…… 最近在项目中遇到了对接小鱼易联直播的情况,因为小鱼本身提供了直播功能,采用的是网页方式,所以在手机端直接进行对接网页,然后在进行全屏播放视频,又遇到了一个大坑。一个没有修饰过的原生WebView几乎不可能在某一个主流视频网站实现全屏播放,倘若在客户端自己实现简单的播放器,链接拿过来,摆个VideoView,想怎么全屏怎么全屏,放在WebView上,一切就悲剧了,大多数情况下点击“全屏按钮”是没有反应的,或者无法实现横屏全屏。今天来介绍一种简单易行粗暴的方式来实现WebView的视频全屏播放。

当你无从下手的时候,照例先看一下官方文档,很多常见的问题官方文档都给我们提供思路。你会发现下面这样一段话,发现官方已经提供了实现全屏展示html content的方式。

应用如果需要支持HTML5的video标签,必须打开硬件加速。我们只需要在Application标签或者相应Activity标签下添加android:hardwareAccelerated=”true”即可。接着为了支持全屏,需要重写WebChromeClient的onShowCustomView()和onHideCustomView()方法,这两个方法缺一不可。先来看一下onShowCustomView():

当前页面进入全屏模式的时候会调用这个方法,并且返回了两个参数。第一个是我们要在全屏模式时显示的View,第二个是一个CustomViewCallBack接口,可以调用这个接口请求关闭全屏模式。再看一下onHideCustomView()方法:

通知应用当前页面已经关闭全屏模式,我们需要做的操作是隐藏之前onSHowCustomView()方法中取到的View。了解这两个方法之后,我们就可以进行一些操作来实现简单的全屏播放了。

下面我们以实际例子说明如何实现。

  1. 布局文件中我们增加一个和WebView同层级的Framelayout,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- View that will be hidden when video goes fullscreen -->
    <RelativeLayout
    android:id="@+id/nonVideoLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.daren.app.html.fullscreen_webview.VideoEnabledWebView
    android:id="@+id/webView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

    </RelativeLayout>

    <!-- View where the video will be shown when video goes fullscreen -->
    <RelativeLayout
    android:id="@+id/videoLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    </RelativeLayout>

    </RelativeLayout>
  2. 自定义WebChromeClient
    关键代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    @Override
    public void onShowCustomView(View view, CustomViewCallback callback)
    {
    if (view instanceof FrameLayout)
    {
    // A video wants to be shown
    FrameLayout frameLayout = (FrameLayout) view;
    View focusedChild = frameLayout.getFocusedChild();

    // Save video related variables
    this.isVideoFullscreen = true;
    this.videoViewContainer = frameLayout;
    this.videoViewCallback = callback;

    // Hide the non-video view, add the video view, and show it
    activityNonVideoView.setVisibility(View.INVISIBLE);
    activityVideoView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    activityVideoView.setVisibility(View.VISIBLE);

    if (focusedChild instanceof android.widget.VideoView)
    {
    // android.widget.VideoView (typically API level <11)
    android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;

    // Handle all the required events
    videoView.setOnPreparedListener(this);
    videoView.setOnCompletionListener(this);
    videoView.setOnErrorListener(this);
    }
    else
    {
    // Other classes, including:
    // - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 11-18)
    // - android.webkit.HTML5VideoFullScreen$VideoTextureView, which inherits from android.view.TextureView (typically API level 11-18)
    // - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 19+)

    // Handle HTML5 video ended event only if the class is a SurfaceView
    // Test case: TextureView of Sony Xperia T API level 16 doesn't work fullscreen when loading the javascript below
    if (webView != null && webView.getSettings().getJavaScriptEnabled() && focusedChild instanceof SurfaceView)
    {
    // Run javascript code that detects the video end and notifies the Javascript interface
    String js = "javascript:";
    js += "var _ytrp_html5_video_last;";
    js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
    js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
    {
    js += "_ytrp_html5_video_last = _ytrp_html5_video;";
    js += "function _ytrp_html5_video_ended() {";
    {
    js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
    }
    js += "}";
    js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
    }
    js += "}";
    webView.loadUrl(js);
    }
    }

    // Notify full-screen change
    if (toggledFullscreenCallback != null)
    {
    toggledFullscreenCallback.toggledFullscreen(true);
    }
    }
    }

    @Override @SuppressWarnings("deprecation")
    public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) // Available in API level 14+, deprecated in API level 18+
    {
    onShowCustomView(view, callback);
    }

    @Override
    public void onHideCustomView()
    {
    // This method should be manually called on video end in all cases because it's not always called automatically.
    // This method must be manually called on back key press (from this class' onBackPressed() method).

    if (isVideoFullscreen)
    {
    // Hide the video view, remove it, and show the non-video view
    activityVideoView.setVisibility(View.INVISIBLE);
    activityVideoView.removeView(videoViewContainer);
    activityNonVideoView.setVisibility(View.VISIBLE);

    // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
    if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium."))
    {
    videoViewCallback.onCustomViewHidden();
    }

    // Reset video related variables
    isVideoFullscreen = false;
    videoViewContainer = null;
    videoViewCallback = null;

    // Notify full-screen change
    if (toggledFullscreenCallback != null)
    {
    toggledFullscreenCallback.toggledFullscreen(false);
    }
    }
    }
  3. 最后别忘记处理屏幕旋转,否则横竖屏切换会重新走一遍生命周期.

    1
    2
    3
    4
    5
    <activity
    android:name=".html.ZbtWebViewActivity"
    android:theme="@style/Theme.AppCompat.NoActionBar"
    android:configChanges="orientation">
    </activity>
  4. 隐藏网页中的广告

    1
    2
    3
    4
    5
    6
    7
    @Override
    public void onPageFinished(WebView view, String url) {
    String js = "javascript:function hideAd(){document.getElementsByClassName('mobile__live--title')[0].style.display='none'}";
    webView.loadUrl(js);
    webView.loadUrl("javascript:hideAd();"); //调用js方法,隐藏广告
    super.onPageFinished(view, url);
    }

代码地址:可全屏的webview

参考链接:

分享到