上次鄙人做了一个简单的利用webView实现的一个浏览器!其中遇到了两个问题,一个是将浏览器中需要下载的内容托管到系统默认的下载程序进行下载,这个比较简单就不在这里讨论了;另一个问题就是我们的Android设备版本是4.0.3,不能像Android2.3那样支持全屏播放视频,这个问题比较纠结,但是经过不断的摸索,终于解决了这个问题。在这里和大家分享一下解决方法:
1、首先定义一个VideoEnabledWebView继承自WebView,复写其中的loadData,loadDataWithBaseURL,loadUrl方法,道理很简单就是在加载url或者js的时候初始化一些内容。见代码:
- package com.danielme.android.webviewdemo;
- import java.util.Map;
- import android.annotation.SuppressLint;
- import android.content.Context;
- import android.os.Handler;
- import android.os.Looper;
- import android.util.AttributeSet;
- import android.webkit.WebChromeClient;
- import android.webkit.WebView;
- public class VideoEnabledWebView extends WebView
- {
- public interface ToggledFullscreenCallback
- {
- public void toggledFullscreen(boolean fullscreen);
- }
- private VideoEnabledWebChromeClient videoEnabledWebChromeClient;
- private boolean addedJavascriptInterface;
- public VideoEnabledWebView(Context context)
- {
- super(context);
- addedJavascriptInterface = false;
- }
- public VideoEnabledWebView(Context context, AttributeSet attrs)
- {
- super(context, attrs);
- addedJavascriptInterface = false;
- }
- public VideoEnabledWebView(Context context, AttributeSet attrs, int defStyle)
- {
- super(context, attrs, defStyle);
- addedJavascriptInterface = false;
- }
- /**
- * Pass only a VideoEnabledWebChromeClient instance.
- */
- @Override
- @SuppressLint ("SetJavaScriptEnabled")
- public void setWebChromeClient(WebChromeClient client)
- {
- getSettings().setJavaScriptEnabled(true);
- if (client instanceof VideoEnabledWebChromeClient)
- {
- this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client;
- }
- super.setWebChromeClient(client);
- }
- @Override
- public void loadData(String data, String mimeType, String encoding)
- {
- addJavascriptInterface();
- super.loadData(data, mimeType, encoding);
- }
- @Override
- public void loadDataWithBaseURL(String baseUrl, String data,
- String mimeType, String encoding,
- String historyUrl)
- {
- addJavascriptInterface();
- super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
- }
- @Override
- public void loadUrl(String url)
- {
- addJavascriptInterface();
- super.loadUrl(url);
- }
- @Override
- public void loadUrl(String url, Map<String, String> additionalHttpHeaders)
- {
- addJavascriptInterface();
- super.loadUrl(url, additionalHttpHeaders);
- }
- private void addJavascriptInterface()
- {
- System.out.println(addedJavascriptInterface);
- if (!addedJavascriptInterface)
- {
- // Add javascript interface to be called when the video ends (must be done before page load)
- addJavascriptInterface(new Object()
- {
- }, "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient
- addedJavascriptInterface = true;
- }
- }
- }
其中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方法就是视频播放完成会被调用的。其中有个构造函数需要提出来:
- public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, VideoEnabledWebView webView)
- {
- this.activityNonVideoView = activityNonVideoView;
- this.activityVideoView = activityVideoView;
- this.loadingView = loadingView;
- this.webView = webView;
- this.isVideoFullscreen = false;
- }
这个构造函数中的参数,第一个是webView的父布局,activityVideoView是另外的一个占满整个屏幕的布局,loadingView是播放器的那个显示缓冲状态的view,webView就是webView啦!
见activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <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"
- tools:context=".MainActivity" >
- <RelativeLayout
- android:id="@+id/nonVideoLayout"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <com.danielme.android.webviewdemo.VideoEnabledWebView
- android:id="@+id/webView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </RelativeLayout>
- <FrameLayout
- android:id="@+id/videoLayout"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- </FrameLayout>
- </RelativeLayout>
不多说了,直接贴代码VideoEnabledWebChromeClient.java代码。
- package com.danielme.android.webviewdemo;
- import android.app.ActionBar.LayoutParams;
- import android.media.MediaPlayer;
- import android.media.MediaPlayer.OnCompletionListener;
- import android.media.MediaPlayer.OnErrorListener;
- import android.media.MediaPlayer.OnPreparedListener;
- import android.view.View;
- import android.view.ViewGroup;
- import android.webkit.WebChromeClient;
- import android.widget.FrameLayout;
- import android.widget.VideoView;
- public class VideoEnabledWebChromeClient extends WebChromeClient implements OnPreparedListener, OnCompletionListener, OnErrorListener
- {
- public interface ToggledFullscreenCallback
- {
- public void toggledFullscreen(boolean fullscreen);
- }
- private View activityNonVideoView;
- private ViewGroup activityVideoView;
- private View loadingView;
- private VideoEnabledWebView webView;
- private boolean isVideoFullscreen; // Indicates if the video is being displayed using a custom view (typically full-screen)
- private FrameLayout videoViewContainer;
- private CustomViewCallback videoViewCallback;
- private ToggledFullscreenCallback toggledFullscreenCallback;
- /**
- * Never use this constructor alone.
- * This constructor allows this class to be defined as an inline inner class in which the user can override methods
- */
- public VideoEnabledWebChromeClient()
- {
- }
- /**
- * Builds a video enabled WebChromeClient.
- * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
- * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
- */
- public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView)
- {
- this.activityNonVideoView = activityNonVideoView;
- this.activityVideoView = activityVideoView;
- this.loadingView = null;
- this.webView = null;
- this.isVideoFullscreen = false;
- }
- /**
- * Builds a video enabled WebChromeClient.
- * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
- * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
- * @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.
- */
- public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView)
- {
- this.activityNonVideoView = activityNonVideoView;
- this.activityVideoView = activityVideoView;
- this.loadingView = loadingView;
- this.webView = null;
- this.isVideoFullscreen = false;
- }
- /**
- * Builds a video enabled WebChromeClient.
- * @param activityNonVideoView A View in the activity's layout that contains every other view that should be hidden when the video goes full-screen.
- * @param activityVideoView A ViewGroup in the activity's layout that will display the video. Typically you would like this to fill the whole layout.
- * @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.
- * @param webView The owner VideoEnabledWebView. Passing it will enable the VideoEnabledWebChromeClient to detect the HTML5 video ended event and exit full-screen.
- * 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).
- */
- public VideoEnabledWebChromeClient(View activityNonVideoView, ViewGroup activityVideoView, View loadingView, VideoEnabledWebView webView)
- {
- this.activityNonVideoView = activityNonVideoView;
- this.activityVideoView = activityVideoView;
- this.loadingView = loadingView;
- this.webView = webView;
- this.isVideoFullscreen = false;
- }
- /**
- * Indicates if the video is being displayed using a custom view (typically full-screen)
- * @return true it the video is being displayed using a custom view (typically full-screen)
- */
- public boolean isVideoFullscreen()
- {
- return isVideoFullscreen;
- }
- /**
- * Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen)
- * @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback
- */
- public void setOnToggledFullscreen(ToggledFullscreenCallback callback)
- {
- this.toggledFullscreenCallback = callback;
- }
- @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.GONE);
- activityVideoView.addView(videoViewContainer, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- activityVideoView.setVisibility(View.VISIBLE);
- if (focusedChild instanceof VideoView)
- {
- // VideoView (typically API level <11)
- VideoView videoView = (VideoView) focusedChild;
- // Handle all the required events
- videoView.setOnPreparedListener(this);
- videoView.setOnCompletionListener(this);
- videoView.setOnErrorListener(this);
- }
- else // Usually android.webkit.HTML5VideoFullScreen$VideoSurfaceView, sometimes android.webkit.HTML5VideoFullScreen$VideoTextureView
- {
- // HTML5VideoFullScreen (typically API level 11+)
- // Handle HTML5 video ended event
- if (webView != null && webView.getSettings().getJavaScriptEnabled())
- {
- // Run javascript code that detects the video end and notifies the interface
- String js = "javascript:";
- js += "_ytrp_html5_video = document.getElementsByTagName('video')[0];";
- js += "if (_ytrp_html5_video !== undefined) {";
- {
- js += "function _ytrp_html5_video_ended() {";
- {
- js += "_ytrp_html5_video.removeEventListener('ended', _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
- public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) // Only available in API level 14+
- {
- onShowCustomView(view, callback);
- }
- @Override
- public void onHideCustomView()
- {
- // This method must be manually (internally) called on video end in the case of VideoView (typically API level <11)
- // 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
- // This method must be manually (internally) 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.GONE);//播放视频的
- activityVideoView.removeView(videoViewContainer);
- activityNonVideoView.setVisibility(View.VISIBLE);
- // Call back
- if (videoViewCallback != null) videoViewCallback.onCustomViewHidden();
- // Reset video related variables
- isVideoFullscreen = false;
- videoViewContainer = null;
- videoViewCallback = null;
- // Notify full-screen change
- if (toggledFullscreenCallback != null)
- {
- toggledFullscreenCallback.toggledFullscreen(false);
- }
- }
- }
- @Override
- public View getVideoLoadingProgressView() // Video will start loading, only called in the case of VideoView (typically API level <11)
- {
- if (loadingView != null)
- {
- loadingView.setVisibility(View.VISIBLE);
- return loadingView;
- }
- else
- {
- return super.getVideoLoadingProgressView();
- }
- }
- @Override
- public void onPrepared(MediaPlayer mp) // Video will start playing, only called in the case of VideoView (typically API level <11)
- {
- if (loadingView != null)
- {
- loadingView.setVisibility(View.GONE);
- }
- }
- @Override
- public void onCompletion(MediaPlayer mp) // Video finished playing, only called in the case of VideoView (typically API level <11)
- {
- onHideCustomView();
- }
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) // Error while playing video, only called in the case of VideoView (typically API level <11)
- {
- return false; // By returning false, onCompletion() will be called
- }
- /**
- * Notifies the class that the back key has been pressed by the user.
- * 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.
- * @return Returns true if the event was handled, and false if it is not (video view is not visible)
- */
- public boolean onBackPressed()
- {
- if (isVideoFullscreen)
- {
- onHideCustomView();
- return true;
- }
- else
- {
- return false;
- }
- }
- }
主要是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