JavaFX Subklasse von WebView

breakpoint

Mitglied
Die Klasse javafx.scene.web.WebView ist final und kann darum nicht erweitert werden.

Gibt es evtl. einen Trick, um trotzdem das hier zu erreichen:
Code:
class MeinWebView extends javafx.scene.web.WebView {
  MeinWebView() {
    super();
  }
  public void eigeneFunktionFuerWebView() {
  }
}

MeinWebView webview = new MeinWebView();
webview.eigeneFunktionFuerWebView();

Oder ist die einzige Lösung, ein andere Klasse zu erweitern und dieser
dann WebView als Child-Node hinzuzufügen?

Danke für eure Hilfe.
 
Zuletzt bearbeitet:

dzim

Top Contributor
Was genau ist denn deine Absicht? Der WebView ist wahrscheinlich vor allem deswegen final, weil er im Hintergrund auf WebKit aufsetzt und man wohl verhindern wollte, das man zu viel "Unsinn" damit treiben kann. Aber das ist nur eine Vermutung.

Also noch mal: Was möchtest du mit deiner eigenen Methode erreichen?
 

breakpoint

Mitglied
Vielen Dank für deine Antwort.
Es geht nicht um eine einzige eigene Methode, sondern um eine ganze handvoll eigener Methoden. Ich fände es vom Code-Design her sehr schick, eine Subklasse von WebView zu erzeugen. Mehr auch nicht.
Ich werde jetzt einfach die WebView-Node in eine andere Klasse packen und die Sache hat sich :).
 

Thallius

Top Contributor
Könnte es sein, dass Du gerade vorhast MVC zu verletzen? die Webview ist eine View. Hier findet nur eine Ausgabe statt. Diese kannst du nicht mit eigenen Methoden erweitern, da die Webview das nicht zuläßt. Mir erscheint Du willst gerade einen Controller erstellen, der die Webview Inhalte steuert. Der hat aber so oder so nichts in der Webvieh Klasse verloren.

Gruß

Claus
 

dzim

Top Contributor
Ah. Verstehe. Aber das "in eine andere Klasse verpacken" ist auch eher Swing-Style... Finde ich. Es gibt natürlich Situationen, wo das sicher sinnvoll ist, aber meist würde ich darauf verzichten und stattdessen eher Den WebView Teil (auch als alleinigen Teil) eines FXMLs sein lassen. Und alle Operationen auf dem WebView in dem zugehörigen Controller verlagern. Eventuell mit den noch notwendigen GUI-Elementen ringsherum.
Beispiel: Ich brauchte einmal einen minimalistischen Browser:

HTML:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.effect.DropShadow?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.paint.Color?>
<?import javafx.scene.web.WebView?>
<?scenebuilder-preview-i18n-resource ../../res/strings.properties?>
<?scenebuilder-stylesheet ../../res/application.css?>

<BorderPane xmlns:fx="http://javafx.com/fxml" fx:id="parent" fx:controller="my.package.WebContainerController">

    <effect>
        <DropShadow blurType="GAUSSIAN" height="25" width="40" radius="15" spread="0.1">
            <color><Color fx:constant="BLACK" /></color>
        </DropShadow>
    </effect>
    <top>
        <ToolBar fx:id="tbTop" style="-fx-border-radius: 5px 5px 0px 0px; -fx-background-radius: 5px 5px 0px 0px;" BorderPane.alignment="CENTER_LEFT">
            <HBox alignment="CENTER_LEFT">
                <Button fx:id="buttonBack" style="-fx-border-radius: 50 0 0 50; -fx-background-radius: 50 0 0 50; " contentDisplay="GRAPHIC_ONLY" text="Back" onAction="#onBack">
                    <graphic>
                        <ImageView fitWidth="20" fitHeight="20">
                            <image>
                                <Image url="@../../res/ic_menu_back.png"/>
                            </image>
                        </ImageView>
                    </graphic>
                </Button>
                <Button fx:id="buttonForward" style="-fx-border-radius: 0 50 50 0; -fx-background-radius: 0 50 50 0;" contentDisplay="GRAPHIC_ONLY" text="Forward" onAction="#onForward">
                    <graphic>
                        <ImageView fitWidth="20" fitHeight="20">
                            <image>
                                <Image url="@../../res/ic_menu_forward.png"/>
                            </image>
                        </ImageView>
                    </graphic>
                </Button>
                <Button fx:id="buttonReloadStop" style="-fx-border-radius: 50 50 50 50; -fx-background-radius: 50 50 50 50;" contentDisplay="GRAPHIC_ONLY" text="Reload/Stop" onAction="#onReloadStop">
                    <graphic>
                        <ImageView fitWidth="20" fitHeight="20">
                            <image>
                                <Image url="@../../res/ic_menu_rotate.png"/>
                            </image>
                        </ImageView>
                    </graphic>
                    <HBox.margin>
                        <Insets left="10"/>
                    </HBox.margin>
                </Button>
            </HBox>
            <Region fx:id="spacer"/>
            <HBox alignment="CENTER_LEFT">
                <Button fx:id="buttonBrowser" style="-fx-border-radius: 50 0 0 50; -fx-background-radius: 50 0 0 50; " contentDisplay="GRAPHIC_ONLY" text="Browser" onAction="#onBrowser">
                    <graphic>
                        <ImageView fitWidth="20" fitHeight="20">
                            <image>
                                <Image url="@../../res/ic_menu_view.png"/>
                            </image>
                        </ImageView>
                    </graphic>
                </Button>
                <Button fx:id="buttonClose" style="-fx-border-radius: 0 50 50 0; -fx-background-radius: 0 50 50 0;" contentDisplay="GRAPHIC_ONLY" text="Back" onAction="#onClose">
                    <graphic>
                        <ImageView fitWidth="20" fitHeight="20">
                            <image>
                                <Image url="@../../res/ic_menu_home.png"/>
                            </image>
                        </ImageView>
                    </graphic>
                </Button>
            </HBox>
            <BorderPane.margin>
                <Insets left="5"/>
            </BorderPane.margin>
        </ToolBar>
    </top>
    <center>
        <StackPane>
            <WebView fx:id="webView">
                <StackPane.margin>
                    <Insets left="5"/>
                </StackPane.margin>
            </WebView>
            <ProgressBar fx:id="progressBar" progress="0" minHeight="10" maxHeight="10" StackPane.alignment="TOP_CENTER">
                <maxWidth><Double fx:constant="MAX_VALUE" /></maxWidth>
                <StackPane.margin>
                    <Insets left="5"/>
                </StackPane.margin>
            </ProgressBar>
        </StackPane>
    </center>
    <bottom>
        <ToolBar fx:id="tbBotton" style="-fx-border-radius: 0px 0px 5px 5px; -fx-background-radius: 0px 0px 5px 5px;" BorderPane.alignment="CENTER_LEFT">
            <Label fx:id="labelStatus"/>
            <BorderPane.margin>
                <Insets left="5"/>
            </BorderPane.margin>
        </ToolBar>
    </bottom>
</BorderPane>

Der Controller sah dann z.B. so aus:
Java:
public class WebContainerController {
   
    @FXML
    private BorderPane parent;
    @FXML
    private ToolBar tbTop;
    @FXML
    private Button buttonBack;
    @FXML
    private Button buttonForward;
    @FXML
    private Button buttonReloadStop;
    @FXML
    private Region spacer;
    @FXML
    private Button buttonBrowser;
    @FXML
    private Button buttonClose;
    @FXML
    private WebView webView;
    @FXML
    private ProgressBar progressBar;
    @FXML
    private ToolBar tbBotton;
    @FXML
    private Label labelStatus;
   
    private HostServices hostServices = null;
   
    public void setHostServices(HostServices hostServices) {
        this.hostServices = hostServices;
    }
   
    private Window window;
   
    public void setWindow(Window window) {
        this.window = window;
    }
   
    private String url;
   
    public void setUrl(String url) {
        this.url = url;
        if (webEngine != null) {
            webEngine.load(url);
        }
    }
   
    public String getUrl() {
        return url;
    }
   
    private WebEngine webEngine;
   
    private ImageView ivStop = null;
    private ImageView ivReload = null;
   
    private EventListener<?> prefsEventListener = null;
   
    @FXML
    public void initialize() {
       
        // by standards I know now: far too complicated...

        if (prefsEventListener == null) {
            prefsEventListener = new InternalPreferenceEventListener();
            TypedEventDispatcher.getInstance().addListener(prefsEventListener, Preferences.EventName.INTERNAL_EVENT_LANGUAGE);
        }
       
        HBox.setHgrow(spacer, Priority.ALWAYS);
       
        webEngine = webView.getEngine();
        UIUtils.updateWebEngine(window, webEngine, true, false);
       
        ivStop = ImageResource.getImageView(ImageType.WEB_STOP);
        ivStop.setFitHeight(20);
        ivStop.setFitWidth(20);
        ivReload = ImageResource.getImageView(ImageType.WEB_RELOAD);
        ivReload.setFitHeight(20);
        ivReload.setFitWidth(20);
       
        progressBar.setVisible(false);
        progressBar.setManaged(false);
       
        webView.setContextMenuEnabled(false);
       
        webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
            @Override
            public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
                if (newValue == State.SUCCEEDED) {
                    buttonReloadStop.setGraphic(ivReload);
                    buttonReloadStop.setTooltip(new Tooltip(StringResource.getString("web.button.relaod")));
                    progressBar.setVisible(false);
                    progressBar.setManaged(false);
                } else {
                    buttonReloadStop.setGraphic(ivStop);
                    buttonReloadStop.setTooltip(new Tooltip(StringResource.getString("web.button.stop")));
                    progressBar.setVisible(true);
                    progressBar.setManaged(true);
                }
            }
        });
        webEngine.getLoadWorker().progressProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                progressBar.setProgress(newValue.doubleValue());
            }
        });
        webEngine.locationProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                if (newValue.toLowerCase(Locale.ENGLISH).startsWith("mailto")) {
                    hostServices.showDocument(newValue);
                    webEngine.load(oldValue);
                }
            }
        });
       
        // some more init stuff
    }
   
    @FXML
    public void onBack(ActionEvent event) {
        WebHistory history = webEngine.getHistory();
        if (history.getEntries().size() == 0)
            return;
        int index = history.getCurrentIndex();
        if (index > 0)
            history.go(-1);
    }
   
    @FXML
    public void onForward(ActionEvent event) {
        WebHistory history = webEngine.getHistory();
        if (history.getEntries().size() == 0)
            return;
        int index = history.getCurrentIndex();
        if (index < (history.getEntries().size() - 1))
            history.go(+1);
    }
   
    @FXML
    public void onReloadStop(ActionEvent event) {
        if (webEngine.getLoadWorker().isRunning()) {
            webEngine.getLoadWorker().cancel();
        } else {
            webEngine.reload();
        }
    }
   
    @FXML
    public void onBrowser(ActionEvent event) {
        hostServices.showDocument(webEngine.getLocation());
    }
   
    @FXML
    public void onClose(ActionEvent event) {
        // hide this overlay
    }
}

Das Ganze ist ein Overlay in einem Programm, das auf Wunsch spezielle vordefinierte Webseiten anzeigt (mit #setUrl() wird das gesteuert).
Etwas zu kompliziert - heute würde ich es evtl etwas kompakter schreiben, aber seinerzeit un mit Java7 (also JavaFX 2.2) war's ok. Und es tut auch heute noch gut seinen Dienst.
 

dzim

Top Contributor
Ja, Thalius, das dachte ich auch schon so. Daher mein etwas langatmiger Post...

#edit: Hab überlegt, ob ich noch etwas auf MVC eingehe, aber hatte IMHO schon genug Zeug reingenommen :-D
 

Ähnliche Java Themen

Neue Themen


Oben