android 4.0以上WebView不能全屏播放视频的解决办法

来源:互联网 发布:免费营销软件 编辑:程序博客网 时间:2024/04/27 19:03




上次做了一个简单的利用webView实现的一个浏览器!其中遇到了两个问题,一个是将浏览器中需要下载的内容托管到系统默认的下载程序进行下载。

AD:WOT2015 互联网运维与开发者大会 热销抢票


    上次鄙人做了一个简单的利用webView实现的一个浏览器!其中遇到了两个问题,一个是将浏览器中需要下载的内容托管到系统默认的下载程序进行下载,这个比较简单就不在这里讨论了;另一个问题就是我们的Android设备版本是4.0.3,不能像Android2.3那样支持全屏播放视频,这个问题比较纠结,但是经过不断的摸索,终于解决了这个问题。在这里和大家分享一下解决方法:

    1、首先定义一个VideoEnabledWebView继承自WebView,复写其中的loadData,loadDataWithBaseURL,loadUrl方法,道理很简单就是在加载url或者js的时候初始化一些内容。见代码:

    1. package com.danielme.android.webviewdemo; 
    2.  
    3. import java.util.Map; 
    4. import android.annotation.SuppressLint; 
    5. import android.content.Context; 
    6. import android.os.Handler; 
    7. import android.os.Looper; 
    8. import android.util.AttributeSet; 
    9. import android.webkit.WebChromeClient; 
    10. import android.webkit.WebView; 
    11.  
    12. public class VideoEnabledWebView extends WebView 
    13.     public interface ToggledFullscreenCallback 
    14.     { 
    15.         public void toggledFullscreen(boolean fullscreen); 
    16.     }     
    17.     private VideoEnabledWebChromeClient videoEnabledWebChromeClient; 
    18.     private boolean addedJavascriptInterface;    
    19.     public VideoEnabledWebView(Context context) 
    20.     { 
    21.         super(context); 
    22.         addedJavascriptInterface = false
    23.     }    
    24.     public VideoEnabledWebView(Context context, AttributeSet attrs) 
    25.     { 
    26.         super(context, attrs); 
    27.         addedJavascriptInterface = false
    28.     }    
    29.     public VideoEnabledWebView(Context context, AttributeSet attrs, int defStyle) 
    30.     { 
    31.         super(context, attrs, defStyle); 
    32.         addedJavascriptInterface = false
    33.     }     
    34.     /** 
    35.      * Pass only a VideoEnabledWebChromeClient instance. 
    36.      */ 
    37.     @Override 
    38.     @SuppressLint ("SetJavaScriptEnabled"
    39.     public void setWebChromeClient(WebChromeClient client) 
    40.     { 
    41.         getSettings().setJavaScriptEnabled(true); 
    42.  
    43.         if (client instanceof VideoEnabledWebChromeClient) 
    44.         { 
    45.             this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client; 
    46.         } 
    47.  
    48.         super.setWebChromeClient(client); 
    49.     }     
    50.     @Override 
    51.     public void loadData(String data, String mimeType, String encoding) 
    52.     { 
    53.         addJavascriptInterface(); 
    54.         super.loadData(data, mimeType, encoding); 
    55.     }     
    56.     @Override 
    57.     public void loadDataWithBaseURL(String baseUrl, String data, 
    58.                                     String mimeType, String encoding, 
    59.                                     String historyUrl) 
    60.     { 
    61.         addJavascriptInterface(); 
    62.         super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); 
    63.     }     
    64.     @Override 
    65.     public void loadUrl(String url) 
    66.     { 
    67.         addJavascriptInterface(); 
    68.         super.loadUrl(url); 
    69.     }     
    70.     @Override 
    71.     public void loadUrl(String url, Map<String, String> additionalHttpHeaders) 
    72.     { 
    73.         addJavascriptInterface(); 
    74.         super.loadUrl(url, additionalHttpHeaders); 
    75.     }     
    76.     private void addJavascriptInterface() 
    77.     { 
    78.         System.out.println(addedJavascriptInterface); 
    79.         if (!addedJavascriptInterface) 
    80.         { 
    81.             // Add javascript interface to be called when the video ends (must be done before page load) 
    82.             addJavascriptInterface(new Object() 
    83.             { 
    84.             }, "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient             
    85.             addedJavascriptInterface = true
    86.         } 
    87.     }    
    88.   

    其中addJavascriptInterface方法是将一个当前的java对象绑定到一个javascript上面,使用如下方法

    webv.addJavascriptInterface(this, "_VideoEnabledWebView");//this为当前对象,绑定到js的_VideoEnabledWebView上面,主要_VideoEnabledWebView的作用域是全局的。这个部分的内容我不是很懂,提供链接给大家学习下,希望看懂的朋友能教教这个步骤是干嘛的!(http://www.oschina.net/code/snippet_232612_8531)

    2、定义一个类VideoEnabledWebChromeClient继承自WebChromeClient,这个WebChromeClient中的onShowCustomView方法就是播放网络视频时会被调用的方法,onHideCustomView方法就是视频播放完成会被调用的。其中有个构造函数需要提出来:

    1. public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, VideoEnabledWebView webView) 
    2.     { 
    3.         this.activityNonVideoView = activityNonVideoView; 
    4.         this.activityVideoView = activityVideoView; 
    5.         this.loadingView = loadingView; 
    6.         this.webView = webView; 
    7.         this.isVideoFullscreen = false
    8.     } 
    9.   

    这个构造函数中的参数,第一个是webView的父布局,activityVideoView是另外的一个占满整个屏幕的布局,loadingView是播放器的那个显示缓冲状态的view,webView就是webView啦!

    见activity_main.xml

    1. <?xml version="1.0" encoding="utf-8"?> 
    2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    3.     xmlns:tools="http://schemas.android.com/tools" 
    4.     android:layout_width="match_parent" 
    5.     android:layout_height="match_parent" 
    6.     tools:context=".MainActivity" > 
    7.     <RelativeLayout 
    8.         android:id="@+id/nonVideoLayout" 
    9.         android:layout_width="match_parent" 
    10.         android:layout_height="match_parent" > 
    11.         <com.danielme.android.webviewdemo.VideoEnabledWebView 
    12.             android:id="@+id/webView" 
    13.             android:layout_width="match_parent" 
    14.             android:layout_height="match_parent" /> 
    15.     </RelativeLayout> 
    16.     <FrameLayout 
    17.         android:id="@+id/videoLayout" 
    18.         android:layout_width="match_parent" 
    19.         android:layout_height="match_parent" > 
    20.     </FrameLayout> 
    21. </RelativeLayout> 
    22.   

    不多说了,直接贴代码VideoEnabledWebChromeClient.java代码。

    1. package com.danielme.android.webviewdemo; 
    2.  
    3. import android.app.ActionBar.LayoutParams; 
    4. import android.media.MediaPlayer; 
    5. import android.media.MediaPlayer.OnCompletionListener; 
    6. import android.media.MediaPlayer.OnErrorListener; 
    7. import android.media.MediaPlayer.OnPreparedListener; 
    8. import android.view.View; 
    9. import android.view.ViewGroup; 
    10. import android.webkit.WebChromeClient; 
    11. import android.widget.FrameLayout; 
    12. import android.widget.VideoView; 
    13.  
    14. public class VideoEnabledWebChromeClient extends WebChromeClient implements OnPreparedListener, OnCompletionListener, OnErrorListener 
    15.     public interface ToggledFullscreenCallback 
    16.     { 
    17.         public void toggledFullscreen(boolean fullscreen); 
    18.     } 
    19.     private View activityNonVideoView; 
    20.     private ViewGroup activityVideoView; 
    21.     private View loadingView; 
    22.     private VideoEnabledWebView webView; 
    23.     private boolean isVideoFullscreen; // Indicates if the video is being displayed using a custom view (typically full-screen) 
    24.     private FrameLayout videoViewContainer; 
    25.     private CustomViewCallback videoViewCallback; 
    26.     private ToggledFullscreenCallback toggledFullscreenCallback; 
    27.     /** 
    28.      * Never use this constructor alone. 
    29.      * This constructor allows this class to be defined as an inline inner class in which the user can override methods 
    30.      */ 
    31.     public VideoEnabledWebChromeClient() 
    32.     { 
    33.     } 
    34.     /** 
    35.      * Builds a video enabled WebChromeClient. 
    36.      * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen. 
    37.      * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout. 
    38.      */ 
    39.     public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView) 
    40.     { 
    41.         this.activityNonVideoView = activityNonVideoView; 
    42.         this.activityVideoView = activityVideoView; 
    43.         this.loadingView = null
    44.         this.webView = null
    45.         this.isVideoFullscreen = false
    46.     } 
    47.     /** 
    48.      * Builds a video enabled WebChromeClient. 
    49.      * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen. 
    50.      * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout. 
    51.      * @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and without a parent view. 
    52.      */ 
    53.     public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView) 
    54.     { 
    55.         this.activityNonVideoView = activityNonVideoView; 
    56.         this.activityVideoView = activityVideoView; 
    57.         this.loadingView = loadingView; 
    58.         this.webView = null
    59.         this.isVideoFullscreen = false
    60.     } 
    61.     /** 
    62.      * Builds a video enabled WebChromeClient. 
    63.      * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen. 
    64.      * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout. 
    65.      * @param loadingView A View to be shown while the video is loading (typically only used in API level <11). Must be already inflated and without a parent view. 
    66.      * @param webView The owner VideoEnabledWebView. Passing it will enable the VideoEnabledWebChromeClient to detect the HTML5 video ended event and exit full-screen. 
    67.      * Note: The web page must only contain one video tag in order for the HTML5 video ended event to work. This could be improved if needed (see Javascript code). 
    68.      */ 
    69.     public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, VideoEnabledWebView webView) 
    70.     { 
    71.         this.activityNonVideoView = activityNonVideoView; 
    72.         this.activityVideoView = activityVideoView; 
    73.         this.loadingView = loadingView; 
    74.         this.webView = webView; 
    75.         this.isVideoFullscreen = false
    76.     } 
    77.     /** 
    78.      * Indicates if the video is being displayed using a custom view (typically full-screen) 
    79.      * @return true it the video is being displayed using a custom view (typically full-screen) 
    80.      */ 
    81.     public boolean isVideoFullscreen() 
    82.     { 
    83.         return isVideoFullscreen; 
    84.     } 
    85.     /** 
    86.      * Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen) 
    87.      * @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback 
    88.      */ 
    89.     public void setOnToggledFullscreen(ToggledFullscreenCallback callback) 
    90.     { 
    91.         this.toggledFullscreenCallback = callback; 
    92.     } 
    93.     @Override 
    94.     public void onShowCustomView(View view, CustomViewCallback callback) 
    95.     { 
    96.         if (view instanceof FrameLayout) 
    97.         { 
    98.             // A video wants to be shown 
    99.             FrameLayout frameLayout = (FrameLayout) view; 
    100.             View focusedChild = frameLayout.getFocusedChild(); 
    101.             // Save video related variables 
    102.             this.isVideoFullscreen = true
    103.             this.videoViewContainer = frameLayout; 
    104.             this.videoViewCallback = callback; 
    105.             // Hide the non-video view, add the video view, and show it 
    106.             activityNonVideoView.setVisibility(View.GONE);             
    107.             activityVideoView.addView(videoViewContainer, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 
    108.             activityVideoView.setVisibility(View.VISIBLE); 
    109.             if (focusedChild instanceof VideoView) 
    110.             { 
    111.                 // VideoView (typically API level <11) 
    112.                 VideoView videoView = (VideoView) focusedChild; 
    113.                 // Handle all the required events 
    114.                 videoView.setOnPreparedListener(this); 
    115.                 videoView.setOnCompletionListener(this); 
    116.                 videoView.setOnErrorListener(this); 
    117.             } 
    118.             else // Usually android.webkit.HTML5VideoFullScreen$VideoSurfaceView, sometimes android.webkit.HTML5VideoFullScreen$VideoTextureView 
    119.             { 
    120.                 // HTML5VideoFullScreen (typically API level 11+) 
    121.                 // Handle HTML5 video ended event 
    122.                 if (webView != null && webView.getSettings().getJavaScriptEnabled()) 
    123.                 { 
    124.                     // Run javascript code that detects the video end and notifies the interface 
    125.                     String js = "javascript:"
    126.                     js += "_ytrp_html5_video = document.getElementsByTagName('video')[0];"
    127.                     js += "if (_ytrp_html5_video !== undefined) {"
    128.                     { 
    129.                         js += "function _ytrp_html5_video_ended() {"
    130.                         { 
    131.                             js += "_ytrp_html5_video.removeEventListener('ended', _ytrp_html5_video_ended);"
    132.                             js += "_VideoEnabledWebView.notifyVideoEnd();"// Must match Javascript interface name and method of VideoEnableWebView 
    133.                         } 
    134.                         js += "}"
    135.                         js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);"
    136.                     } 
    137.                     js += "}"
    138.                     webView.loadUrl(js); 
    139.                 } 
    140.             } 
    141.  
    142.             // Notify full-screen change 
    143.             if (toggledFullscreenCallback != null
    144.             { 
    145.                 toggledFullscreenCallback.toggledFullscreen(true); 
    146.             } 
    147.         } 
    148.     } 
    149.     @Override 
    150.     public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) // Only available in API level 14+ 
    151.     { 
    152.         onShowCustomView(view, callback); 
    153.     } 
    154.     @Override 
    155.     public void onHideCustomView() 
    156.     { 
    157.         // This method must be manually (internally) called on video end in the case of VideoView (typically API level <11) 
    158.         // This method must be manually (internally) called on video end in the case of HTML5VideoFullScreen (typically API level 11+) because it's not always called automatically 
    159.         // This method must be manually (internally) called on back key press (from this class' onBackPressed() method) 
    160.         if (isVideoFullscreen) 
    161.         { 
    162.             // Hide the video view, remove it, and show the non-video view 
    163.             activityVideoView.setVisibility(View.GONE);//播放视频的 
    164.             activityVideoView.removeView(videoViewContainer); 
    165.             activityNonVideoView.setVisibility(View.VISIBLE); 
    166.  
    167.             // Call back 
    168.             if (videoViewCallback != null) videoViewCallback.onCustomViewHidden(); 
    169.  
    170.             // Reset video related variables 
    171.             isVideoFullscreen = false
    172.             videoViewContainer = null
    173.             videoViewCallback = null
    174.  
    175.             // Notify full-screen change 
    176.             if (toggledFullscreenCallback != null
    177.             { 
    178.                 toggledFullscreenCallback.toggledFullscreen(false); 
    179.             } 
    180.         } 
    181.     } 
    182.     @Override 
    183.     public View getVideoLoadingProgressView() // Video will start loading, only called in the case of VideoView (typically API level <11) 
    184.     { 
    185.         if (loadingView != null
    186.         { 
    187.             loadingView.setVisibility(View.VISIBLE); 
    188.             return loadingView; 
    189.         } 
    190.         else 
    191.         { 
    192.             return super.getVideoLoadingProgressView(); 
    193.         } 
    194.     } 
    195.     @Override 
    196.     public void onPrepared(MediaPlayer mp) // Video will start playing, only called in the case of VideoView (typically API level <11) 
    197.     { 
    198.         if (loadingView != null
    199.         { 
    200.             loadingView.setVisibility(View.GONE); 
    201.         } 
    202.     } 
    203.  
    204.     @Override 
    205.     public void onCompletion(MediaPlayer mp) // Video finished playing, only called in the case of VideoView (typically API level <11) 
    206.     { 
    207.         onHideCustomView(); 
    208.     } 
    209.     @Override 
    210.     public boolean onError(MediaPlayer mp, int what, int extra) // Error while playing video, only called in the case of VideoView (typically API level <11) 
    211.     { 
    212.         return false// By returning false, onCompletion() will be called 
    213.     } 
    214.     /** 
    215.      * Notifies the class that the back key has been pressed by the user. 
    216.      * This must be called from the Activity's onBackPressed(), and if it returns false, the activity itself should handle it. Otherwise don't do anything. 
    217.      * @return Returns true if the event was handled, and false if it is not (video view is not visible) 
    218.      */ 
    219.     public boolean onBackPressed() 
    220.     { 
    221.         if (isVideoFullscreen) 
    222.         { 
    223.             onHideCustomView(); 
    224.             return true
    225.         } 
    226.         else 
    227.         { 
    228.             return false
    229.         } 
    230.     }     
    231.   

    主要是onShowCustomView方法中,当这个方法被调用,将含有webView的那个父布局隐藏掉(GONE),然后将第一个参数view加到布局中。获取第一个参数view的子控件childView,进行判断childView是否属于VideoView(Android 4.0之前是VideoView),如果是Android 4.0之后,则会执行else中的代码,新建String类型js代码,然后调用loadUrl(js)就可以进行视频播放了。其中我个人不知道它是如何通过js来播放视频的,我觉得和之前的addJavascriptInterface这个方法有一定关系,希望知道如何实现的能够指导一下本人。其它的函数就很好理解了。

    其中多说一句,Android 4.0之前的那个第一个参数view是videoView,Android 4.0之后是那个HTML5VideoFullScreen$VideoSurfaceView

    0 0
    原创粉丝点击