Well, I've been searching few days already, how to display HTML5 video in full-screen mode on android WebView.
好吧,我已经搜索了几天了,如何在android WebView的全屏模式下显示HTML5视频。
I managed to play HTML5 videos on my webview. Problems are arising when displaying video in fullscreen mode.
我设法在我的webview上播放HTML5的视频。在全屏模式显示视频时出现问题。
As I figured out, android has two ways of handling the
正如我所指出的,android有两种方式来处理 <视频> 标签:
On android versions <= 2.3.3, the onShowCustomView method is fired, and I can have the VideoView instance, and set listeners when the video completes, set controllers etc. So far so good.
在android版本的<= 2.3.3中,onShowCustomView方法被触发,我可以有视频视图实例,在视频完成时设置监听器,设置控制器等等。
On ICS (and probably 3.0 and above), it looks like the
在ICS(可能是3.0或以上)上,它看起来像 <视频> ,以不同的方式处理。当HTML5视频播放onShowCustomView不是被称为正常模式——它看起来像有一个内部业务WebView中播放视频,和所有的控件中定义的 <视频> 标记所示——我不能以任何方式访问它。实际上,如果视频在正常模式下播放,这是可以的,因为控制在那里并且正在工作。
That led me to the big problem: when displaying the video in full screen mode the onShowCustomView is being called - but on ICS the "view" parameter isn't an instance of VideoView.
这让我想到了一个大问题:当在全屏模式下显示视频时,onShowCustomView正在被调用,但在ICS上,“view”参数并不是VideoView的实例。
I managed to find out that the instance is of VideoSurfaceView, a private inner class of HTML5VideoFullScreen class. The only way we can access this inner-class is via reflection.
我设法找到了VideoSurfaceView的实例,它是HTML5VideoFullScreen类的一个私有内部类。我们可以通过反射来访问这个内部类。
After looking at GrepCode for this class, I learnt that unlike VideoView, the HTML5VideoFullScreen$VideoSurfaceView doesn't hold a MediaPlayer instance that I can listen to its events or access its controls. The only thing I can do is take this VideoSurfaceView as it is and put it inside a full-screen-layout without controlling it.
在查看了这个类的GrepCode之后,我了解到,与VideoView不同的是,HTML5VideoFullScreen$VideoSurfaceView不包含一个MediaPlayer实例,我可以侦听它的事件或访问它的控件。我能做的唯一一件事就是把这个VideoSurfaceView放到一个全屏幕布局中而不去控制它。
Bottom line - When displaying video in full-screen, I don't know when the video ends, its controls aren't shown - this is pretty sad. I can't get the trigger for closing the full-screen.
底线——当在全屏显示视频时,我不知道视频什么时候结束,它的控制没有显示出来——这是相当可悲的。我无法得到关闭全屏的触发器。
I tried few unsuccessful workarounds:
我尝试了几个不成功的方法:
Reflection: I tried to reach the HTML5VideoFullScreen instance, which holds a MediaPlayer member, from the inner-class VideoSurfaceView. I didn't manage to get it, I'm not sure this is possible (ViewSurfaceView doesn't hold its owner's instance).
反射:我试着访问了HTML5VideoFullScreen实例,它包含一个MediaPlayer成员,来自于内部类VideoSurfaceView。我没有得到它,我不确定这是可能的(ViewSurfaceView不保存它的所有者的实例)。
Register for the video events via Javascript (onended, for example), and handle what I need back in JAVA via JavascriptInterface: I found this solution isn't reliable because while doing this I encountered another problem: the
通过Javascript(例如onend)注册视频事件,并通过JavascriptInterface处理我在JAVA中需要的东西:我发现这个解决方案不可靠,因为在做这个过程时,我遇到了另一个问题:
I'm still searching for a solution, very little is written about this issue. Did anyone manage to solve it? Help would be much appreciated!
我还在寻找解决方案,关于这个问题的文章很少。有人能解决吗?非常感谢您的帮助!
VideoView class: Here (has MediaPlayer)
VideoView类:这里(有MediaPlayer)
HTML5VideoFullScreen$VideoSurfaceView class: Here (doesn't have MediaPlayer)
HTML5VideoFullScreen$VideoSurfaceView类:这里(没有MediaPlayer)
157
Edit 2014/10: by popular demand I'm maintaining and moving this to GitHub. Please check cprcrack/VideoEnabledWebView for the last version. Will keep this answer only for reference.Edit 2014/01: improved example usage to include the nonVideoLayout, videoLayout, and videoLoading views, for those users requesting more example code for better understading.
编辑2014/01:改进的示例用法包括非视频播放、视频播放和视频播放视图,这些用户请求更多的示例代码,以便更好地理解。
Edit 2013/12: some bug fixes related to Sony Xperia devices compatibility, but which in fact affected all devices.
编辑2013/12:一些与索尼Xperia设备兼容性相关的bug修复,但实际上影响了所有设备。
Edit 2013/11: after the release of Android 4.4 KitKat (API level 19) with its new Chromium webview, I had to work hard again. Several improvements were made. You should update to this new version. I release this source under WTFPL. Donations are accepted (find Donate link at bottom).
编辑2013/11:Android 4.4 KitKat (API level 19)发布后,我不得不再次努力工作。了一些改进。您应该更新到这个新版本。我在WTFPL下释放这个源。捐赠被接受(在底部发现捐赠链接)。
Edit 2013/04: after 1 week of hard work, I finally have achieved everything I needed. I think this two generic classes that I have created can solve all you problems.
编辑2013/04:经过一周的辛苦工作,我终于实现了我所需要的一切。我认为我创建的这两个通用类可以解决您所有的问题。
VideoEnabledWebChromeClient
can be used alone if you do not require the functionality that VideoEnabledWebView
adds. But VideoEnabledWebView
must always rely on a VideoEnabledWebChromeClient
. Please read all the comments of the both classes carefully.
如果你不需要VideoEnabledWebView添加的功能,VideoEnabledWebChromeClient可以单独使用。但VideoEnabledWebView必须始终依赖于VideoEnabledWebChromeClient。请仔细阅读这两门课的所有评论。
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebChromeClient;
import android.widget.FrameLayout;
/**
* This class serves as a WebChromeClient to be set to a WebView, allowing it to play video.
* Video will play differently depending on target API level (in-line, fullscreen, or both).
*
* It has been tested with the following video classes:
* - android.widget.VideoView (typically API level <11)
* - android.webkit.HTML5VideoFullScreen$VideoSurfaceView/VideoTextureView (typically API level 11-18)
* - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView (typically API level 19+)
*
* Important notes:
* - For API level 11+, android:hardwareAccelerated="true" must be set in the application manifest.
* - The invoking activity must call VideoEnabledWebChromeClient's onBackPressed() inside of its own onBackPressed().
* - Tested in Android API levels 8-19. Only tested on http://m.youtube.com.
*
* @author Cristian Perez (http://cpr.name)
*
*/
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
*/
@SuppressWarnings("unused")
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.
*/
@SuppressWarnings("unused")
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.
*/
@SuppressWarnings("unused")
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.INVISIBLE);
activityVideoView.addView(videoViewContainer, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
activityVideoView.setVisibility(View.VISIBLE);
if (focusedChild instanceof android.widget.VideoView)
{
// android.widget.VideoView (typically API level <11)
android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;
// Handle all the required events
videoView.setOnPreparedListener(this);
videoView.setOnCompletionListener(this);