Отключить WebView в JavaFX, это возможно?
Очень просто. Можно ли отключить или контролировать громкость JavaFX WebView? Некоторое время я гуглил, но не могу найти упоминаний об этом. Я посмотрел на код для WebView
а также WebEngine
и, кажется, ничего не происходит в управлении громкостью.
Мне все еще нужны другие MediaPlayer
Он работает в одном приложении и производит звук, поэтому я не могу отключить звук всего приложения.
3 ответа
Мы все знаем, что это недостающая часть API, так что вы можете сделать, чтобы реализовать его самостоятельно? Давайте подумаем, что такое WebView:
Компонент встроенного браузера основан на WebKit [...]. По умолчанию WebKit не поддерживает рендеринг веб-страниц. Чтобы визуализировать и отображать HTML-контент, Oracle должен был написать собственный рендер, используя Java Graphics 2D API.
Это означает, что двигатель должен иметь реализацию. Реализация WebKit находится в com.sun.webkit
package (значительная часть классов - это просто оболочка для нативных вызовов). Конечно, в большинстве случаев вы не хотите использовать com.sun.*
классы, но в настоящее время вы работаете с JavaFX, так что это не имеет значения.
Если мы немного прыгнем в источниках солнца, мы можем найти WCMediaPlayer.class
с некоторыми абстрактными аудио методами, такими как:
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);
Давай, Ява, позволь мне позвонить тебе:
volumeMethod = WCMediaPlayer.class.getDeclaredMethod("setVolume", float.class);
volumeMethod.setAccessible(true);
Я ценю вашу помощь, но как я могу получить WCMediaPlayer
случаи? Мы должны смотреть на ссылки на new WCMediaPlayerImpl()
, Попался! WCGraphicsManager.class
создает MediaPlayer fwkCreateMediaPlayer()
метод, в конце концов, он помещает указатель и экземпляр в refMap
:
Field refMapField = WCGraphicsManager.class.getDeclaredField("refMap");
refMapField.setAccessible(true);
К счастью, менеджер выставил getGraphicsManager()
способ получить экземпляр:
WCGraphicsManager graphicsManager = WCGraphicsManager.getGraphicsManager();
refMap = (Map<Integer, Ref>) refMapField.get(graphicsManager);
Загруженная карта содержит Ref
экземпляры (есть и другие WC*
экземпляры), поэтому вы должны отфильтровать их:
Collection<WCMediaPlayer> mediaPlayers = refMap.values().stream()
.filter(ref -> ref instanceof WCMediaPlayer)
.map(ref -> (WCMediaPlayer) ref)
.collect(Collectors.toList());
Вы, вероятно, ожидаете работающий пример, поэтому вот код, который я использовал:
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());
}
}
Наконец, помните о порядке этих звонков. Например, refMap
не содержит все Refs
пока состояние двигателя не SUCCEEDED
или же WCGraphicsManager.getGraphicsManager()
возвращается null
если графический элемент вообще не создан.
Вероятно, лучший способ - объединить эти решения. Трудно поддерживать сочетание веб-технологий без scenerio и плохого API, предоставляемого JavaFX. Вы также можете попробовать встроить другой браузер, например, Chromium.
В настоящее время нет такого API для звуковой системы в WebView или WebEngine Control.
Но вы можете добавить JavaScript для отключения / включения звука и управления звуками. Этот метод работает только с элементами HTML5 или , потому что такие элементы, как инициализированное аудио Flash или JS, вообще невозможно ограничить. Я создал пример программы для вас:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
WebView myWebView = new WebView();
WebEngine engine = myWebView.getEngine();
//dirty code xD
Button btn = new Button("Load Youtube");
btn.setOnAction((ActionEvent event) -> engine.load("http://www.youtube.com/embed/A6hrw6KGdtM?autoplay=1"));
Button btn2 = new Button("Mute");
btn2.setOnAction((ActionEvent event) -> engine.executeScript(getJSAudioVideo(true)));
Button btn3 = new Button("Unmute");
btn3.setOnAction((ActionEvent event) -> engine.executeScript(getJSAudioVideo(false)));
Button btn4 = new Button("+");
btn4.setOnAction((ActionEvent event) -> engine.executeScript(getJSSoundVolume(0.9)));
Button btn5 = new Button("-");
btn5.setOnAction((ActionEvent event) -> engine.executeScript(getJSSoundVolume(0.1)));
VBox root = new VBox();
root.getChildren().addAll(myWebView, btn, btn2, btn3, btn4, btn5);
Scene scene = new Scene(root, 800, 500);
primaryStage.setScene(scene);
primaryStage.setTitle("Mute a WebView in JavaFX, is it possible?");
primaryStage.show();
}
/**
* @param mute
* @return JS code for mute/unmute
*/
public String getJSAudioVideo(boolean mute){
return "var videos = document.querySelectorAll('video'),\n" +
" audios = document.querySelectorAll('listen');\n" +
" [].forEach.call(videos, function(video) { video.muted = "+String.valueOf(mute)+"; });\n" +
" [].forEach.call(audios, function(audio) { audio.muted = "+String.valueOf(mute)+"; });";
}
/**
* @param volume
* @return JS code for setting volume sound
*/
public String getJSSoundVolume(double volume){
//max 1.0, min 0.0 Default: 1.0
double _volume = (volume > 1.0 || volume < 0.0) ? 1.0 : volume;
return "var videos = document.querySelectorAll('video'),\n" +
" audios = document.querySelectorAll('listen');\n" +
" [].forEach.call(videos, function(video) { video.volume = "+String.valueOf(_volume)+"; });\n" +
" [].forEach.call(audios, function(audio) { audio.volume = "+String.valueOf(_volume)+"; });";
}
public static void main(String[] args) {
launch(args);
}
}
Чтобы сказать о WebView, это расширение класса Node. Он инкапсулирует объект WebEngine, включает HTML-контент в сцену приложения и предоставляет свойства и методы для применения эффектов и преобразований. Метод getEngine(), вызываемый для объекта WebView, возвращает связанный с ним веб-движок. Источник
Так что WebView сейчас не поддерживает API для отключения звука / управления громкостью. Существует API для отключения контента, загруженного в MediaPlayer (т. Е.,)
Чтобы получить статус muteProperty: ссылка на источник
public BooleanProperty muteProperty()
Чтобы проверить условие отключения звука: Ссылка на источник
public final boolean isMute()
Для отключения звука видео установите значение true: ссылка на источник
public final void setMute(boolean value)
ИЛИ в противном случае используйте JavaScript API для отключения звука видео / аудио:
getElementById("Your video Id");
и установите контроль на Mute:
vid.muted = true;