1

I already started an attempt to get some information about this issue (see here), but this time, I've create an example application to showcase my issue (hopefully in a better way, then the last time).

Before I get startet, here is the link to the repository with the application: https://github.com/bgmf/example

So lets start with the code. Here's my Gradle file

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.2.0'
    }
}

apply plugin: 'org.javafxports.jfxmobile'

repositories {
    jcenter()
    maven {
        url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
    }
}

mainClassName = 'eu.dzim.example.Main'

dependencies {
    compile 'com.gluonhq:charm:4.2.0'
    compile 'org.controlsfx:controlsfx:8.40.12'
    compile 'de.jensd:fontawesomefx-commons:8.13'
    compile 'de.jensd:fontawesomefx-fontawesome:4.7.0'
    compile 'de.jensd:fontawesomefx-materialdesignfont:1.7.22'
    compile 'com.fasterxml.jackson.core:jackson-databind:2.8.4'
    compileNoRetrolambda 'com.airhacks:afterburner.mfx:1.6.2'
}

jfxmobile {
    downConfig {
        version = '3.1.0'
        plugins 'display', 'lifecycle', 'statusbar', 'storage', 'settings'
    }
    android {
        manifest = 'src/android/AndroidManifest.xml'
        compileSdkVersion = 22
        minSdkVersion = 19
        targetSdkVersion = 22
        dexOptions {
            javaMaxHeapSize '2g'
        }
        packagingOptions {
            pickFirst 'META-INF/LICENSE'
            pickFirst 'META-INF/NOTICE'
            pickFirst 'license/LICENSE.txt'
        }
    }
}

I used the Gluon plugins method to create an app with a single view. But because of very... special... design requirements I needed to ditch the Material Design inspired UI, Gluon provides. Unfortunately. So I need a simple javafx.application.Application class and set the icon, the CSS and the size.

There is a small singleton instance of a app model. Why? I'm - unfortunately - required to provide a technique to change the text size throughout the app. The model holds the reference to the current size and a Utils class is used to change it for the specified Nodes (which is both more or less accurate and more or less slow).

The most crucial part is, that there is a component to swipe through pages and each page is loaded on demand from the resources or file system (whatever is available). The swiping works like a charm, but the loading and rendering takes a relativly huge time (device dependent!) from 3 to 7 seconds (~2s to load the file from the system and to process it via the FXMLLoader; the rest of the time the UI freeze (see the progress indicator) until it is displayed)! Even the demo application takes that much time. IM'm doing nothing to fancy, so here is the loading mechanism of the demo app:

package eu.dzim.example.ui;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

import eu.dzim.example.model.ApplicationModel;
import eu.dzim.example.util.DualAcceptor;
import eu.dzim.example.util.Utils;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;

public class RootController {

    private static final Logger LOG = Logger.getLogger(RootController.class.getName());

    @FXML private StackPane content;
    @FXML private ScrollPane scrollContent;
    @FXML private VBox contentBox;
    @FXML private ProgressIndicator progress;
    @FXML private Button showContent;
    @FXML private Button showTextSize;

    private boolean contentLoaded = false;;

    private List<CollapsibleItemPane> panes = new ArrayList<>();
    private List<Node> resizableNodes = new ArrayList<>();

    private ChangeListener<Number> onContentTextSizeChange = this::handleContentTextSizeChanged;

    private DualAcceptor<CollapsibleItemButton, Boolean> collapsibleItemAction = this::handleCollapsibleItemAction;

    private ExecutorService executor = Executors.newSingleThreadExecutor();

    @FXML
    public void initialize() {

        ApplicationModel.getInstance().textSizeProperty().addListener(onContentTextSizeChange);

        progress.managedProperty().bind(progress.visibleProperty());

        showContent.setOnAction(e -> showContent());
        showTextSize.setOnAction(e -> switchThroughTextSize());
    }

    private void showContent() {
        if (contentLoaded)
            return;
        progress.setVisible(true);
        Task<Pane> task = new Task<Pane>() {
            @Override
            protected Pane call() throws Exception {
                FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ObjectRestauration.fxml"));
                return loader.load();
            }
        };
        task.setOnSucceeded(value -> {
            Pane result = (Pane) value.getSource().getValue();
            if (result == null)
                return;

            for (CollapsibleItemPane pane : panes) {
                pane.setOnActionAcceptor(null);
            }
            contentBox.getChildren().add(result);
            panes = getCollapsibleItemPanesFromContent();
            for (CollapsibleItemPane pane : panes) {
                pane.setOnActionAcceptor(collapsibleItemAction);
            }

            resizableNodes.clear();
            resizableNodes.addAll(Utils.getAllResizableNodes(contentBox));
            handleContentTextSizeChanged(ApplicationModel.getInstance().textSizeProperty(), null, ApplicationModel.getInstance().getTextSize());
            progress.setVisible(false);
            contentLoaded = true;
            executor.shutdownNow();
        });
        task.setOnFailed(value -> {
            Throwable t = value.getSource().getException();
            LOG.log(Level.SEVERE, t.getMessage(), t);
            progress.setVisible(false);
        });
        executor.submit(task);
    }

    private void switchThroughTextSize() {
        switch (ApplicationModel.getInstance().getTextSize()) {
        case Utils.TEXT_SIZE_SMALL:
            ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_DEFAULT);
        case Utils.TEXT_SIZE_DEFAULT:
            ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_LARGE);
        case Utils.TEXT_SIZE_LARGE:
            ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_SMALL);
        default:
            ApplicationModel.getInstance().setTextSize(Utils.TEXT_SIZE_DEFAULT);
        }
    }

    private List<CollapsibleItemPane> getCollapsibleItemPanesFromContent() {
        List<CollapsibleItemPane> panes = new ArrayList<>();
        // only collect first OR second tier panes
        for (Node node : content.getChildren()) {
            if (node instanceof CollapsibleItemPane)
                panes.add((CollapsibleItemPane) node);
            else if (node instanceof Pane) {
                for (Node child : ((Pane) node).getChildren()) {
                    if (child instanceof CollapsibleItemPane)
                        panes.add((CollapsibleItemPane) child);
                }
            }
        }
        return panes;
    }

    private void handleContentTextSizeChanged(ObservableValue<? extends Number> obs, Number o, Number n) {
        new Thread(() -> {
            Utils.handleTextSizeChange(ApplicationModel.getInstance().getTextSize(), null, resizableNodes.toArray(new Node[0]));
        }).start();
    }

    private void handleCollapsibleItemAction(CollapsibleItemButton source, Boolean visible) {
        if (!visible)
            return;
        Node parent = source.getParent();
        for (CollapsibleItemPane pane : panes) {
            if (parent == pane)
                continue;
            pane.getCollapsibleButton().hideContent();
        }
    }
}

The FXML in question looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
    Do not edit this file it is generated by e(fx)clipse from ../src/main/resources/fxml/ObjectRestauration.fxgraph
-->

<?import java.lang.*?>
<?import eu.dzim.example.ui.CollapsibleItemPane?>
<?import eu.dzim.example.ui.LineBreakText?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.text.TextFlow?>
<?scenebuilder-stylesheet /style.css?>

<VBox xmlns:fx="http://javafx.com/fxml">

    <CollapsibleItemPane titleText="Damit sie im alten Glanz erstrahlt" titleUserData="content-text-default"> 
        <titleStyleClass>
            <String fx:value="content-text-default" />
        </titleStyleClass>
        <content>
            <VBox fx:id="content" managed="false" visible="false" spacing="3"> 
                <TextFlow lineSpacing="3"> 
                    <Text userData="content-text-small" text="Die Restaurierung der Klosterkirche St. Martin ist dringend notwendig: Nur so können wir das Kulturgut nationaler Bedeutung und seinen hohen kunsthistorischen Wert erhalten."> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                        <image>
                            <Image url="@img/kloster_werbung.jpg"/> 
                        </image>
                    </ImageView>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Die Südfassade musste eingerüstet werden, um Besucher und Mönche vor den Folgen von Frostschäden und Fassadenrissen zu schützen."> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="1400 Jahre Kloster Disentis: Kulturgut von nationaler Bedeutung."> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Das wohl älteste, noch lebendige Benediktinerkloster nördlich der Alpen weist eine wechselvolle Geschichte auf. Die grosszügige Barockanlage des Benediktinerklosters beherrscht mit der Klosterkirche und ihren beiden Kuppeltürmen majestätisch die Tal-Ebene von Disentis."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Die Klosterkirche St. Martin ist von hohem kunsthistorischen Wert und ein Kulturgut nationaler Bedeutung. Laut Schweizerischem Kunstführer GSK stellt sie einen der frühen Wandpfeiler-Emporen-Räume in der süddeutschen barocken Kulturregion dar und ist  eine einmalige Architekturleistung Vorarlberger-Schule."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Um dieses einmalige Kulturgut in seiner Pracht zu erhalten, bedarf die Klosterkirche St. Martin dringend einer umfangreichen Restaurierung. In der langen Geschichte der Abtei musste die Kirche etliche Male wieder aufgebaut und erneuert werden. Jedes Mal war es ein Kraftakt. Beachtliche finanzielle Mittel sind auch heute notwendig, um diese grosse Aufgabe der Restaurierung zu leisten."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Letzte Gesamtsanierung der Klosterkirche vor knapp 100 Jahren."> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Die Südfassade mit den beiden Kirchtürmen wurde letztmals im Jahr 1954 renoviert. Zwar bemühte sich die Klostergemeinschaft stets, die Klosteranlage und vor allem die Klosterkirche zu pflegen; der Konvent war für deren aufwändigen Unterhalt dauernd in hohem Masse besorgt. "> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Ihre Unterstützung, ein bedeutendes Bauwerk für die Zukunft zu retten!"> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Die geschätzte Gesamtsumme für die Vorhaben an der Klosterkirche St. Martin samt Umgebung beträgt nach heutigem Kostenvoranschlag rund CHF 15 Mio. Der Klostergemeinschaft ist es nicht möglich, die zusätzlichen Mittel für die Restaurierung der Klosterkirche aus eigener Kraft aufzubringen. Um die Finanzierung der notwendigen Restaurierung zu gewährleisten, startete das Kloster im Jubiläumsjahr 2014 eine breite Sammel-Aktion."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Zurzeit laufen breit angelegte Fundraising-Aktivitäten. Falls deren Ziele erreicht werden, wird mit der Detailplanung und Vorarbeiten im Jahr 2016 begonnen. Während der Jahre 2017 und 2018 ist die Aussen-Restaurierung vorgesehen, überlappend bzw. anschliessend erfolgt während der Jahre 2018 und 2019 die Innen-Restaurierung."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Ihre Spende hilft, die Restaurierung so schnell als möglich zu starten."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                </TextFlow>
            </VBox>
        </content>
    </CollapsibleItemPane>
    <CollapsibleItemPane titleText="Kunsthistorische Werte erhalten"> 
        <content>
            <VBox fx:id="content" managed="false" visible="false" spacing="3"> 
                <TextFlow lineSpacing="3"> 
                    <Text userData="content-text-small" text="Imposante barocke « Kirchenburg »"> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Vom mittelalterlichen Passkloster und seinen drei Kirchen sind die heute sichtbaren Ausgrabungen im Innenhof, der Chor der Marienkirche sowie ausgestellte Funde im Klostermuseum erhalten."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Mit dem Klosterneubau im 17. und 18. Jahrhundert wurde die mittelalterliche Gebäudelandschaft durch eine «imposante barocke Kirchenburg» ersetzt."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                        <image>
                            <Image url="@img/dsc03801.jpg"/> 
                        </image>
                    </ImageView>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Blick auf die Klosteranlage mit Internats-Gymnasium"> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Hoher kunsthistorischer Wert der Klosteranlage und Klosterkirche"> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Die Klosterkirche ist im Schweizerischen Kunstführer GSK («Die Benediktinerabtei Disentis») ausführlich dokumentiert:"> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="«Raum, Kirchenfassade und Kloster bilden inmitten der Bündner Hochgebirgslandschaft einen Stil, den man alpinen Barock nennen möchte» (Oscar Sandner). Die Klosterkirche St. Martin bildet den Ostflügel des eindrücklichen Klosterkomplexes. Sie stellt einen der frühen Wandpfeiler-Emporen-Räume in der süddeutschen barocken Kulturregion dar. Ihre kompakte Ausführung mit der Bildung einer Lichtrahmenschicht an den Längsfassaden ist zudem eine einmalige Architekturleistung der Vorarlberger-Schule."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Die Altarausstattung stammt zum grossen Teil aus der Erbauungszeit und umfasst auch zwei bedeutsame Renaissancealtäre der Vorgängerkirche St. Martin III."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                        <image>
                            <Image url="@img/stiftung_006.png"/> 
                        </image>
                    </ImageView>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Blick in die Klosterkirche"> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                </TextFlow>
            </VBox>
        </content>
    </CollapsibleItemPane>
    <CollapsibleItemPane titleText="Restaurierungs-Bedarf"> 
        <content>
            <VBox fx:id="content" managed="false" visible="false" spacing="3"> 
                <TextFlow lineSpacing="3"> 
                    <Text userData="content-text-small" text="Der Blick trügt – die Restaurierung der Klosterkirche ist dringend notwendig"> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Zwar bemühte sich die Klostergemeinschaft stets, die Klosteranlage und vor allem die Klosterkirche zu pflegen, der Konvent war für deren aufwändigen Unterhalt dauernd in hohem Masse besorgt."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Inzwischen sind die Risse an den Fassaden und im Inneren des Bauwerks sowie weitere Bauschäden unübersehbar. Eine Gesamt-Instandstellung der Klosterkirche St. Martin erscheint deshalb dem Konvent, den Besuchern und allen beteiligten Experten als dringend notwendig."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                        <image>
                            <Image url="@img/patronatskommitee_v5_print-7.jpg"/> 
                        </image>
                    </ImageView>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Deckenfresken von Schimmelpilz befallen"> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                        <image>
                            <Image url="@img/patronatskommitee_v5_print-8.jpg"/> 
                        </image>
                    </ImageView>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Holzwurmschäden an den Altären"> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                        <image>
                            <Image url="@img/patronatskommitee_v5_print-10.jpg"/> 
                        </image>
                    </ImageView>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Staub- und Schmutzschichten, Risse im Gemäuer"> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                        <image>
                            <Image url="@img/patronatskommitee_v5_print-12.jpg"/> 
                        </image>
                    </ImageView>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Frostschäden an der Südfassade"> 
                        <styleClass>
                            <String fx:value="content-text-italic" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                </TextFlow>
            </VBox>
        </content>
    </CollapsibleItemPane>
    <CollapsibleItemPane titleText="Vorhaben 2016-2019"> 
        <content>
            <VBox fx:id="content" managed="false" visible="false" spacing="3"> 
                <TextFlow lineSpacing="3"> 
                    <Text userData="content-text-small" text="Umfangreiche Vorarbeiten für die Restaurierung sind abgeschlossen"> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Als Vorbereitung hierzu sind unter der Leitung des spezialisierten Architekturbüros Schmid Krieger AG aus Luzern, während der Jahre 2006 / 07 und 2013 / 14, umfassende Untersuchungen durchgeführt und Vorzustands-Dokumentationen erstellt worden."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Diese Arbeiten erfolgten in enger Zusammenarbeit mit der Denkmalpflege des Kantons Graubünden."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="In und um das Kirchengebäude erstellten die Experten mit neuesten Techniken eine detaillierte Situationsanalyse. Zudem befassten sie sich intensiv mit den Fachbereichen Baustatik, Bauphysik, Elektroplanung, Lüftung, Heizung, Akustik, Beleuchtung, Gebäudeautomation sowie mit der Konservierung von Fenstern, von Natursteinböden, ebenso mit den Orgeln des rund 300 Jahre alten Baudenkmals."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Realisation Vorhaben 2016 bis 2019"> 
                        <styleClass>
                            <String fx:value="content-text-bold" />
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Dank erfreulicher Fortschritte der Fundraising-Aktivitäten ermunterte der Kanton Graubünden das Kloster, die Restaurierung der havarierten Südfassade der Klosterkirche zeitlich vorzuziehen, d.h. bereits 2016 zu realisieren. Das vorgezogene Projekt beläuft sich auf CHF 2.3 Mio. Daran beteiligen sich der Kanton (Denkmalpflege) sowie der Bund (Bundesamt für Kultur / Denkmalpflege) mit CHF 0.8 Mio. Der Restbetrag ist durch Fundraising-Zusagen abgesichert."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                    <LineBreakText/> 
                    <Text userData="content-text-small" text="Weitere Fundraising-Anstrengungen sind notwendig um in den Jahren 2017 die Ostfassade der Klosterkirche sowie in den Jahren 2018/2019 die Innensanierung durchführen zu können. "> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                </TextFlow>
            </VBox>
        </content>
    </CollapsibleItemPane>
    <CollapsibleItemPane titleText="Fundraising"> 
        <content>
            <VBox fx:id="content" managed="false" visible="false" spacing="3"> 
                <TextFlow lineSpacing="3"> 
                    <Text userData="content-text-small" text="..."> 
                        <styleClass>
                            <String fx:value="content-text-small" />
                        </styleClass>
                    </Text>
                </TextFlow>
            </VBox>
        </content>
    </CollapsibleItemPane>
</VBox>

Note, that the controls CollapsibleItemPane and LineBreakText are simeple custom components using dynamic FXMLs (please refer to the repository for these controls).

Unfortunatly there is no simple way of describing the issue and the GitHub repo is the only way I know of, that someone else might have a look into it. Someone, with hopefully more insight into these issues, that I have...

Thanks in advance, Daniel


EDIT 1: Because on some suggestions via other channels, I changed the Text resizing code, but this has no impact on the load/rendering performance. Removing the style classes from the big FXML file has no effect, as well.


EDIT 2 (Tests on my daily driver Nexus 6): On LogCat I see these warnings

01-13 14:05:23.509: W/art(23324): Class com.sun.javafx.css.StyleManager failed lock verification and will run slower.
01-13 14:05:23.753: W/art(23324): Class javafx.scene.image.Image failed lock verification and will run slower.
01-13 14:05:23.764: W/art(23324): Class com.sun.javafx.iio.ImageStorage failed lock verification and will run slower.
01-13 14:05:23.921: W/linker(23324): /data/app/eu.dzim.example-1/lib/arm/libjavafx_font.so: is missing DT_SONAME will use basename as a replacement: "libjavafx_font.so"
01-13 14:05:23.922: W/System.err(23324): Loading FontFactory com.sun.javafx.font.freetype.FTFactory
01-13 14:05:23.922: W/System.err(23324): Subpixel: enabled
01-13 14:05:23.928: W/linker(23324): /data/app/eu.dzim.example-1/lib/arm/libjavafx_font_freetype.so: is missing DT_SONAME will use basename as a replacement: "libjavafx_font_freetype.so"
01-13 14:05:23.932: W/System.err(23324): Freetype2 Loaded (version 2.5.0)
01-13 14:05:23.932: W/System.err(23324): LCD support Enabled
01-13 14:05:23.934: W/System.err(23324): Temp file created: /data/user/0/eu.dzim.example/cache/+JXF1587292252.tmp
01-13 14:05:23.949: W/art(23324): Class com.sun.javafx.font.PrismFontFile failed lock verification and will run slower.
01-13 14:05:23.994: W/art(23324): Class com.sun.javafx.text.PrismTextLayoutFactory failed lock verification and will run slower.
01-13 14:05:24.006: W/art(23324): Class com.sun.javafx.text.PrismTextLayout failed lock verification and will run slower.
01-13 14:05:24.007: W/System.err(23324): File not found: /system/etc/system_fonts.xml
[... there are more in other log-blocks ...]

Maybe they have something to do with it?

When I press the load-Button, the following log appears

01-13 14:05:35.864: I/System.out(23324): don't add points, primary = -1
01-13 14:05:36.137: W/linker(23324): /data/app/eu.dzim.example-1/lib/arm/libjavafx_iio.so: is missing DT_SONAME will use basename as a replacement: "libjavafx_iio.so"
01-13 14:05:36.273: I/art(23324): Do full code cache collection, code=95KB, data=125KB
01-13 14:05:36.275: I/art(23324): After code cache collection, code=65KB, data=65KB
01-13 14:05:37.055: I/art(23324): Do partial code cache collection, code=96KB, data=124KB
01-13 14:05:37.056: I/art(23324): After code cache collection, code=95KB, data=124KB
01-13 14:05:37.056: I/art(23324): Increasing code cache capacity to 512KB
01-13 14:05:41.627: I/System.out(23324): ES2ResourceFactory: Prism - createStockShader: Texture_Color.frag

You can see the time the phone takes, to display the UI (6s).

Community
  • 1
  • 1
dzim
  • 1,131
  • 2
  • 12
  • 28

1 Answers1

1

I'm able to reproduce your findings on my Nexus 6.

While I don't have a magic solution for you, and this might be not a proper answer, I'll try to add some information that could be of value to you.

First of all, running your app it takes as well 6 seconds to load the fxml file.

So I've run the Android Device Monitor app (Android sdk/tools/monitor) as it helps you running a method profiling on your app (run the monitor, open your app, select the process under Devices tab and click on the Start Method Profiling button, and after a few seconds, click again to stop.

Then open the DDMS perspective and see the result of the profiling:

App profiling

The picture shows that most of the time is employed in the text layout (com.sun.javafx.text.PrismTextLayout.layout) inside the JavaFX application thread.

In order to measure the impact of the TextFlow nodes, I've created a new project and just added all the TextFlow nodes from your ObjectRestauration.fxml file to a VBox inside a ScrollPane to a View.

<View fx:id="secondary" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gluonhq.textflow.views.SecondaryPresenter">
   <center>
      <ScrollPane BorderPane.alignment="CENTER">
         <content>
            <VBox prefWidth="350">
                <TextFlow lineSpacing="3"> 
                    <Text text="Die Restaurierung der Klosterkirche St. Martin ist dringend notwendig: Nur so können wir das Kulturgut nationaler Bedeutung und seinen hohen kunsthistorischen Wert erhalten."> 
                            <font>
                                    <Font size="12"/> 
                            </font>
                            <userData>
                                    <Integer fx:value="12"/> 
                            </userData>
                    </Text>
                    <Text text="&#10;"/> 
                    <ImageView preserveRatio="true" fitHeight="300"> 
                            <image>
                                    <Image url="@img/kloster_werbung.jpg"/> 
                            </image>
                    </ImageView>
...

Running this project, it takes 2-3 seconds to load the view with all the content, and I see time consumed by com.sun.javafx.font.freetype.OSFreetype or com.sun.javafx.iio.jpeg.JPEGImageLoader.

This is unavoidable, as you need to load your content anyhow. But you can see that your custom components take a lot of time to load and manage that content: 3 seconds for content and 3 seconds for custom components.

I'll suggest splitting the FXML content into more smaller ones, given that you want to show them in different collapsible panes, using background threads, cache technics, among other solutions.

Also, removing the Font and UserData assignments to every Text node, might help.

There are also good points in this question about using FXML and reflection. So probably you can also find a way to load content in a different way or also try an FXML compiler option, like this one.

Community
  • 1
  • 1
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • First of all :Thank you very much for the effort, you put into this. I'm no thinking of where I can reduce the complexity - I guess the first thing is making the custom component pure code. I unfortunately need to rely on the Font/userData stuff (the app is required switching the text size at runtime). I need to create the UI via FXML, until I figure out, how to create pure code (which I'm not very good at, since I rely on FXMLs since the early JavaFX 2.1 times). I will test it in my repository and - well give feedback. If I can go down to your timing, I'll accept your answer, ok? ;-) – dzim Jan 14 '17 at 22:56
  • Thank you very much for your input! This was a great help indeed. I've updated the repository with the changes I did to speed up everything. What I did was: __1)__ no FXMLs for the small custom components and __2)__ placeholder node for the content (overwritten, if requested for the first time). I left the text resize (Font and userData properties), since it is a feature, I can't remove. Timing down to approx. 2-3s (like you) and not much delay on first placeholder update. Even the text resize profited a lot of the changes. Much faster as well! Again: __Thank you very much, José!__ – dzim Jan 16 '17 at 12:17
  • 1
    Good to know. As you can see, there is always room for improvement. Things we have for granted on desktop might require a few iterations on mobile to get the expected performance. – José Pereda Jan 16 '17 at 12:21
  • You are absolutely right. I already had some parts improved due to this, but I - obviously - did not think far enough. Cheers, Daniel – dzim Jan 16 '17 at 12:22
  • This may be related to this issue: https://bitbucket.org/javafxports/android/issues/90/unnecessary-creation-of-font_map-for-every If you are capable of building your own JRE, you can try caching and re-using the fontmap and see if it has any effect. – Itai Apr 02 '17 at 17:46