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



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

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

1. package
2.  
3. import
4. import
5. import
6. import
7. import
8. import
9. import
10. import
11.  
12. public class VideoEnabledWebView extends
13. { 
14. public interface
15.     { 
16. public void toggledFullscreen(boolean
17.     }     
18. private
19. private boolean
20. public
21.     { 
22. super(context); 
23. false; 
24.     }    
25. public
26.     { 
27. super(context, attrs); 
28. false; 
29.     }    
30. public VideoEnabledWebView(Context context, AttributeSet attrs, int
31.     { 
32. super(context, attrs, defStyle); 
33. false; 
34.     }     
35. /**
36.      * Pass only a VideoEnabledWebChromeClient instance.
37.      */
38. @Override
39. @SuppressLint ("SetJavaScriptEnabled") 
40. public void
41.     { 
42. true); 
43.  
44. if (client instanceof
45.         { 
46. this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client; 
47.         } 
48.  
49. super.setWebChromeClient(client); 
50.     }     
51. @Override
52. public void
53.     { 
54.         addJavascriptInterface(); 
55. super.loadData(data, mimeType, encoding); 
56.     }     
57. @Override
58. public void
59.                                     String mimeType, String encoding, 
60.                                     String historyUrl) 
61.     { 
62.         addJavascriptInterface(); 
63. super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); 
64.     }     
65. @Override
66. public void
67.     { 
68.         addJavascriptInterface(); 
69. super.loadUrl(url); 
70.     }     
71. @Override
72. public void
73.     { 
74.         addJavascriptInterface(); 
75. super.loadUrl(url, additionalHttpHeaders); 
76.     }     
77. private void
78.     { 
79.         System.out.println(addedJavascriptInterface); 
80. if
81.         { 
82. // Add javascript interface to be called when the video ends (must be done before page load)
83. new
84.             { 
85. "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient            
86. true; 
87.         } 
88.     }    
89. } 
90.   
 
  
其中addJavascriptInterface方法是将一个当前的java对象绑定到一个javascript上面,使用如下方法
webv.addJavascriptInterface(this, "_VideoEnabledWebView");//this为当前对象,绑定到js的_VideoEnabledWebView上面,主要_VideoEnabledWebView的作用域是全局的。这个部分的内容我不是很懂,提供链接给大家学习下,希望看懂的朋友能教教这个步骤是干嘛的!()
2、定义一个类VideoEnabledWebChromeClient继承自WebChromeClient,这个WebChromeClient中的onShowCustomView方法就是播放网络视频时会被调用的方法,onHideCustomView方法就是视频播放完成会被调用的。其中有个构造函数需要提出来:
 
  
1. public
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. "http://schemas.android.com/tools"
4. "match_parent"
5. "match_parent"
6. ".MainActivity"
7.     <RelativeLayout 
8. "@+id/nonVideoLayout"
9. "match_parent"
10. "match_parent"
11.         <com.danielme.android.webviewdemo.VideoEnabledWebView 
12. "@+id/webView"
13. "match_parent"
14. "match_parent"
15.     </RelativeLayout> 
16.     <FrameLayout 
17. "@+id/videoLayout"
18. "match_parent"
19. "match_parent"
20.     </FrameLayout> 
21. </RelativeLayout> 
22.   
 
  
不多说了,直接贴代码VideoEnabledWebChromeClient.java代码。
 
  
1. package
2.  
3. import
4. import
5. import
6. import
7. import
8. import
9. import
10. import
11. import
12. import
13.  
14. public class VideoEnabledWebChromeClient extends WebChromeClient implements
15. { 
16. public interface
17.     { 
18. public void toggledFullscreen(boolean
19.     } 
20. private
21. private
22. private
23. private
24. private boolean isVideoFullscreen; // Indicates if the video is being displayed using a custom view (typically full-screen)
25. private
26. private
27. private
28. /**
29.      * Never use this constructor alone.
30.      * This constructor allows this class to be defined as an inline inner class in which the user can override methods
31.      */
32. public
33.     { 
34.     } 
35. /**
36.      * Builds a video enabled WebChromeClient.
37.      * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
38.      * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
39.      */
40. public
41.     { 
42. this.activityNonVideoView = activityNonVideoView; 
43. this.activityVideoView = activityVideoView; 
44. this.loadingView = null; 
45. this.webView = null; 
46. this.isVideoFullscreen = false; 
47.     } 
48. /**
49.      * Builds a video enabled WebChromeClient.
50.      * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
51.      * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
52.      * @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.
53.      */
54. public
55.     { 
56. this.activityNonVideoView = activityNonVideoView; 
57. this.activityVideoView = activityVideoView; 
58. this.loadingView = loadingView; 
59. this.webView = null; 
60. this.isVideoFullscreen = false; 
61.     } 
62. /**
63.      * Builds a video enabled WebChromeClient.
64.      * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
65.      * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
66.      * @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.
67.      * @param webView The owner VideoEnabledWebView. Passing it will enable the VideoEnabledWebChromeClient to detect the HTML5 video ended event and exit full-screen.
68.      * 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).
69.      */
70. public
71.     { 
72. this.activityNonVideoView = activityNonVideoView; 
73. this.activityVideoView = activityVideoView; 
74. this.loadingView = loadingView; 
75. this.webView = webView; 
76. this.isVideoFullscreen = false; 
77.     } 
78. /**
79.      * Indicates if the video is being displayed using a custom view (typically full-screen)
80.      * @return true it the video is being displayed using a custom view (typically full-screen)
81.      */
82. public boolean
83.     { 
84. return
85.     } 
86. /**
87.      * Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen)
88.      * @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback
89.      */
90. public void
91.     { 
92. this.toggledFullscreenCallback = callback; 
93.     } 
94. @Override
95. public void
96.     { 
97. if (view instanceof
98.         { 
99. // A video wants to be shown
100.             FrameLayout frameLayout = (FrameLayout) view; 
101.             View focusedChild = frameLayout.getFocusedChild(); 
102. // Save video related variables
103. this.isVideoFullscreen = true; 
104. this.videoViewContainer = frameLayout; 
105. this.videoViewCallback = callback; 
106. // Hide the non-video view, add the video view, and show it
107.             activityNonVideoView.setVisibility(View.GONE);             
108. new
109.             activityVideoView.setVisibility(View.VISIBLE); 
110. if (focusedChild instanceof
111.             { 
112. // VideoView (typically API level <11)
113.                 VideoView videoView = (VideoView) focusedChild; 
114. // Handle all the required events
115. this); 
116. this); 
117. this); 
118.             } 
119. else // Usually android.webkit.HTML5VideoFullScreen$VideoSurfaceView, sometimes android.webkit.HTML5VideoFullScreen$VideoTextureView
120.             { 
121. // HTML5VideoFullScreen (typically API level 11+)
122. // Handle HTML5 video ended event
123. if (webView != null
124.                 { 
125. // Run javascript code that detects the video end and notifies the interface
126. "javascript:"; 
127. "_ytrp_html5_video = document.getElementsByTagName('video')[0];"; 
128. "if (_ytrp_html5_video !== undefined) {"; 
129.                     { 
130. "function _ytrp_html5_video_ended() {"; 
131.                         { 
132. "_ytrp_html5_video.removeEventListener('ended', _ytrp_html5_video_ended);"; 
133. "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
134.                         } 
135. "}"; 
136. "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);"; 
137.                     } 
138. "}"; 
139.                     webView.loadUrl(js); 
140.                 } 
141.             } 
142.  
143. // Notify full-screen change
144. if (toggledFullscreenCallback != null) 
145.             { 
146. true); 
147.             } 
148.         } 
149.     } 
150. @Override
151. public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) // Only available in API level 14+
152.     { 
153.         onShowCustomView(view, callback); 
154.     } 
155. @Override
156. public void
157.     { 
158. // This method must be manually (internally) called on video end in the case of VideoView (typically API level <11)
159. // 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
160. // This method must be manually (internally) called on back key press (from this class' onBackPressed() method)
161. if
162.         { 
163. // Hide the video view, remove it, and show the non-video view
164. //播放视频的
165.             activityVideoView.removeView(videoViewContainer); 
166.             activityNonVideoView.setVisibility(View.VISIBLE); 
167.  
168. // Call back
169. if (videoViewCallback != null) videoViewCallback.onCustomViewHidden(); 
170.  
171. // Reset video related variables
172. false; 
173. null; 
174. null; 
175.  
176. // Notify full-screen change
177. if (toggledFullscreenCallback != null) 
178.             { 
179. false); 
180.             } 
181.         } 
182.     } 
183. @Override
184. public View getVideoLoadingProgressView() // Video will start loading, only called in the case of VideoView (typically API level <11)
185.     { 
186. if (loadingView != null) 
187.         { 
188.             loadingView.setVisibility(View.VISIBLE); 
189. return
190.         } 
191. else
192.         { 
193. return super.getVideoLoadingProgressView(); 
194.         } 
195.     } 
196. @Override
197. public void onPrepared(MediaPlayer mp) // Video will start playing, only called in the case of VideoView (typically API level <11)
198.     { 
199. if (loadingView != null) 
200.         { 
201.             loadingView.setVisibility(View.GONE); 
202.         } 
203.     } 
204.  
205. @Override
206. public void onCompletion(MediaPlayer mp) // Video finished playing, only called in the case of VideoView (typically API level <11)
207.     { 
208.         onHideCustomView(); 
209.     } 
210. @Override
211. public boolean onError(MediaPlayer mp, int what, int extra) // Error while playing video, only called in the case of VideoView (typically API level <11)
212.     { 
213. return false; // By returning false, onCompletion() will be called
214.     } 
215. /**
216.      * Notifies the class that the back key has been pressed by the user.
217.      * 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.
218.      * @return Returns true if the event was handled, and false if it is not (video view is not visible)
219.      */
220. public boolean
221.     { 
222. if
223.         { 
224.             onHideCustomView(); 
225. return true; 
226.         } 
227. else
228.         { 
229. return false; 
230.         } 
231.     }     
232. } 
233.


主要是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