125

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

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

今こすり:WebViewそれが好きではありません。まったく。ポスター画像をクリックしても何も起こりません。

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

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

「サーバーからxmlをプルし、アプリで再フォーマットする」ことはしたくありません。サーバーでストーリーレイアウトを維持することで、ユーザーにアプリの更新を強制することなく、コンテンツを少し適切に制御できます。したがって、ネイティブブラウザのようにタグを処理するようにWebViewを説得できれば、それが最適です。

私は明らかに何か明白なものを見逃しています..しかし、私には何の手がかりがありません。

4

13 に答える 13

94

誰かがそれを読んで結果に興味がある場合に備えて、私はこのトピックに答えます。WebView内でビデオ要素(video 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回目にビデオディスパッチャ(ポスターまたは再生ボタン)をクリックしても、何も起こりません。

また、メディアプレーヤーウィンドウを開くのではなく、WebViewフレーム内でビデオを再生したいのですが、これは私にとって二次的な問題です。

それが誰かに役立つことを願っています。また、コメントや提案にも感謝します。

Saludos、terrícolas。

于 2010-10-07T14:34:02.357 に答える
62

After long research, I got this thing working. See the following code:

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>

Expecting rest of things you can understand.

于 2012-06-06T10:12:45.193 に答える
34

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

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

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

次に、ソースコードを調べて、Androidブラウザがどのように機能するかを確認しました。ページ検索を実行して、それらがどのように処理されるかを確認しますonShowCustomView()。それらは、CustomViewCallbackおよびカスタムビューへの参照を保持します。

これらすべてを考慮し、mdelolmoの回答を念頭に置いて、ビデオが完成したら、2つのことを行う必要があります。まず、VideoView参照を保存した上で、後で他の場所で使用できるようにstopPlayback()を解放する呼び出しを行います。VideoViewのソースコードMediaPlayerで確認できます。次に、を呼び出すための参照を保存しました。CustomViewCallbackCustomViewCallback.onCustomViewHidden()

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

お役に立てば幸いです。

于 2011-01-09T04:48:06.140 に答える
7

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

mWebView.setWebChromeClient(new WebChromeClient());

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

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

ビデオの展開ボタンをインターセプトする方法はありますか?

于 2012-03-09T00:52:13.917 に答える
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();
         }
     }
 }

だから、私が助けてくれることを願っています...私もビデオエンコーディングをいじる必要があり、html5ビデオでWebViewを使用するさまざまな種類を見ました.インターネットと私が自分で理解しなければならなかったいくつかのこと。それは本当にa *の苦痛でした。

于 2011-08-09T12:09:31.393 に答える
4

このアプローチは 2.3 まで非常にうまく機能し、hardwareaccelerated=true を追加することで、3.0 から ICS でも機能します。現在直面している問題の 1 つは、再生を停止してメディア プレーヤーをリリースしていないため、メディア プレーヤー アプリケーションの 2 回目の起動時にクラッシュすることです。3.0 OS から onShowCustomView 関数で取得する VideoSurfaceView オブジェクトは、2.3 OS までのように 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) 

代わりは。

私が遭遇した別の問題は、ビデオの再生中にユーザーが戻るボタンをクリックすると、次回はこのアクティビティ (singleTop 1) に移動してビデオを再生できないことです。これを修正するために、私は

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

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

于 2011-12-16T05:45:29.513 に答える
2

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

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

于 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 を使用してビデオ イベントを取得するためのプラグインを登録しない限り不可能です。(個人的には、Java の移植性を失って、Atom ベースの Android タブレットが今後数か月以内に登場するときに混乱に対処したくないので、JNI を避けています。)

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

イッキー。

于 2010-10-05T18:04:55.977 に答える
1

ハニカムの使用hardwareaccelerated=trueで動作するpluginstate.on_demandようです

于 2012-01-25T23:30:51.820 に答える