简体   繁体   English

JavaFX webview全局CSS黑暗主题

[英]JavaFX webview global CSS dark theme

Using JavaFX webview, I'm changing several html attributes (background, border, and color) for all website DOM tags globally. 使用JavaFX webview,我正在全局更改所有网站DOM标记的几个html属性(背景,边框和颜色)。 My intent is to have a customized dark theme that performs much like a high contrast settings would. 我的目的是拥有一个定制的黑暗主题,其表现与高对比度设置非常相似。 The only problem I can't resolve is the slight delay the code in the last method setWebpageTheme(Boolean succeeded) produces. 我无法解决的唯一问题是最后一个方法setWebpageTheme(布尔成功)产生的代码略有延迟。 It results in a bright white flicker as the html css attributes are applied and change the white to dark page backgrounds. 当应用html css属性时,它会产生明亮的白色闪烁,并将白色变为黑色页面背景。 Please see the complete code below. 请参阅下面的完整代码。

Main Class: 主类:

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    public Parent createContent() {
        final WebBrowser browser = new WebBrowser();
        return browser;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setResizable(true);
        Scene scene = new Scene(createContent());
        primaryStage.setTitle("Eric's Web Demo");
        scene.getStylesheets().add("style/template.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Web-browser GUI: Web浏览器GUI:

import org.w3c.dom.Attr;
import org.w3c.dom.NodeList;

import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.geometry.Dimension2D;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebHistory;
import javafx.scene.web.WebHistory.Entry;
import javafx.scene.web.WebView;

public class WebBrowser extends BorderPane {

    private static final Dimension2D DIM = new Dimension2D(1220, 680);
    private final WebView webView;
    private final WebEngine webEngine;
    private final ComboBox<String> addressBox;
    private final TextField searchField;
    private final Button backButton;
    private final Button forwardButton;
    private final Button proceedButton;
    private final Button searchButton;
    private final ProgressBar progressBar;
    private final Label statusLabel;

    public WebBrowser() {

        this.setMinSize(DIM.getWidth(), DIM.getHeight());
        this.setPrefSize(DIM.getWidth(), DIM.getHeight());

        backButton = new Button("\uD83E\uDC78");
        backButton.setOnAction(this::backButtonListener);

        forwardButton = new Button("\u2794");
        forwardButton.setDefaultButton(true);
        forwardButton.setOnAction(this::forwardButtonListener);

        proceedButton = new Button("Go");
        proceedButton.setOnAction(this::proceedButtonListener);

        final HBox buttonGroup = new HBox();
        buttonGroup.setSpacing(5);
        buttonGroup.setPadding(new Insets(10, 5, 10, 5));
        buttonGroup.getChildren().addAll(backButton, forwardButton, proceedButton);

        addressBox = new ComboBox<String>();
        addressBox.setItems(FXCollections.observableArrayList());
        addressBox.setValue("http://stackoverflow.com/questions/32783532/applying-css-file-to-javafx-webview");
        addressBox.setOnAction(this::proceedButtonListener);
        addressBox.setEditable(true);
        addressBox.setMaxWidth(Double.MAX_VALUE);

        searchField = new TextField();
        searchField.setPromptText("\uD83D\uDD0D Search");

        searchButton = new Button("\uD83D\uDD0D");
        searchButton.setDefaultButton(true);
        searchButton.setOnAction(this::searchButtonListener);

        statusLabel = new Label("Status: ");
        progressBar = new ProgressBar(0);   

        webView = new WebView();
        webEngine = webView.getEngine();
        webEngine.load(addressBox.getValue());
        webEngine.getLoadWorker().stateProperty().addListener(this::stateChangeListener);
        webEngine.locationProperty().addListener(this::urlChangeListener);
        progressBar.progressProperty().bind(webEngine.getLoadWorker().progressProperty());

        final WebHistory history = webEngine.getHistory();
        history.getEntries().addListener(this::historyListener);

        final GridPane root = new GridPane();
        GridPane.setConstraints(buttonGroup,  0, 0, 1, 1, HPos.LEFT,   VPos.CENTER, Priority.NEVER,  Priority.NEVER);
        GridPane.setConstraints(addressBox,   1, 0, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.NEVER);
        GridPane.setConstraints(searchField,  2, 0, 1, 1, HPos.RIGHT,  VPos.CENTER, Priority.NEVER,  Priority.NEVER);
        GridPane.setConstraints(searchButton, 3, 0, 1, 1, HPos.RIGHT,  VPos.CENTER, Priority.NEVER,  Priority.NEVER);
        GridPane.setConstraints(webView,      0, 1, 4, 1, HPos.LEFT,   VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
        GridPane.setConstraints(statusLabel,  0, 2, 1, 1, HPos.LEFT,   VPos.CENTER, Priority.NEVER,  Priority.NEVER);
        GridPane.setConstraints(progressBar,  3, 2, 3, 1, HPos.RIGHT,  VPos.CENTER, Priority.NEVER,  Priority.NEVER);
        GridPane.setMargin(addressBox,   new Insets(5, 0, 5, 0));
        GridPane.setMargin(searchField,  new Insets(5, 5, 5, 5));
        GridPane.setMargin(searchButton, new Insets(5, 8, 5, 0));
        GridPane.setMargin(statusLabel,  new Insets(5, 0, 5, 5));
        GridPane.setMargin(progressBar,  new Insets(5, 5, 5, 5));
        root.addRow(0, buttonGroup, addressBox, searchField, searchButton);
        root.addRow(1, webView); 
        root.addRow(2,statusLabel, progressBar);

        this.setCenter(root);       
    }  

    public void historyListener(Change<? extends Entry> changeValue) {
        changeValue.next();
        for (Entry entry : changeValue.getRemoved()) {
            addressBox.getItems().remove(entry.getUrl());
            System.out.print("Removed url: ");
            System.out.println(entry.getUrl());
        }
        for (Entry entry : changeValue.getAddedSubList()) {
            System.out.print("Added url: ");
            addressBox.getItems().add(entry.getUrl());
            System.out.println(entry.getUrl());
        }
    }

    public void progressBarListener(ObservableValue<? extends Number> ov, Number old_val, Number new_val) {
        progressBar.setProgress(new_val.doubleValue());
    }

    private void stateChangeListener(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {

        setWebpageTheme(newValue == State.SUCCEEDED);
        String output = newValue.toString().toLowerCase();
        statusLabel.setText("Status: " + output);
    }

    private void urlChangeListener(ObservableValue<? extends String> observable, String oldValue, String newValue) {
        addressBox.setValue(newValue);
    }

    public void forwardButtonListener(ActionEvent event) {
        webEngine.executeScript("history.forward()");
    }

    private void backButtonListener(ActionEvent event) {
        webEngine.executeScript("history.back()");
    }

    private void searchButtonListener(ActionEvent event) {
        String google = "http://www.google.com/search?q=" + searchField.getText();
        webEngine.load(google.startsWith("http://") || google.startsWith("https://") 
                ? google : "http://" + google);
    }

    private void proceedButtonListener(ActionEvent event) {
        String url = addressBox.valueProperty().getValue();
        webEngine.load(url.startsWith("http://") || url.startsWith("https://") 
                ? url : "http://" + url);
    }

    private void setWebpageTheme(Boolean succeeded) {
        // Can safely access DOM and set styles.
        if (succeeded == true) {
            // This gives the DOM Document for the web page.
            NodeList htmlTags = webEngine.getDocument().getElementsByTagName("*");
            Attr newAttr = null;
            for (int i = 0; i < htmlTags.getLength(); i++) {
                newAttr = webEngine.getDocument().createAttribute("style");
                newAttr.setValue("background-color: #222; border-color: #333; background: #222; color: #bbb; ");
                htmlTags.item(i).getAttributes().setNamedItem(newAttr);
            }
        }
    }
}

Text file-path name: /style/template.css < -fx GUI settings (not for web-pages) 文本文件路径名:/style/template.css <-fx GUI设置(不适用于网页)

.root{
   -fx-background: rgb(44,44,44);
}

.button {
   -fx-border-radius: 5;
   -fx-background-radius: 5;
   -fx-min-height: 32;
   -fx-min-width: 40;
   -fx-background-color: radial-gradient(radius 100%, rgb(22, 33, 188), rgb(3,22,122));
   -fx-text-fill: rgb(196,188,222);
}

TextField {
   -fx-border-radius: 5;
   -fx-background-radius: 5;
   -fx-max-height: 32;
   -fx-min-width: 280;
   -fx-border-color: #555;
   -fx-border-width: 1 1 1 1;
   -fx-background-color: #333;
   -fx-text-fill: rgb(196,188,222);
}

.combo-box-base {
   -fx-border-radius: 5;
   -fx-background-radius: 5;
   -fx-min-height: 35;
   -fx-background-color: #333;
   -fx-border-color: transparent;
   -fx-border-width: 2 2 2 2;
}

.combo-box-base .arrow-button {
    -fx-background-color: radial-gradient(radius 100%, rgb(22, 33, 188), rgb(3,22,122));
}

.combo-box .combo-box-popup, .list-view, .list-cell {
    -fx-background-color:  #333;
    -fx-text-fill: rgb(155,188,166);
}

.combo-box .text-input {
    -fx-border-radius: 5;
    -fx-background-radius: 5;
    -fx-background-color: #333;
    -fx-border-color: #555;
    -fx-border-width: 1 1 1 1;
    -fx-text-fill: rgb(155,188,166);
}

.scroll-bar {
    -fx-background-color: #222;
}

.scroll-bar .thumb {
    -fx-background-color: radial-gradient(radius 100%, rgb(22, 33, 188), rgb(3,22,122));
}

.label {
    -fx-font: 14px "Arial";
    -fx-border-color: rgb(57, 58, 59);
    -fx-border-width: 2 2 2 2;
    -fx-text-fill: rgb(155,188,166);
}

.progress-bar > .track {
  -fx-text-box-border: rgb(44, 44, 44);
  -fx-control-inner-background: rgb(22, 22, 44);
}

I've come up with a solution to minimize the impact of the dark background delay or to be specific, the inefficiency in which I'm adding attributes to all DOM html tags. 我想出了一个解决方案,以最大限度地减少黑暗背景延迟的影响,或具体来说,我正在为所有DOM html标签添加属性的低效率。 I've added the below html stylesheet using the following code prior to invoking the method setWebpageTheme(boolean succeed); 在调用方法setWebpageTheme(boolean succeed)之前,我使用以下代码添加了下面的html样式表; It's not 100% perfect but it's bearable for my purposes. 它并非100%完美,但它对我的目的来说是可以忍受的。 If someone finds a better solution, I'm still interested. 如果有人找到更好的解决方案,我仍然感兴趣。

    webEngine.setUserStyleSheetLocation(getClass().getResource("/style/style.css").toString());

style.css style.css文件

*, a, abbr, acronym, address, applet, b, big, blockquote, body, br, button, caption, center, cite, code, dd, del, dfn, div,
dl, dt, element, em, em, fieldset, font, form, h1, h2, h3, h4, h5, h6, head, header, html, html, i, iframe, iframe, img, 
img, input, ins, kbd, label, label, legend, li, link, meta, nav, noscript, object, ol, ol, p, path, pre, q, s, samp, script,
small, span, strike, strong, style, sub, sup, svg, table, tbody, td, textarea, tfoot, th, thead, title, tr, tt, u, ul, var { 
    background-color: #222; border-color: #333; background: #222; color: #bbb; 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM