WebView和HTML5 <video> </video>

时间:2010-09-28 16:49:25

标签: android html5 video webview android-webview

我拼凑了一个廉价的应用程序,其中包括“框架”我们的一些网站...非常简单的WebViewClient。直到我点击视频。

视频以HTML5个元素完成,这些功能在Chrome,iPhone上工作得很好,现在我们修复了它在原生浏览器Android上运行良好的编码问题。

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

谷歌搜索,我发现this很接近,但似乎是基于'链接'(如在href ...中)而不是视频元素。 (onDownloadListener似乎不会在视频元素上调用...)

我也看到覆盖onShowCustomView的引用,但似乎没有在视频元素上调用...还没有shouldOverrideUrlLoading ..

我宁愿不进入“从服务器拉取xml,在应用程序中重新格式化”......通过在服务器上保留故事布局,我可以更好地控制内容,而不必强迫人们不断更新应用程序。因此,如果我可以说服WebView处理像本机浏览器这样的标签,那将是最好的。

我显然错过了一些明显的东西..但我不知道是什么。

13 个答案:

答案 0 :(得分:92)

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

- 找到正确编码的视频

- 初始化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(true);
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();
        }
    }
}

- 处理视频的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();
}

但现在我应该说还有一个重要的问题。我只能玩一次。我第二次点击视频调度员(海报或一些播放按钮),它什么也没做。

我还希望视频在WebView框架内播放,而不是打开Media Player窗口,但这对我来说是次要问题。

我希望它对某人有所帮助,我也要感谢任何意见或建议。

Saludos,terrícolas。

答案 1 :(得分:60)

经过长时间的研究,我得到了这个东西。请参阅以下代码:

<强> 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>

期待你可以理解的其他事情。

答案 2 :(得分:33)

mdelolmo的答案非常有帮助,但就像他说的那样,视频只播放一次,然后再打开它。

我对此进行了一些调查,这就是我发现的内容,以防万一像我这样疲惫不堪的WebView旅行者将来偶然发现这篇文章。

首先,我查看了VideoViewMediaPlayer的文档,更好地了解了这些工作原理。我强烈推荐那些。

然后,我看着source code to see how the Android Browser做了。查找页面并查看它们如何处理onShowCustomView()。他们保留对CustomViewCallback和自定义视图的引用。

考虑到所有这些,并且考虑到mdelolmo的答案,当您完成视频时,您需要做的就是两件事。首先,在您保存引用的VideoView上,调用将释放stopPlayback()的{​​{1}}以便稍后在其他地方使用。您可以在VideoView源代码中看到它。其次,在MediaPlayer上,您保存了引用CustomViewCallback

的引用

完成这两件事之后,您可以点击相同的视频或任何其他视频,它将像以前一样打开。无需重新启动整个WebView。

希望有所帮助。

答案 3 :(得分:8)

实际上,将股票WebChromeClient附加到客户端视图似乎就足够了,ala

mWebView.setWebChromeClient(new WebChromeClient());

您需要启用硬件加速!

至少,如果您不需要播放全屏视频,则无需将VideoView从WebView中拉出并将其推入Activity的视图中。它将在视频元素的分配矩形中播放。

如何截取扩展视频按钮?

答案 4 :(得分:6)

我知道这个帖子已有几个月了,但我找到了一个在WebView中播放视频的解决方案,而不是全屏(但仍然在媒体播放器......)。到目前为止,我没有在互联网上找到任何暗示,所以也许这对其他人来说也很有趣。 我仍在努力解决一些问题(即将媒体播放器置于屏幕的右侧,不知道为什么我做错了,但我认为这是一个相对较小的问题......)。

在自定义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();
         }
     }
 }

所以,我希望我可以提供帮助...我也不得不玩视频编码并看到使用带有html5视频的WebView的各种类型 - 最后我的工作代码是不同代码部分的混合我在互联网上发现了一些我必须自己弄清楚的事情。这真的是a *中的痛苦。

答案 5 :(得分:4)

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

答案 6 :(得分:3)

A-M类似于BrowerActivity所做的。 对于       FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams(768,512);

我认为我们可以使用

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

代替。

我遇到的另一个问题是,如果正在播放视频,并且用户点击后退按钮,则下次进入此活动(singleTop one)并且无法播放视频。为了解决这个问题,我打电话给

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

在活动的onBackPressed方法中。

答案 7 :(得分:2)

我知道这是一个非常古老的问题,但您是否尝试过针对您的应用或活动的hardwareAccelerated="true"清单标记?

使用这个集合,它似乎可以在没有任何WebChromeClient修改的情况下工作(我期望从DOM-Element中获得它。)

答案 8 :(得分:2)

我使用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);
}

要使视频可旋转,请将android:configChanges =“orientation”代码放入您的Activity中 例如(Androidmanifest.xml)

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

并覆盖onConfigurationChanged方法。

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

答案 9 :(得分:2)

这个问题已有数年之久,但也许我的答案会帮助像我这样的人,他们必须支持旧的Android版本。我尝试了很多不同的方法,这些方法适用于某些Android版本,但并非总体而言。我找到的最佳解决方案是使用针对HTML5功能支持进行了优化的Crosswalk Webview,适用于Android 4.1及更高版本。它与默认的Android WebView一样简单。你只需要包括库。在这里,您可以找到有关如何使用它的简单教程:https://diego.org/2015/01/07/embedding-crosswalk-in-android-studio/

答案 10 :(得分:1)

嗯,显然,如果不使用JNI注册插件来获取视频事件,这是不可能的。 (就我个人而言,我正在避免使用JNI,因为我真的不想在未来几个月内基于Atom的Android平板电脑问世时出现问题,从而失去了Java的可移植性。)

唯一真正的替代方案似乎是为WebView创建一个新的网页,并使用上面的Codelark网址中引用的A HREF链接以旧式方式制作视频。

恶心。

答案 11 :(得分:1)

蜂窝使用hardwareaccelerated=truepluginstate.on_demand似乎有用

答案 12 :(得分:1)

我有类似的问题。我在我的应用的assets-folder中有HTML文件和视频。

因此,视频位于APK内部。因为APK实际上是一个ZIP文件,所以WebView无法读取视频文件。

将所有HTML和视频文件复制到SD卡上对我有用。