WebView和HTML5 <video>

我正在拼凑一个廉价的应用程序,除了其他的东西“框架”我们的一些网站…很简单的WebViewClient 。 直到我点击video。

该video是以HTML5元素的forms完成的,而且这些元素在Chrome,iPhone上都可以正常工作,现在我们可以在本机浏览器中解决Android上的编码问题。

摩擦: WebView不喜欢它。 完全一样。 我可以点击海报图片,没有任何反应。

谷歌search,我发现这是接近,但似乎是基于“链接”(如在HREF …)而不是video元素。 (onDownloadListener似乎没有被video元素调用…)

我也看到重写onShowCustomView引用,但似乎没有被video元素调用…也不应该shouldOverrideUrlLoading ..

我宁愿不进入“从服务器拉xml,在应用程序重新格式化”..通过保持故事布局在服务器上,我可以控制内容好一点,而不强迫人们不断更新应用程序。 所以如果我能说服WebView来处理标签,比如原生浏览器,那最好。

我明显失去了一些明显的东西..但我不知道是什么。

我回答了这个问题,以防万一有人读到它,并对结果感兴趣。 可以在WebView中查看video元素(videohtml5标签),但是我必须说我必须处理好几天。 这些是我到目前为止必须遵循的步骤:

– find一个正确编码的video

当初始化WebView时,设置JavaScript,插件WebViewClient和WebChromeClient。

 url = new String(“http://broken-links.com/tests/video/”); 
 mWebView =(WebView)findViewById(R.id.webview);
 mWebView.setWebChromeClient(chromeClient);
 mWebView.setWebViewClient(wvClient);
 mWebView.getSettings()setJavaScriptEnabled(真)。
 。mWebView.getSettings()setPluginState(PluginState.ON);
 mWebView.loadUrl(URL);

– 处理WebChromeClient对象中的onShowCustomView。

 @Override public void onShowCustomView(View view, CustomViewCallback callback) { super.onShowCustomView(view, callback); if (view instanceof FrameLayout){ FrameLayout frame = (FrameLayout) view; if (frame.getFocusedChild() instanceof VideoView){ VideoView video = (VideoView) frame.getFocusedChild(); frame.removeView(video); a.setContentView(video); video.setOnCompletionListener(this); video.setOnErrorListener(this); video.start(); } } } 

– 处理video的onCompletion和onError事件,以便返回到Web视图。

 public void onCompletion(MediaPlayer mp) { Log.d(TAG, "Video completo"); a.setContentView(R.layout.main); WebView wb = (WebView) a.findViewById(R.id.webview); a.initWebView(); } 

但是现在我应该说还有一个重要的问题。 我只能玩一次。 第二次点击video调度员(海报或播放button),它什么都不做。

我还想让video在WebView框架内播放,而不是打开媒体播放器窗口,但这对我来说是第二个问题。

我希望这有助于某人,我也要感谢任何意见或build议。

Saludos,terrícolas。

经过长期的研究,我得到了这个东西的工作。 看下面的代码:

Test.java

 import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; public class Test extends Activity { HTML5WebView mWebView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWebView = new HTML5WebView(this); if (savedInstanceState != null) { mWebView.restoreState(savedInstanceState); } else { mWebView.loadUrl("http://192.168.1.18/xxxxxxxxxxxxxxxx/"); } setContentView(mWebView.getLayout()); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mWebView.saveState(outState); } @Override public void onStop() { super.onStop(); mWebView.stopLoading(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (mWebView.inCustomView()) { mWebView.hideCustomView(); // mWebView.goBack(); //mWebView.goBack(); return true; } } return super.onKeyDown(keyCode, event); } } 

HTML%VIDEO.java

 package com.ivz.idemandtest; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.webkit.GeolocationPermissions; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; public class HTML5WebView extends WebView { private Context mContext; private MyWebChromeClient mWebChromeClient; private View mCustomView; private FrameLayout mCustomViewContainer; private WebChromeClient.CustomViewCallback mCustomViewCallback; private FrameLayout mContentView; private FrameLayout mBrowserFrameLayout; private FrameLayout mLayout; static final String LOGTAG = "HTML5WebView"; private void init(Context context) { mContext = context; Activity a = (Activity) mContext; mLayout = new FrameLayout(context); mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(a).inflate(R.layout.custom_screen, null); mContentView = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.main_content); mCustomViewContainer = (FrameLayout) mBrowserFrameLayout.findViewById(R.id.fullscreen_custom_content); mLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS); // Configure the webview WebSettings s = getSettings(); s.setBuiltInZoomControls(true); s.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); s.setUseWideViewPort(true); s.setLoadWithOverviewMode(true); // s.setSavePassword(true); s.setSaveFormData(true); s.setJavaScriptEnabled(true); mWebChromeClient = new MyWebChromeClient(); setWebChromeClient(mWebChromeClient); setWebViewClient(new WebViewClient()); setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); // enable navigator.geolocation // s.setGeolocationEnabled(true); // s.setGeolocationDatabasePath("/data/data/org.itri.html5webview/databases/"); // enable Web Storage: localStorage, sessionStorage s.setDomStorageEnabled(true); mContentView.addView(this); } public HTML5WebView(Context context) { super(context); init(context); } public HTML5WebView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public HTML5WebView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public FrameLayout getLayout() { return mLayout; } public boolean inCustomView() { return (mCustomView != null); } public void hideCustomView() { mWebChromeClient.onHideCustomView(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if ((mCustomView == null) && canGoBack()){ goBack(); return true; } } return super.onKeyDown(keyCode, event); } private class MyWebChromeClient extends WebChromeClient { private Bitmap mDefaultVideoPoster; private View mVideoProgressView; @Override public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { //Log.i(LOGTAG, "here in on ShowCustomView"); HTML5WebView.this.setVisibility(View.GONE); // if a view already exists then immediately terminate the new one if (mCustomView != null) { callback.onCustomViewHidden(); return; } mCustomViewContainer.addView(view); mCustomView = view; mCustomViewCallback = callback; mCustomViewContainer.setVisibility(View.VISIBLE); } @Override public void onHideCustomView() { System.out.println("customview hideeeeeeeeeeeeeeeeeeeeeeeeeee"); if (mCustomView == null) return; // Hide the custom view. mCustomView.setVisibility(View.GONE); // Remove the custom view from its container. mCustomViewContainer.removeView(mCustomView); mCustomView = null; mCustomViewContainer.setVisibility(View.GONE); mCustomViewCallback.onCustomViewHidden(); HTML5WebView.this.setVisibility(View.VISIBLE); HTML5WebView.this.goBack(); //Log.i(LOGTAG, "set it to webVew"); } @Override public View getVideoLoadingProgressView() { //Log.i(LOGTAG, "here in on getVideoLoadingPregressView"); if (mVideoProgressView == null) { LayoutInflater inflater = LayoutInflater.from(mContext); mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null); } return mVideoProgressView; } @Override public void onReceivedTitle(WebView view, String title) { ((Activity) mContext).setTitle(title); } @Override public void onProgressChanged(WebView view, int newProgress) { ((Activity) mContext).getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress*100); } @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); } } static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); } 

custom_screen.xml

 <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"> <FrameLayout android:id="@+id/fullscreen_custom_content" android:visibility="gone" android:background="@color/black" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/error_console" android:layout_width="match_parent" android:layout_height="wrap_content" /> <FrameLayout android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </FrameLayout> 

video_loading_progress.xml

 <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2009 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/progress_indicator" android:orientation="vertical" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ProgressBar android:id="@android:id/progress" style="?android:attr/progressBarStyleLarge" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:paddingTop="5dip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/loading_video" android:textSize="14sp" android:textColor="?android:attr/textColorPrimary" /> </LinearLayout> 

colors.xml

 <?xml version="1.0" encoding="utf-8"?> <!-- /* //device/apps/common/assets/res/any/http_authentication_colors.xml ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ --> <!-- FIXME: Change the name of this file! It is now being used generically for the browser --> <resources> <color name="username_text">#ffffffff</color> <color name="username_edit">#ff000000</color> <color name="password_text">#ffffffff</color> <color name="password_edit">#ff000000</color> <color name="ssl_text_label">#ffffffff</color> <color name="ssl_text_value">#ffffffff</color> <color name="white">#ffffffff</color> <color name="black">#ff000000</color> <color name="geolocation_permissions_prompt_background">#ffdddddd</color> </resources> 

的Manifest.xml

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.test" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="7" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Test" android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:configChanges="orientation|keyboardHidden|keyboard"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_GPS" /> <uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" /> <uses-permission android:name="android.permission.ACCESS_LOCATION" /> </manifest> 

期待其他的东西,你可以理解。

mdelolmo的回答非常有帮助,但就像他说的那样,video只播放一次,然后又不能再打开。

我研究了一下,这是我发现的,以防万一像我这样疲惫的WebView旅行者在将来遇到这个post。

首先,我查看了VideoView和MediaPlayer的文档,并更好地了解了这些工作。 我强烈推荐这些。

然后,我查看了源代码,看看Android Browser是如何实现的 。 做一个页面find并看看他们如何处理onShowCustomView() 。 他们保持对CustomViewCallback和自定义视图的引用。

所有这一切,以及mdelolmo的回答,当你完成video,你需要做的只有两件事情。 首先,在您保存引用的VideoView上,调用stopPlayback() ,以释放将在其他地方使用的MediaPlayer 。 你可以在VideoView源代码中看到它。 其次,在CustomViewCallback保存了一个调用CustomViewCallback.onCustomViewHidden()的引用。

做完这两件事后,你可以点击相同的video或任何其他video,它会像以前一样打开。 无需重新启动整个WebView。

希望有所帮助。

实际上,仅仅将WebChromeClient附加到客户端视图ala就足够了

 mWebView.setWebChromeClient(new WebChromeClient()); 

你需要开启硬件加速function!

至less,如果您不需要播放全屏video,则无需将VideoView拖出WebView并将其推入活动视图。 它将在video元素的分配矩形中播放。

任何想法如何拦截扩大videobutton?

我知道这个线程已经有几个月的历史了,但是我发现了一个在WebView中播放video的解决scheme,而不是全屏播放(但仍然在媒体播放器中)。 到目前为止,我在互联网上没有发现任何暗示,所以也许这对其他人也是有趣的。 我仍然在努力解决一些问题(比如把媒体播放器放在屏幕的右边,不知道为什么我做错了,但这是一个相对较小的问题,我认为…)。

在Custom ChromeClient中指定LayoutParams:

 // 768x512 is the size of my video FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams (768, 512); 

我的onShowCustomView方法如下所示:

 public void onShowCustomView(final View view, final CustomViewCallback callback) { // super.onShowCustomView(view, callback); if (view instanceof FrameLayout) { this.mCustomViewContainer = (FrameLayout) view; this.mCustomViewCallback = callback; this.mContentView = (WebView) this.kameha.findViewById(R.id.webview); if (this.mCustomViewContainer.getFocusedChild() instanceof VideoView) { this.mCustomVideoView = (VideoView) this.mCustomViewContainer.getFocusedChild(); this.mCustomViewContainer.setVisibility(View.VISIBLE); final int viewWidth = this.mContentView.getWidth(); final int viewLeft = (viewWidth - 1024) / 2; // get the x-position for the video (I'm porting an iPad-Webapp to Xoom, // so I can use those numbers... you have to find your own of course... this.LayoutParameters.leftMargin = viewLeft + 256; this.LayoutParameters.topMargin = 128; // just add this view so the webview underneath will still be visible, // but apply the LayoutParameters specified above this.kameha.addContentView(this.mCustomViewContainer, this.LayoutParameters); this.mCustomVideoView.setOnCompletionListener(this); this.mCustomVideoView.setOnErrorListener(this); // handle clicks on the screen (turning off the video) so you can still // navigate in your WebView without having the video lying over it this.mCustomVideoView.setOnFocusChangeListener(this); this.mCustomVideoView.start(); } } } 

所以,我希望我可以帮助…我也必须玩video编码,并看到不同types的使用HTML5video的WebView – 最后我的工作代码是在不同的代码部分,我发现互联网和我必须自己弄清楚的一些事情。 这真是一个痛苦的a *。

这种方法工作得很好,直到2.3和通过添加硬件加速=真,它甚至从3.0到ICS的工作我目前面临的一个问题是第二次推出的媒体播放器应用程序正在崩溃,因为我没有停止播放和发布媒体播放器。 因为我们从3.0 OS获得的onShowCustomView函数中的VideoSurfaceView对象是特定于浏览器而不是VideoView对象的,所以直到2.3 OS如何访问它并停止播放和释放资源?

AM的function与BrowerActivity的function类似。 对于FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams(768,512);

我想我们可以使用

 FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT, FrameLayout.LayoutParams.FILL_PARENT) 

代替。

我遇到的另一个问题是如果video正在播放,用户点击后退button,下一次,你去这个活动(singleTop之一),不能播放video。 为了解决这个问题,我打电话给

 try { mCustomVideoView.stopPlayback(); mCustomViewCallback.onCustomViewHidden(); } catch(Throwable e) { //ignore } 

在activity的onBackPressed方法中。

我用html5webview来解决这个问题。下载并把它放到你的项目中,然后你就可以像这样编码。

 private HTML5WebView mWebView; String url = "SOMEURL"; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWebView = new HTML5WebView(this); if (savedInstanceState != null) { mWebView.restoreState(savedInstanceState); } else { mWebView.loadUrl(url); } setContentView(mWebView.getLayout()); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mWebView.saveState(outState); } 

为了使video可以旋转,把android:configChanges =“orientation”代码放到你的Activity中(例如Androidmanifest.xml)

 <activity android:name=".ui.HTML5Activity" android:configChanges="orientation"/> 

并覆盖onConfigurationChanged方法。

 @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } 

这个问题已经过去了几年,但也许我的回答会帮助像我这样的人支持旧的Android版本。 我尝试了很多不同的方法,在一些Android版本上工作,但不是所有。 我发现最好的解决scheme是使用Crosswalk Webview ,它针对HTML5function支持进行了优化,适用于Android 4.1及更高版本。 这与使用默认的Android WebView一样简单。 你只需要包括图书馆。 在这里你可以find一个关于如何使用它的简单教程: https : //diego.org/2015/01/07/embedding-crosswalk-in-android-studio/

我知道这是一个非常古老的问题,但是您是否已经尝试过hardwareAccelerated="true"清单标志为您的应用程序或活动?

有了这个集合,似乎没有任何WebChromeClient修改(我期望从一个DOM元素)。

在蜂窝使用hardwareaccelerated=truepluginstate.on_demand似乎工作

我有一个类似的问题。 我的应用程序资产文件夹中有HTML文件和video。

因此,这些video位于APK的内部。 由于APK确实是ZIP文件,因此WebView无法读取video文件。

将所有HTML和video文件复制到SD卡上工作。

那么,显然这是不可能的,没有使用JNI注册插件来获取video事件。 (就我个人而言,我正在避免JNI,因为当Atom基于Android的平板电脑在未来几个月推出时,我真的不想处理混乱,失去了Java的可移植性。)

唯一真正的select似乎是为WebView创build一个新的Web页面,并使用上面的Codelark url中引用的A HREF链接来录制老派的方式。

恶心。