Je suis en train de reconstituer une application cheapo qui, entre autres choses, "encadre" certains de nos sites Web ... Plutôt simple avec le WebViewClient
. jusqu'à ce que je frappe la vidéo.
La vidéo est réalisée sous la forme d'éléments HTML5
, et ceux-ci fonctionnent parfaitement sous Chrome, sur les iPhones. Maintenant que nous avons corrigé les problèmes d'encodage, cela fonctionne parfaitement sous Android
dans le navigateur natif.
Maintenant, le frotter: WebView
ne l'aime pas. Du tout. Je peux cliquer sur l'image de l'affiche et rien ne se passe.
En cherchant sur Google, j'ai trouvé this 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 des é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 ne devrait le faire par-dessus.Le chargement ..
Je préférerais ne pas entrer dans "extraire le XML du serveur, le reformater dans l'application" ... en gardant la présentation de l'histoire sur le serveur, je peux contrôler le contenu un peu mieux sans obliger les gens à mettre à jour une application. Donc, si je peux convaincre WebView de gérer des balises telles que le navigateur natif, ce serait mieux.
Il me manque clairement quelque chose d’évident ... mais je ne sais pas quoi.
Je réponds à ce sujet juste au cas où quelqu'un le lirait et serait intéressé par le résultat . Il est possible d'afficher un élément vidéo (balise video html5) dans une vue Web, mais je dois dire que j'ai dû 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 le JavaScript, les plug-ins WebViewClient et 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);
-Handle 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();
}
}
}
-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 devrais dire qu'il y a encore un problème important. Je ne peux jouer qu'une fois. La deuxième fois que je clique sur le répartiteur vidéo (l'affiche ou un bouton de lecture), cela ne fait rien.
J'aimerais également que la vidéo soit lue dans le cadre WebView au lieu d'ouvrir la fenêtre de Media Player, mais il s'agit pour moi d'un problème secondaire.
J'espère que cela aide quelqu'un et je remercie également tout commentaire ou suggestion.
Saludos, terrícolas.
Après de longues recherches, cette chose a fonctionné. 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);
}
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>
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>
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>
Attendre le reste de choses que vous pouvez comprendre.
la réponse de mdelolmo a été incroyablement utile, mais comme il l'a dit, la vidéo n'est lue qu'une fois et vous ne pouvez plus l'ouvrir à nouveau.
J'ai un peu examiné la situation et voici ce que j'ai trouvé, au cas où des voyageurs WebView fatigués comme moi tomberaient sur ce message à l'avenir.
Tout d’abord, j’ai jeté un œil à la documentation de VideoView et MediaPlayer et j’ai pu mieux comprendre leur fonctionnement. Je les recommande fortement.
Ensuite, j'ai regardé le code source pour voir comment le navigateur Android le fait. Faites une page et cherchez comment ils gèrent onShowCustomView()
. Ils gardent une référence à la CustomViewCallback
et à la vue personnalisée.
Avec tout cela, et avec la réponse de mdelolmo en tête, lorsque vous avez terminé avec la vidéo, tout ce que vous avez à faire est de faire deux choses. Tout d'abord, sur la VideoView
à laquelle vous avez enregistré une référence, appelez stopPlayback()
qui libérera la MediaPlayer
pour qu'elle soit utilisée ultérieurement ailleurs. Vous pouvez le voir dans le code source VideoView . Deuxièmement, sur la CustomViewCallback
, vous avez enregistré une référence à l'appel CustomViewCallback.onCustomViewHidden()
.
Après avoir fait ces deux choses, vous pouvez cliquer sur la même vidéo ou sur une autre vidéo et celle-ci s'ouvrira comme avant. Pas besoin de redémarrer la totalité de WebView.
J'espère que cela pourra aider.
En fait, il semble suffisant de simplement attacher un stock WebChromeClient à la vue client,
mWebView.setWebChromeClient(new WebChromeClient());
et vous devez activer 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 d'extraire VideoView de WebView et de l'insérer dans la vue Activité. Il sera lu dans le recto attribué à l'élément vidéo.
Des idées comment intercepter le bouton de développement de la vidéo?
Je sais que ce fil a plusieurs mois, mais j'ai trouvé une solution pour lire la vidéo dans WebView sans la faire en plein écran (mais toujours dans le lecteur multimédia ...). Jusqu’à présent, je n’avais trouvé aucune indication à ce sujet sur Internet, c’est peut-être aussi intéressant pour d’autres . Je suis toujours aux prises avec certaines difficultés (par exemple, placer le lecteur multimédia dans la partie droite de l’écran, don Je ne sais pas pourquoi je me trompe mais c’est un problème relativement modeste, je pense…).
Dans le 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();
}
}
}
J'espère donc pouvoir vous aider ... moi aussi, j'ai dû jouer avec l'encodage vidéo et j'ai vu différents types d'utilisation de WebView avec la vidéo html5 - mon code de travail était à la fin un mélange sauvage de différentes parties de code que j'ai trouvées dans Internet et certaines choses que je devais comprendre par moi-même. C'était vraiment pénible à la *.
Cette approche fonctionne très bien jusqu’à la version 2.3 Et en ajoutant hardwareaccelerated = true, elle fonctionne même de la version 3.0 à l’ICS. lecteur multimédia publié . En tant qu’objet VideoSurfaceView, que nous obtenons dans la fonction onShowCustomView à partir de la version 3.0, sont spécifiques au navigateur et non à un objet VideoView tel que, jusqu’à la version 2.3.
A-M 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 que si la vidéo est en cours de lecture et que l'utilisateur clique sur le bouton Précédent, la prochaine fois, vous accédez à cette activité (singleTop one) et 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é.
Cette question a plusieurs années, mais ma réponse aidera peut-être des personnes comme moi qui doivent supporter l'ancienne version d'Android. J'ai essayé beaucoup d'approches différentes qui fonctionnaient sur certaines versions d'Android, mais pas sur toutes. La meilleure solution que j'ai trouvée consiste à utiliser Crosswalk Webview , optimisé pour la prise en charge des fonctionnalités HTML5 et fonctionnant sous Android 4.1 et versions ultérieures. Il est aussi simple à utiliser que la WebView Android par défaut. Vous devez juste inclure la bibliothèque. Ici vous pouvez trouver un tutoriel simple sur son utilisation: https://diego.org/2015/01/07/embedding-crosswalk-in-Android-studio/
Je sais que la question est très ancienne, mais avez-vous essayé l'indicateur de manifeste hardwareAccelerated="true"
pour votre application ou activité?
Avec cet ensemble, il semble fonctionner sans aucune modification de WebChromeClient (ce que j'attendrais d'un élément DOM.)
J'ai utilisé html5webview pour résoudre ce problème.Download et le mettre dans votre projet, alors vous pouvez coder comme ça.
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);
}
Pour rendre la vidéo rotative, insérez le code Android: configChanges = "orientation" dans votre activité Par exemple (Androidmanifest.xml)
<activity Android:name=".ui.HTML5Activity" Android:configChanges="orientation"/>
et substituer la méthode onConfigurationChanged.
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
hardwareaccelerated=true
et pluginstate.on_demand
semblent fonctionner en nid d'abeille
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, cela n’est tout simplement pas possible sans utiliser un JNI pour enregistrer un plugin afin d’obtenir l’événement vidéo. (Personnellement, j'évite les JNI car je ne veux vraiment pas m'inquiéter lorsque des tablettes Android basées sur Atom sortent dans les prochains mois, perdant la portabilité de Java.)
La seule alternative réelle semble être de créer une nouvelle page Web réservée à WebView et de faire de la vidéo à l'ancienne, avec un lien A HREF cité dans l'URL Codelark ci-dessus.
Icky.