WebView et HTML5

Je reconstitue une application pas chère qui, entre autres, “encadre” certains de nos sites Web … Assez simple avec WebViewClient . jusqu’à ce que je frappe la vidéo.

La vidéo est faite en tant qu’éléments HTML5 , et ceux-ci fonctionnent parfaitement sur Chrome, iPhones, et maintenant que nous avons corrigé les problèmes d’encodage, cela fonctionne parfaitement sur Android dans le navigateur natif.

Maintenant, le frottement: WebView n’aime pas ça. Du tout. Je peux cliquer sur l’image de l’affiche et rien ne se passe.

Googling, j’ai trouvé ce 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 appelé sur les éléments vidéo …)

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

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

Je manque clairement quelque chose d’évident. Mais je n’ai aucune idée de quoi.

Je réponds à ce sujet au cas où quelqu’un le lirait et serait intéressé par le résultat. Il est possible de voir un élément vidéo (balise video html5) dans une WebView, mais je dois dire que je dois le gérer pendant quelques jours. Ce sont les étapes que j’ai dû suivre jusqu’à présent:

-Trouver une vidéo correctement encodée

-Lors de l’initialisation de WebView, définissez JavaScript, les plug-ins, WebViewClient et WebChromeClient.

 url = new Ssortingng ("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);

-Handle onShowCustomView dans l’object 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(); } } } 

-Handle 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 ya encore un problème important. Je ne peux y jouer qu’une fois. La deuxième fois que je clique sur le répartiteur vidéo (affiche ou bouton de lecture), il ne fait rien.

Je voudrais aussi que la vidéo soit diffusée dans le cadre de 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 voudrais également remercier tout commentaire ou suggestion.

Saludos, terrícolas.

Après de longues recherches, j’ai fait fonctionner cette chose. 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.AtsortingbuteSet; 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 Ssortingng 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.isorting.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, AtsortingbuteSet attrs) { super(context, attrs); init(context); } public HTML5WebView(Context context, AtsortingbuteSet 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, Ssortingng 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(Ssortingng 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

          

video_loading_progress.xml

       

colors.xml

     #ffffffff #ff000000 #ffffffff #ff000000 #ffffffff #ffffffff #ffffffff #ff000000 #ffdddddd  

Manifest.xml

                    

Attendre le rest des choses que vous pouvez comprendre.

La réponse de mdelolmo a été incroyablement utile, mais comme il l’a dit, la vidéo ne joue qu’une seule fois et vous ne pouvez plus l’ouvrir.

Je me suis penchée là-dessus et voici ce que j’ai trouvé, au cas où des voyageurs WebView fatigués comme moi trébucheraient sur ce post dans le futur.

Tout d’abord, j’ai consulté la documentation de VideoView et de MediaPlayer et j’ai mieux compris comment cela fonctionnait. Je les recommande fortement

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

Avec tout cela, et avec la réponse de mdelolmo en tête, quand vous avez fini avec la vidéo, tout ce que vous avez à faire est de deux choses. Tout d’abord, sur VideoView auquel vous avez enregistré une référence, appelez stopPlayback() qui libère le MediaPlayer à utiliser ultérieurement ailleurs. Vous pouvez le voir dans le code source de VideoView . Deuxièmement, sur 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 celle-ci s’ouvrira comme avant. Pas besoin de redémarrer l’intégralité de WebView.

J’espère que cela pourra aider.

En fait, il semble suffisant de simplement attacher un stock WebChromeClient à la vue client, ala

 mWebView.setWebChromeClient(new WebChromeClient()); 

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

Au moins, si vous n’avez pas besoin de lire une vidéo en plein écran, il n’est pas nécessaire de sortir VideoView de WebView et de le placer dans la vue de l’activité. Il jouera dans le recto de l’élément vidéo.

Des idées sur la façon d’intercepter le bouton d’extension vidéo?

Je sais que ce sujet a plusieurs mois, mais j’ai trouvé une solution pour lire la vidéo dans WebView sans le faire en plein écran (mais toujours dans le lecteur multimédia …). Jusqu’à présent, je n’ai trouvé aucun indice à ce sujet sur Internet, alors peut-être est-ce aussi intéressant pour les autres. Je suis toujours aux sockets avec certains problèmes (c.-à-d. Placer le lecteur multimédia dans la section droite de l’écran, je ne sais pas pourquoi je le fais mal, mais je pense que c’est un problème relativement petit …).

Dans ChromeClient personnalisé, 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(); } } } 

Donc, j’espère que je pourrais aider… Je devais aussi jouer avec l’encodage vidéo et voir différents types d’utilisation de WebView avec la vidéo html5 – à la fin, mon code de travail était un mélange sauvage de différentes parties de code trouvées dans Internet et certaines choses que je devais découvrir par moi-même. C’était vraiment une douleur dans le a *.

Cette approche fonctionne très bien jusqu’à 2.3 Et en ajoutant hardwareaccelerated = true cela fonctionne même de 3.0 à ICS Un problème auquel je suis confronté actuellement lors du deuxième lancement de l’application de lecteur multimédia se bloque parce que je n’ai pas arrêté la lecture et sorti Media player. En tant qu’object VideoSurfaceView, que nous obtenons dans la fonction onShowCustomView à partir de 3.0 OS, sont spécifiques au navigateur et non à un object VideoView comme dans, jusqu’à 2.3 OS Comment puis-je y accéder et arrêterPlayback et libérer des ressources?

AM est similaire à ce que fait BrowerActivity . pour FrameLayout.LayoutParams LayoutParameters = new FrameLayout.LayoutParams (768, 512);

Je pense que nous pouvons utiliser

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

au lieu.

Un autre problème que j’ai rencontré est le suivant: si la vidéo est en cours de lecture et que l’utilisateur clique sur le bouton Retour, la prochaine fois, vous accédez à cette activité (une seule au maximum) et vous ne pouvez pas lire la vidéo. pour résoudre ce problème, j’ai appelé le

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

dans la méthode onBackPressed de l’activité.

Je sais que c’est une très vieille question, mais avez-vous essayé l’indicateur de manifeste hardwareAccelerated="true" pour votre application ou votre activité?

Avec cet ensemble, il semble fonctionner sans aucune modification WebChromeClient (ce à quoi je m’attendrais d’un élément DOM.)

J’ai utilisé html5webview pour résoudre ce problème. Téléchargez-le et insérez-le dans votre projet, vous pouvez alors coder comme ceci.

 private HTML5WebView mWebView; Ssortingng 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); } 

Pour rendre la vidéo rotative, placez le code Android: configChanges = “orientation” dans votre activité par exemple (Androidmanifest.xml)

  

et remplacer la méthode onConfigurationChanged.

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

Cette question a plusieurs années, mais peut-être que ma réponse aidera des gens comme moi qui doivent supporter une ancienne version d’Android. J’ai essayé beaucoup d’approches différentes qui fonctionnaient sur certaines versions d’Android, mais pas sur tous. La meilleure solution que j’ai trouvée consiste à utiliser Crosswalk Webview, qui est optimisé pour la prise en charge des fonctionnalités HTML5 et fonctionne sous Android 4.1 et les versions ultérieures. Il est aussi simple à utiliser que le WebView Android par défaut. Il suffit d’inclure la bibliothèque. Ici vous pouvez trouver un tutoriel simple sur comment l’utiliser: https://diego.org/2015/01/07/embedding-crosswalk-in-android-studio/

En nid d’abeille, utilisez hardwareaccelerated=true et pluginstate.on_demand semble fonctionner

J’avais un problème similaire. J’avais des fichiers HTML et des vidéos dans le dossier assets de mon application.

Par conséquent, les vidéos étaient situées à l’intérieur de l’APK. Parce que l’APK est vraiment un fichier ZIP, WebView n’a pas pu lire les fichiers vidéo.

Copier tous les fichiers HTML et vidéo sur la carte SD a fonctionné pour moi.

Eh bien, apparemment, ce n’est simplement pas possible sans utiliser un JNI pour enregistrer un plugin pour obtenir l’événement vidéo. (Personnellement, j’évite les JNI car je ne veux vraiment pas faire face à un désordre lorsque des tablettes Android basées sur Atom sortiront dans les prochains mois, perdant la portabilité de Java.)

La seule vraie alternative semble être de créer une nouvelle page Web uniquement pour WebView et de faire de la vidéo à l’ancienne avec un lien A HREF cité dans l’URL Codelark ci-dessus.

Icky.