Отключить WebView в JavaFX, возможно ли это?

Очень просто. Можно ли отключить звук или управлять объемом JavaFX WebView? Некоторое время я искал Google, но я не могу найти упоминания об этом. Я посмотрел на код для WebView и WebEngine и, похоже, ничего не стоит контролировать громкость.

Мне все еще нужен другой MediaPlayer в том же приложении для работы и создания звука, поэтому я не могу отключить все приложение.

Ответ 1

Мы все знаем, что это недостающая часть API, поэтому что вы можете сделать для ее реализации самостоятельно? Подумайте, что такое WebView:

Встроенный компонент браузера основан на WebKit [...] По умолчанию WebKit не поддерживает рендеринг веб-страниц. Чтобы отображать и отображать HTML-контент, Oracle должен был написать собственный рендеринг с использованием Java Graphics 2D API.

Это означает, что двигатель должен иметь реализацию. Реализация WebKit находится в пакете com.sun.webkit (значительная часть классов - это всего лишь оболочка для собственных вызовов). Конечно, в большинстве случаев вы не хотите использовать 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);

C'mon Java, позвольте мне позвонить вам:

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

Вероятно, вы ожидаете рабочего примера, поэтому вот код, который я использовал:

WebEngineTest Preview

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.

Ответ 2

Прямо сейчас нет такого API для звуковой системы в WebView или WebEngine Control.

Но вы можете добавить JavaScript для отключения звука/включения звука и управления звуками. Этот метод работает только с элементами HTML5 <video> или <audio>, потому что такие элементы, как Flash или JS- инициализированный звук, вообще невозможно ограничить. Я создал для вас пример программы:

WebView mute/unmute - program

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

Ответ 3

Чтобы сказать о 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 для отключения звука Video/Audio:

getElementById("Your video Id");

и установите для параметра "Отключение звука":

vid.muted = true;

Ссылка источника