A custom JavaFX web browser (to show stock quotes)

I’m currently doing something completely different, and writing a little custom web browser using Java, JavaFX and its WebView component. I’m using it so I can easily look at stock quotes and charts. I just started on it, and the current UI looks like this:

A custom JavaFX WebView web browser

All I have to do is click on one of the stock symbols on the left, and it loads the proper URL into the WebView on the right.

The current Java/JavaFX code is crappy, but I’m sharing it here anyway in case it can help anyone else:

package test1;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Rectangle2D;

public class StockBrowser1 extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        
        String usersHomeDir = System.getProperty("user.home");
        String canonDataFile = usersHomeDir + "/" + "StockBrowser.data";
        String[] stockSymbols = readFileToStringArray(canonDataFile);
        
        Group root = new Group();
        Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
        double initialWidth = primaryScreenBounds.getWidth()*2/3;
        double initialHeight = primaryScreenBounds.getHeight()*2/3;
        Scene scene = new Scene(root, initialWidth, initialHeight);
        
        // LIST VIEW (URLS)
        ListView<String> listView = new ListView<String>();
        ObservableList<String> data = FXCollections.observableArrayList (stockSymbols);
        listView.setItems(data);
        listView.setPrefWidth(150);
        
        // BROWSER
        final WebView webView = new WebView();
        final WebEngine webEngine = webView.getEngine();

        listView.getSelectionModel().selectedItemProperty().addListener(
            new ChangeListener<String>() {
                public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) {
                    String url = getUrlFromStockSymbol(newValue);
                    webEngine.load(url);
            }
        });
        
        // THESE METHODS DO GET CALLED (THIS RESIZE APPROACH WORKS)
        // https://blog.idrsolutions.com/2012/11/adding-a-window-resize-listener-to-javafx-scene/
        scene.widthProperty().addListener(new ChangeListener<Number>() {
            @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
                webView.setPrefWidth(newSceneWidth.doubleValue()*0.9);
            }
        });
        scene.heightProperty().addListener(new ChangeListener<Number>() {
            @Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
                webView.setPrefHeight(newSceneHeight.doubleValue()*0.98);
            }
        });
        
        // BORDER PANE
        BorderPane borderPane = new BorderPane();
        borderPane.setLeft(listView);
        borderPane.setCenter(webView);
        root.getChildren().add(borderPane);
        
        primaryStage.setTitle("Stocks Browser");
        primaryStage.setScene(scene);
        primaryStage.show();
        
    }

    public static void main(String[] args) {
        launch(args);
    }
    
    private static String getUrlFromStockSymbol(String symbol) {
        // google finance (their urls depend on nasdaq, etc.)
        //return "https://www.google.com/finance?q=NASDAQ%3AAAPL";
//        String url1 = "http://finance.yahoo.com/echarts?s=";
//        String url2 = "+Interactive#symbol=yhoo;range=1y;compare=;indicator=volume;charttype=area;crosshair=on;ohlcvalues=0;logscale=off;source=undefined;";
//        return url1 + symbol.toUpperCase() + url2;
        return "http://finance.yahoo.com/q?s=" + symbol.trim().toUpperCase();
    }

    private String[] readFileToStringArray(String canonFilename) throws IOException {
        FileReader fileReader = new FileReader(canonFilename);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        List<String> lines = new ArrayList<String>();
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
            lines.add(line);
        }
        bufferedReader.close();
        return lines.toArray(new String[lines.size()]);
    }
    
}

I’ll write more about this some time in the future, but for today I just wanted to post this.

One of the good things about the code is that it handles the resizing of the WebView component. I just started with JavaFX and I don’t know why the WebView doesn’t resize itself by default, but that’s something I’ll have to learn over time.

Note 1: This SO article might show the best way(s) to resize a WebView.

Note 2: Having done all of this work, one of the big things I wanted to see is the interactive charts on Yahoo Finance. Unfortunately those don’t work with the JavaFX WebView, or at least they don’t work with them by default.

In the meantime, if you wanted to see how to use a JavaFX WebView, how to get it to resize, and how to use it in a BorderPane, I hope this (crappy) code is helpful.