上次鄙人做了一个简单的利用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