125 votes

WebView et HTML5 <video>

Je suis en train d'assembler une application bon marché qui, entre autres choses, "encadre" certains de nos sites web... Plutôt simple avec le WebViewClient . jusqu'à ce que je regarde la vidéo.

La vidéo est réalisée comme HTML5 et ceux-ci fonctionnent parfaitement sur Chrome, les iPhones, et maintenant que nous avons corrigé les problèmes d'encodage, ils fonctionnent parfaitement sur Android dans le navigateur natif.

Maintenant, le hic : WebView n'aime pas ça. Pas du tout. Je peux cliquer sur l'image de l'affiche, et rien ne se passe.

En cherchant sur Google, j'ai trouvé este qui est proche, mais semble être basé sur un 'lien' (comme dans un href...) au lieu d'un élément vidéo. (onDownloadListener ne semble pas être invoqué sur les éléments vidéo...)

Je vois aussi des références à la substitution de onShowCustomView, mais cela ne semble pas être appelé sur les éléments vidéo... ni shouldOverrideUrlLoading...

Je préférerais ne pas me lancer dans le "tirer le xml du serveur, le reformater dans l'application". En gardant la mise en page de l'histoire sur le serveur, je peux contrôler un peu mieux le contenu sans forcer les gens à mettre à jour une application. Donc si je peux convaincre WebView de gérer les balises comme le navigateur natif, ce serait le mieux.

Je rate clairement quelque chose d'évident mais je n'ai aucune idée de quoi.

0 votes

0 votes

Dans le même ordre d'idées, je cherche un lecteur HTML5 pour mon site web. Que dois-je utiliser ?

1 votes

94voto

mdelolmo Points 3190

Je réponds à ce sujet au cas où quelqu'un le lirait et serait intéressé par le résultat. Il est possible de visualiser un élément vidéo (balise html5 vidéo) dans une WebView, mais je dois dire que j'ai dû faire face à ce problème pendant quelques jours. Voici les étapes que j'ai dû suivre jusqu'à présent :

-Trouver une vidéo correctement encodée

-Lors de l'initialisation de la WebView, définissez le JavaScript, les plug-ins du WebViewClient et du 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);

-Gérer le onShowCustomView dans l'objet WebChromeClient.

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

-Gérer les événements onCompletion et onError pour la vidéo, afin de revenir à la vue 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();
}

Mais maintenant je dois dire qu'il y a encore une question importante. Je ne peux la lire qu'une seule fois. La deuxième fois que je clique sur le distributeur de la vidéo (soit le poster, soit un bouton de lecture), cela ne fait rien.

J'aimerais également que la vidéo soit lue à l'intérieur du cadre de la WebView, au lieu d'ouvrir la fenêtre du lecteur multimédia, mais c'est pour moi un problème secondaire.

J'espère que cela aidera quelqu'un, et je remercie également tout commentaire ou suggestion.

Saludos, terrícolas.

0 votes

Je n'ai pas de résultat, mon écran est noir. Je veux savoir si cela a besoin d'un sujet. Mon code est trop long pour être posy ici. pouvez-vous me donner un moyen de contact ou ouvrir un autre sujet.

0 votes

Êtes-vous complètement sûr qu'il ne s'agit pas d'un problème d'encodage ? Essayez avec l'URL que j'ai postée. Il faut évidemment que ça fonctionne avec le navigateur natif

0 votes

Merci, mdelolmo ! cela a fait fonctionner mon onShowCustomView... je vais devoir jouer avec. Notez que l'émulateur (au moins sous Linux) n'aime pas trop lire les vidéos, même les choses qu'un vrai Android lit bien. Et l'encodage est un peu difficile... il a fallu un certain temps pour que les droïdes puissent diffuser leurs vidéos.

62voto

surendra Points 471

Après de longues recherches, j'ai réussi à faire fonctionner ce truc. Voir le code suivant :

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

écran_personnalisé.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>

progression_chargement_vidéo.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>

couleurs.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>

Manifeste.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>

Attendre le reste des choses que vous pouvez comprendre.

7 votes

Celle-ci devrait être la réponse acceptée. Surendra et Malenkiy, vous avez tous deux sauvé ma santé mentale. Ça a bien marché pour moi.

0 votes

Je suis d'accord avec George. C'est exactement la façon dont le navigateur natif d'Android fonctionne avec VideoView. Il semble que ce soit le même code qu'ici code.google.com/p/html5webview . Il fonctionne très bien pour toutes les versions d'Android (j'ai testé sur Android 2.2 et plus). Une seule chose à noter, vous devez garder à l'esprit que si vous changez le SDK cible pour quelque chose de supérieur à 13, votre activité sera recréée lors du changement d'orientation. Pour éviter cela, il faut soit ajouter screenSize à Android:configChanges (mais dans ce cas, vous devrez utiliser min sdk < 13) ou utiliser l'ancien SDK cible (alors vous pouvez conserver Android:configChanges).

2 votes

Cela me donne un écran blanc sur le nexus 7 (Android 4.1). Une idée pour réparer cela ?

34voto

cottonBallPaws Points 5384

La réponse de mdelolmo a été très utile, mais comme il l'a dit, la vidéo n'est lue qu'une fois et il est impossible de la rouvrir.

Je me suis penché un peu sur la question et voici ce que j'ai trouvé, au cas où des voyageurs WebView fatigués comme moi tomberaient sur cet article à l'avenir.

Tout d'abord, j'ai regardé le VidéoView y MediaPlayer et j'ai pu me faire une idée plus précise de leur fonctionnement. Je les recommande vivement.

Ensuite, j'ai regardé le code source pour voir comment le navigateur Android le fait. Faites une recherche de page et allez voir comment ils gèrent onShowCustomView() . Ils conservent une référence à la CustomViewCallback et à la vue personnalisée.

Avec tout cela, et en gardant à l'esprit la réponse de mdelolmo, lorsque vous aurez terminé la vidéo, il vous suffira de faire deux choses. Premièrement, sur le VideoView dont vous avez enregistré une référence, appelez stopPlayback() qui libèrera le MediaPlayer pour être utilisé ultérieurement ailleurs. Vous pouvez le voir dans le VidéoView code source. Deuxièmement, sur le CustomViewCallback vous avez enregistré une référence pour appeler CustomViewCallback.onCustomViewHidden() .

Après avoir fait ces deux choses, vous pouvez cliquer sur la même vidéo ou sur toute autre vidéo et elle s'ouvrira comme avant. Il n'est pas nécessaire de redémarrer l'ensemble du WebView.

J'espère que cela vous aidera.

0 votes

En fait, j'ai aussi résolu le problème, mais je n'ai pas pensé à poster la solution (honte à moi). Comme vous, j'ai dû jeter un coup d'oeil au code du navigateur Android, et bien, pas grand chose à ajouter, j'ai utilisé l'écouteur onCompletion pour arrêter le lecteur à la place et j'ai dû définir la visibilité plusieurs fois, mais j'achèterais votre solution. Salutations !

1 votes

Google Code Search a été retiré, je pense que cette version de BrowserActivity.java (de Froyo) correspond à peu près à celle décrite ici : github.com/Android/platform_packages_apps_browser/blob/

0 votes

Le code lié de @spacemanki ne contient aucune mention de VideoView dans l'ensemble du répertoire. Je suppose que BrowserActivity le fait différemment maintenant. Je suis même retourné à la version Eclair. Je n'ai pas trouvé d'utilisation.

7voto

Russ Schnapp Points 81

En fait, il semble suffisant d'attacher simplement un WebChromeClient à la vue client, à l'instar de

mWebView.setWebChromeClient(new WebChromeClient());

et vous devez avoir activé l'accélération matérielle !

Du moins, si vous n'avez pas besoin de lire une vidéo en plein écran, il n'est pas nécessaire de sortir la VideoView de la WebView et de la pousser dans la vue de l'activité. Elle sera lue dans le rectangle attribué à l'élément vidéo.

Une idée pour intercepter le bouton d'expansion de la vidéo ?

0 votes

Vous pouvez l'intercepter en utilisant WebChromeClient et en surchargeant onShowCustomView

0 votes

Veuillez préciser quelle version d'Android. Cela fonctionne différemment selon les versions.

7voto

A-M Points 61

Je sais que ce fil de discussion date de plusieurs mois, mais j'ai trouvé une solution pour lire la vidéo à l'intérieur du WebView sans le faire en plein écran (mais toujours dans le lecteur multimédia...). Jusqu'à présent, je n'ai pas trouvé d'indication à ce sujet sur Internet, alors peut-être que cela intéressera d'autres personnes. J'ai encore du mal à résoudre certains problèmes (par exemple, placer le lecteur multimédia dans la partie droite de l'écran, je ne sais pas pourquoi je le fais mal, mais c'est un problème relativement mineur, je pense...).

Dans le Custom ChromeClient, spécifiez LayoutParams :

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

Ma méthode onShowCustomView ressemble à ceci :

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

J'espère donc pouvoir vous aider... Moi aussi, j'ai dû jouer avec le codage vidéo et j'ai vu différentes façons d'utiliser le WebView avec la vidéo html5 - à la fin, mon code de travail était un mélange sauvage de différentes parties de code que j'ai trouvées sur Internet et de certaines choses que j'ai dû comprendre par moi-même. C'était vraiment un casse-tête.

0 votes

Sur ICS, getFocusedChild() ne renvoie pas une VideoView. Une idée sur la façon de faire fonctionner cette fonction sur ICS ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X