Nous savons tous que c'est une partie manquante de l'API, alors que pouvez-vous faire pour l'implémenter vous-même ? Réfléchissons à ce qu'est réellement un WebView :
Le composant navigateur embarqué est basé sur WebKit [...] Par défaut, WebKit ne prend pas en charge le rendu des pages Web. Afin de rendre et d'afficher le contenu HTML, Oracle a dû écrire son propre moteur de rendu en utilisant l'API Java Graphics 2D.
Cela signifie que le moteur doit avoir une mise en œuvre. L'implémentation de WebKit se trouve dans com.sun.webkit
paquet (une grande partie des classes ne sont que des enveloppes pour les appels natifs). . Bien sûr, dans la plupart des cas, vous ne voulez pas utiliser com.sun.*
mais comme vous travaillez actuellement avec JavaFX, cela n'a pas vraiment d'importance.
Si nous sautons un peu dans les sources du soleil, nous pouvons trouver WCMediaPlayer.class
avec des méthodes audio abstraites comme :
protected abstract void setRate(float rate);
protected abstract void setVolume(float volume);
protected abstract void setMute(boolean mute);
protected abstract void setSize(int w, int h);
protected abstract void setPreservesPitch(boolean preserve);
Allez Java, laisse-moi t'appeler :
volumeMethod = WCMediaPlayer.class.getDeclaredMethod("setVolume", float.class);
volumeMethod.setAccessible(true);
J'apprécie votre aide mais comment puis-je obtenir WCMediaPlayer
instances ? Nous devons examiner les références à new WCMediaPlayerImpl()
. Je l'ai eu ! WCGraphicsManager.class
crée le MediaPlayer par fwkCreateMediaPlayer()
après tout, elle place le pointeur et l'instance dans le fichier refMap
:
Field refMapField = WCGraphicsManager.class.getDeclaredField("refMap");
refMapField.setAccessible(true);
Heureusement, le manager a exposé getGraphicsManager()
pour obtenir une instance :
WCGraphicsManager graphicsManager = WCGraphicsManager.getGraphicsManager();
refMap = (Map<Integer, Ref>) refMapField.get(graphicsManager);
La carte capturée contient Ref
(il existe également d'autres WC*
), il faut donc les filtrer :
Collection<WCMediaPlayer> mediaPlayers = refMap.values().stream()
.filter(ref -> ref instanceof WCMediaPlayer)
.map(ref -> (WCMediaPlayer) ref)
.collect(Collectors.toList());
Vous vous attendez probablement à un exemple fonctionnel, voici donc le code que j'ai utilisé :
public class WebEngineTest extends Application {
private Map<Integer, Ref> refMap;
private Method volumeMethod;
@Override
@SuppressWarnings("unchecked")
public void start(Stage primaryStage) throws Exception {
WebView webView = new WebView();
WebEngine engine = webView.getEngine();
engine.load("https://www.youtube.com/watch?v=hRAZBSoAsgs");
Field refMapField = WCGraphicsManager.class.getDeclaredField("refMap");
refMapField.setAccessible(true);
volumeMethod = WCMediaPlayer.class.getDeclaredMethod("setVolume", float.class);
volumeMethod.setAccessible(true);
WCGraphicsManager graphicsManager = WCGraphicsManager.getGraphicsManager();
refMap = (Map<Integer, Ref>) refMapField.get(graphicsManager);
Button button = new Button("Volume");
button.setOnAction(event -> setVolume(0.1f));
Group group = new Group();
group.getChildren().addAll(webView, button);
Scene scene = new Scene(group, 625, 625);
primaryStage.setScene(scene);
primaryStage.show();
}
public void setVolume(float volume) {
Collection<WCMediaPlayer> mediaPlayers = this.getMediaPlayers();
mediaPlayers.forEach(mediaPlayer -> setVolumeMethod(mediaPlayer, volume));
}
private void setVolumeMethod(Object instance, Object... args) {
try {
volumeMethod.invoke(instance, args);
} catch (Exception e) {
e.printStackTrace();
}
}
private Collection<WCMediaPlayer> getMediaPlayers() {
return refMap.values().stream()
.filter(ref -> ref instanceof WCMediaPlayer)
.map(ref -> (WCMediaPlayer) ref)
.collect(Collectors.toList());
}
}
Enfin, n'oubliez pas l'ordre de ces appels. Par exemple, refMap
ne contient pas tous les Refs
jusqu'à ce que l'état du moteur ne soit pas SUCCEEDED
o WCGraphicsManager.getGraphicsManager()
renvoie à null
si l'élément graphique n'est pas créé du tout.
Le meilleur moyen est probablement de combiner ces solutions. Il est difficile de supporter un mélange de technologies web sans scenerio et l'API pauvre fournie par JavaFX. Vous pouvez également essayer d'intégrer un autre navigateur, comme Chromium.