上次鄙人做了一个简单的利用webView实现的一个浏览器!其中遇到了两个问题,一个是将浏览器中需要下载的内容托管到系统默认的下载程序进行下载,这个比较简单就不在这里讨论了;另一个问题就是我们的Android设备版本是4.0.3,不能像Android2.3那样支持全屏播放视频,这个问题比较纠结,但是经过不断的摸索,终于解决了这个问题。在这里和大家分享一下解决方法:
1、首先定义一个VideoEnabledWebView继承自WebView,复写其中的loadData,loadDataWithBaseURL,loadUrl方法,道理很简单就是在加载url或者js的时候初始化一些内容。见代码:
packagecom.danielme.android.webviewdemo;
importjava.util.Map;
importandroid.annotation.SuppressLint;
importandroid.content.Context;
importandroid.os.Handler;
importandroid.os.Looper;
importandroid.util.AttributeSet;
importandroid.webkit.WebChromeClient;
importandroid.webkit.WebView;
publicclassVideoEnabledWebViewextendsWebView
{
publicinterfaceToggledFullscreenCallback
{
publicvoidtoggledFullscreen(booleanfullscreen);
}
privateVideoEnabledWebChromeClient videoEnabledWebChromeClient;
privatebooleanaddedJavascriptInterface;
publicVideoEnabledWebView(Context context)
{
super(context);
addedJavascriptInterface = false;
}
publicVideoEnabledWebView(Context context, AttributeSet attrs)
{
super(context, attrs);
addedJavascriptInterface = false;
}
publicVideoEnabledWebView(Context context, AttributeSet attrs,intdefStyle)
{
super(context, attrs, defStyle);
addedJavascriptInterface = false;
}
/**
* Pass only a VideoEnabledWebChromeClient instance.
*/
@Override
@SuppressLint("SetJavaScriptEnabled")
publicvoidsetWebChromeClient(WebChromeClient client)
{
getSettings().setJavaScriptEnabled(true);
if(clientinstanceofVideoEnabledWebChromeClient)
{
this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client;
}
super.setWebChromeClient(client);
}
@Override
publicvoidloadData(String data, String mimeType, String encoding)
{
addJavascriptInterface();
super.loadData(data, mimeType, encoding);
}
@Override
publicvoidloadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding,
String historyUrl)
{
addJavascriptInterface();
super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@Override
publicvoidloadUrl(String url)
{
addJavascriptInterface();
super.loadUrl(url);
}
@Override
publicvoidloadUrl(String url, Map additionalHttpHeaders)
{
addJavascriptInterface();
super.loadUrl(url, additionalHttpHeaders);
}
privatevoidaddJavascriptInterface()
{
System.out.println(addedJavascriptInterface);
if(!addedJavascriptInterface)
{
// Add javascript interface to be called when the video ends (must be done before page load)
addJavascriptInterface(newObject()
{
}, "_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方法就是视频播放完成会被调用的。其中有个构造函数需要提出来:
publicVideoEnabledWebChromeClient(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
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
android:id="@+id/nonVideoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:id="@+id/videoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
不多说了,直接贴代码VideoEnabledWebChromeClient.java代码。
packagecom.danielme.android.webviewdemo;
importandroid.app.ActionBar.LayoutParams;
importandroid.media.MediaPlayer;
importandroid.media.MediaPlayer.OnCompletionListener;
importandroid.media.MediaPlayer.OnErrorListener;
importandroid.media.MediaPlayer.OnPreparedListener;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.webkit.WebChromeClient;
importandroid.widget.FrameLayout;
importandroid.widget.VideoView;
publicclassVideoEnabledWebChromeClientextendsWebChromeClientimplementsOnPreparedListener, OnCompletionListener, OnErrorListener
{
publicinterfaceToggledFullscreenCallback
{
publicvoidtoggledFullscreen(booleanfullscreen);
}
privateView activityNonVideoView;
privateViewGroup activityVideoView;
privateView loadingView;
privateVideoEnabledWebView webView;
privatebooleanisVideoFullscreen;// Indicates if the video is being displayed using a custom view (typically full-screen)
privateFrameLayout videoViewContainer;
privateCustomViewCallback videoViewCallback;
privateToggledFullscreenCallback 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
*/
publicVideoEnabledWebChromeClient()
{
}
/**
* 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.
*/
publicVideoEnabledWebChromeClient(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.
*/
publicVideoEnabledWebChromeClient(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).
*/
publicVideoEnabledWebChromeClient(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)
*/
publicbooleanisVideoFullscreen()
{
returnisVideoFullscreen;
}
/**
* 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
*/
publicvoidsetOnToggledFullscreen(ToggledFullscreenCallback callback)
{
this.toggledFullscreenCallback = callback;
}
@Override
publicvoidonShowCustomView(View view, CustomViewCallback callback)
{
if(viewinstanceofFrameLayout)
{
// 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, newLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
activityVideoView.setVisibility(View.VISIBLE);
if(focusedChildinstanceofVideoView)
{
// 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
publicvoidonShowCustomView(View view,intrequestedOrientation, CustomViewCallback callback)// Only available in API level 14+
{
onShowCustomView(view, callback);
}
@Override
publicvoidonHideCustomView()
{
// 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
publicView getVideoLoadingProgressView()// Video will start loading, only called in the case of VideoView (typically API level <11)
{
if(loadingView !=null)
{
loadingView.setVisibility(View.VISIBLE);
returnloadingView;
}
else
{
returnsuper.getVideoLoadingProgressView();
}
}
@Override
publicvoidonPrepared(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
publicvoidonCompletion(MediaPlayer mp)// Video finished playing, only called in the case of VideoView (typically API level <11)
{
onHideCustomView();
}
@Override
publicbooleanonError(MediaPlayer mp,intwhat,intextra)// Error while playing video, only called in the case of VideoView (typically API level <11)
{
returnfalse;// 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)
*/
publicbooleanonBackPressed()
{
if(isVideoFullscreen)
{
onHideCustomView();
returntrue;
}
else
{
returnfalse;
}
}
}
主要是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
【编辑推荐】
【责任编辑:张叶青 TEL:(010)68476606】
点赞 0