web-dev-qa-db-ja.com

WebViewおよびHTML5 <ビデオ>

私は、とりわけ私たちのウェブサイトのいくつかを「フレーム化」するcheapoアプリをつなぎ合わせています... WebViewClientでとても簡単です。私がビデオをヒットするまで。

ビデオはHTML5要素として行われ、これらはChrome、iPhoneでうまく動作します。今では、ネイティブブラウザのAndroidでうまく機能するエンコードの問題を修正しました。

さて、rub:WebViewは好きではありません。まったく。ポスター画像をクリックしても何も起こりません。

グーグル、私は this を見つけましたが、これは近いですが、ビデオ要素ではなく「リンク」(href ...など)に基づいているようです。 (onDownloadListenerは、ビデオ要素で呼び出されるようには見えません...)

OnShowCustomViewのオーバーライドへの参照もありますが、ビデオ要素では呼び出されないようです... shouldOverrideUrlLoading ..

「サーバーからxmlを引き出し、アプリで再フォーマットする」ことは避けたいと思います。ストーリーレイアウトをサーバーに保持することで、アプリを更新し続けることを強制することなく、コンテンツを少し良く制御できます。したがって、ネイティブブラウザのようなタグを処理するようにWebViewを説得することができれば、それが最善でしょう。

私は明らかに明らかな何かを見逃している..しかし、私は何の手がかりも持っていない。

122
brian moore

誰かがそれを読んで結果に興味がある場合に備えて、私はこのトピックに答えます。 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();
        }
    }
}

-Webビューに戻るために、ビデオのonCompletionイベントとonErrorイベントを処理します。

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();
}

しかし、今、私はまだ重要な問題があると言う必要があります。一度だけプレイできます。ビデオディスパッチャー(ポスターまたは再生ボタン)を2回目にクリックしても、何も実行されません。

また、Media Playerウィンドウを開くのではなく、WebViewフレーム内でビデオを再生したいのですが、これは二次的な問題です。

それが誰かの助けになることを願っています。また、コメントや提案にも感謝します。

サルドス、テリコラス

92
mdelolmo

長い研究の後、私はこのことを機能させました。次のコードを参照してください。

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>

理解できることを期待してください。

60
surendra

mdelolmoの答えは信じられないほど役に立ちましたが、彼が言ったように、ビデオは一度だけ再生され、それから再び開くことはできません。

私はこれを少し調べましたが、私のような疲れたWebView旅行者が将来この投稿につまずく場合に備えて、ここで見つけました。

まず、 VideoView および MediaPlayer のドキュメントを見て、それらがどのように機能するかをよりよく理解しました。それらを強くお勧めします。

次に、 ソースコードでAndroid Browserがどのように動作するかを確認します を調べました。ページを見つけて、onShowCustomView()の処理方法を確認してください。 CustomViewCallbackとカスタムビューへの参照を保持します。

これらすべて、およびmdelolmoの回答を念頭に置いて、ビデオの処理が完了したら、必要なことは2つだけです。最初に、参照を保存したVideoViewstopPlayback()を呼び出します。これにより、後で使用されるMediaPlayerが解放されます。 VideoView ソースコードで確認できます。次に、CustomViewCallbackで、CustomViewCallback.onCustomViewHidden()を呼び出すための参照を保存しました。

これらの2つのことを行った後、同じビデオまたは他のビデオをクリックすると、以前のように開きます。 WebView全体を再起動する必要はありません。

お役に立てば幸いです。

33
cottonBallPaws

実際には、単にストックWebChromeClientをクライアントビューに添付するだけで十分なようです。

mWebView.setWebChromeClient(new WebChromeClient());

ハードウェアアクセラレーションを有効にする必要があります!

少なくとも、フルスクリーンビデオを再生する必要がない場合、WebViewからVideoViewを引き出してアクティビティのビューにプッシュする必要はありません。動画要素に割り当てられた四角形で再生されます。

ビデオの展開ボタンを傍受する方法はありますか?

8
Russ Schnapp

私はこのスレッドが数ヶ月前であることを知っていますが、フルスクリーンではなく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();
         }
     }
 }

だから、私は私が助けることができることを願っています...私もビデオエンコーディングで遊んで、さまざまな種類のWebViewをhtml5ビデオで使用するのを見なければなりませんでした-最終的に私の作業コードは私が見つけたさまざまなコードパーツインターネットと私が自分で考えなければならなかったいくつかのこと。それは本当にa *の痛みでした。

7
A-M

このアプローチは2.3まで非常にうまく機能し、hardwareaccelerated = trueを追加することで3.0からICSまで機能します。現在直面している問題の1つは、メディアプレーヤーアプリケーションの2回目の起動時です。メディアプレーヤーをリリースしました。 3.0 OSからonShowCustomView関数で取得するVideoSurfaceViewオブジェクトは、2.3 OSまではVideoViewオブジェクトではなくブラウザに固有であるため、2.3 OSまではどうやってアクセスして停止し、リソースを解放できますか?

4
Nova

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) 

代わりに。

私が出会った別の問題は、ビデオが再生されていて、ユーザーが次に戻るボタンをクリックした場合、このアクティビティ(シングルトップのもの)に移動してビデオを再生できないことです。これを修正するために、私は

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

アクティビティのonBackPressedメソッドで。

3
yincan

この質問は何年も前のものですが、おそらく私の答えは古いAndroidバージョンをサポートしなければならない私のような人々に役立つでしょう。いくつかのAndroidバージョンで動作するさまざまなアプローチを試しましたが、すべてではありませんでした。私が見つけた最良の解決策は、 Crosswalk Webview を使用することです。これはHTML5機能のサポート用に最適化されており、Android 4.1以降で動作します。デフォルトのAndroid WebViewと同じくらい簡単に使用できます。ライブラリを含めるだけです。ここでは、使用方法に関する簡単なチュートリアルを見つけることができます。 https://diego.org/2015/01/07/embedding-crosswalk-in-Android-studio/

2
Apfelsaft

これは非常に古い質問ですが、アプリケーションまたはアクティビティに対してhardwareAccelerated="true"マニフェストフラグを試しましたか?

このセットを使用すると、WebChromeClientを変更しなくても機能するようです(DOM要素に期待します)。

2
Nappy

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);
}
2

ハニカムでhardwareaccelerated=truepluginstate.on_demandを使用すると動作するようです

1
user953501

まあ、どうやらこれは、JNIを使​​用してビデオイベントを取得するプラグインを登録しない限り不可能なようです。 (個人的には、AtomベースのAndroidタブレットが今後数か月で発売され、Javaの移植性が失われたときに混乱に対処したくないので、JNIを避けています。)

唯一の本当の選択肢は、WebView専用の新しいWebページを作成し、上記のCodelarkのURLで引用されているように、A HREFリンクを使用して昔ながらの方法でビデオを作成することです。

イッキー。

1
brian moore

同様の問題がありました。アプリのアセットフォルダーにHTMLファイルとビデオがありました。

したがって、ビデオはAPK内に配置されていました。 APKは実際にはZipファイルであるため、WebViewはビデオファイルを読み取ることができませんでした。

すべてのHTMLファイルとビデオファイルをSDカードにコピーすることができました。

1
RhodanV5500