125

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

视频是作为HTML5 元素完成的,这些在 Chrome、iPhone 上运行良好且花哨,现在我们修复了它Android在本机浏览器中运行良好的编码问题。

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

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

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

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

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

4

13 回答 13

94

我回答这个话题,以防有人读到它并对结果感兴趣。可以在 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 框架内播放,而不是打开媒体播放器窗口,但这对我来说是次要问题。

我希望它可以帮助某人,我也会感谢任何评论或建议。

Saludos,terrícolas。

于 2010-10-07T14:34:02.357 回答
62

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

测试.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>

颜色.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>

清单.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>

期待你能理解的其他事情。

于 2012-06-06T10:12:45.193 回答
34

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

我对此进行了一些研究,这就是我的发现,以防万一像我这样疲倦的 WebView 旅行者将来偶然发现这篇文章。

首先,我查看了VideoViewMediaPlayer的文档,并更好地了解了它们是如何工作的。我强烈推荐那些。

然后,我查看了源代码以了解 Android 浏览器是如何做到的。做一个页面查找并去看看他们是如何处理onShowCustomView()的。他们保留CustomViewCallback对自定义视图的引用。

考虑到所有这些,并考虑到 mdelolmo 的回答,当您完成视频后,您需要做的只是两件事。首先,在VideoView您保存引用的那个上,调用stopPlayback()将释放MediaPlayer稍后在其他地方使用的那个。您可以在VideoView源代码中看到它。其次,在CustomViewCallback您保存了对 call 的引用CustomViewCallback.onCustomViewHidden()

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

希望有帮助。

于 2011-01-09T04:48:06.140 回答
7

我知道这个线程已经有几个月的历史了,但我找到了一个解决方案,可以在 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();
         }
     }
 }

所以,我希望我能帮忙...互联网和一些我必须自己弄清楚的事情。这真的是一个痛苦的*。

于 2011-08-09T12:09:31.393 回答
7

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

mWebView.setWebChromeClient(new WebChromeClient());

并且您需要打开硬件加速!

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

任何想法如何拦截展开视频按钮?

于 2012-03-09T00:52:13.917 回答
4

这种方法在 2.3 之前运行良好并且通过添加 hardwareaccelerated=true 它甚至可以从 3.0 到 ICS 我目前面临的一个问题是在第二次启动媒体播放器应用程序时崩溃,因为我没有停止播放并发布媒体播放器。由于 VideoSurfaceView 对象,我们从 3.0 操作系统中获得的 onShowCustomView 函数,是特定于浏览器的,而不是像在 2.3 操作系统中那样的 VideoView 对象我如何访问它并停止播放和释放资源?

于 2012-03-11T13:48:17.227 回答
3

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

我认为我们可以使用

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

反而。

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

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

在活动的 onBackPressed 方法中。

于 2011-12-16T05:45:29.513 回答
2

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

有了这套,它似乎无需任何 WebChromeClient 修改即可工作(我希望从 DOM-Element 中进行修改。)

于 2011-11-03T15:27:27.777 回答
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" 代码放入您的活动中,例如(Androidmanifest.xml)

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

并覆盖 onConfigurationChanged 方法。

@Override
public void onConfigurationChanged(Configuration newConfig) {
     super.onConfigurationChanged(newConfig);
}
于 2012-04-18T08:09:10.507 回答
2

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

于 2016-10-04T22:12:03.723 回答
1

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

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

恶心。

于 2010-10-05T18:04:55.977 回答
1

关于蜂窝的使用hardwareaccelerated=truepluginstate.on_demand似乎有效

于 2012-01-25T23:30:51.820 回答
1

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

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

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

于 2013-09-30T14:33:47.847 回答