I've written a small javafx app that animates a square moving from the top-left corner to bottom-right. It then reverses the animation and runs it continuously. On my pixel 4 (xxhdpi) the square leaves behind a trail of edges on the return trip. This does not happen on on my Nexus 7 2013 (xhdpi) or on my desktop.
Tried both the gluon plugin and also the gluon-vm plugin.
Seems related to screen pixel density . . . how do you prevent the ghosting artifacts on dense screens? Image and code below.
Pixel 4 screenshot:
Nexus 2013 Screenshot:
And the app:
public class StockJavaFx extends Application {
@Override
public void start(Stage primaryStage) {
Dimension2D dimension = Services.get(DisplayService.class)
.map(DisplayService::getDefaultDimensions)
.orElseThrow(() -> new NullPointerException("DisplayService"));
Rectangle rectangle = new Rectangle(75, 75);
Pane container = new Pane();
container.getChildren().add(new Rectangle(dimension.getWidth(), dimension.getHeight(), Color.DARKSLATEGRAY));
container.getChildren().add(rectangle);
Scene scene = new Scene(container);
primaryStage.setScene(scene);
TranslateTransition tt = new TranslateTransition(Duration.millis(750), rectangle);
tt.setFromX(0);
tt.setToX(dimension.getWidth() - 75);
tt.setFromY(0);
tt.setToY(dimension.getHeight() - 75);
tt.setCycleCount(Animation.INDEFINITE);
tt.setAutoReverse(true);
FillTransition ft = new FillTransition(Duration.millis(750), rectangle);
ft.setFromValue(Color.ORANGERED);
ft.setToValue(Color.CADETBLUE);
ft.setCycleCount(Animation.INDEFINITE);
ft.setAutoReverse(true);
tt.play();
ft.play();
primaryStage.show();
}
}
The old Gluon jfxmobile
plugin is more or less EOL, and it's being replaced by the new Gluon Client plugin. More details can be found here and here. Detailed documentation can be found here.
This is how you can try creating an Android app that will solve the "ghosting" issue, with some extra "small" benefits, like using Java and JavaFX 11+, GraalVM, and getting a much more performant app. Note that the client plugin for Android is still under heavy development and it's not ready for production yet.
Before you get started, please check that you follow the prerequisites here:
Set GRAALVM_HOME
environment variable to the GraalVM installation directory, like:
export GRAALVM_HOME=/opt/graalvm-svm-linux-20.1.0-ea+28
export JAVA_HOME=$GRAALVM_HOME
You can modify one of the existing samples, like HelloGluon.
You can modify the pom to use the latest versions like:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gluonhq.hello</groupId>
<artifactId>hellogluon</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>hellogluon</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>11</maven.compiler.release>
<javafx.version>14.0.1</javafx.version>
<attach.version>4.0.7</attach.version>
<client.plugin.version>0.1.22</client.plugin.version>
<mainClassName>com.gluonhq.hello.HelloGluon</mainClassName>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq</groupId>
<artifactId>charm-glisten</artifactId>
<version>6.0.4</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>display</artifactId>
<version>${attach.version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>lifecycle</artifactId>
<version>${attach.version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>statusbar</artifactId>
<version>${attach.version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>storage</artifactId>
<version>${attach.version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>util</artifactId>
<version>${attach.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>Gluon</id>
<url>https://nexus.gluonhq.com/nexus/content/repositories/releases</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>${maven.compiler.release}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.4</version>
<configuration>
<mainClass>${mainClassName}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>client-maven-plugin</artifactId>
<version>${client.plugin.version}</version>
<configuration>
<target>${client.target}</target>
<attachList>
<list>display</list>
<list>lifecycle</list>
<list>statusbar</list>
<list>storage</list>
</attachList>
<mainClass>${mainClassName}</mainClass>
</configuration>
</plugin>
</plugins>
<profiles>
<profile>
<id>desktop</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<client.target>host</client.target>
</properties>
<dependencies>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>display</artifactId>
<version>${attach.version}</version>
<classifier>desktop</classifier>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>lifecycle</artifactId>
<version>${attach.version}</version>
<classifier>desktop</classifier>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>storage</artifactId>
<version>${attach.version}</version>
<classifier>desktop</classifier>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>ios</id>
<properties>
<client.target>ios</client.target>
</properties>
</profile>
<profile>
<id>android</id>
<properties>
<client.target>android</client.target>
</properties>
</profile>
</profiles>
</build>
</project>
Now you can modify the main view to add your transition:
public class HelloGluon extends MobileApplication {
@Override
public void init() {
addViewFactory(HOME_VIEW, () -> {
Rectangle rectangle = new Rectangle(75, 75, Color.DARKSLATEGRAY);
Pane container = new Pane(rectangle);
container.setStyle("-fx-background-color: darkslategray");
return new View(container) {
@Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
appBar.setTitleText("Gluon Mobile");
appBar.getActionItems().add(MaterialDesignIcon.PLAY_ARROW.button(e -> {
TranslateTransition tt = new TranslateTransition(Duration.millis(750), rectangle);
tt.setFromX(0);
tt.setToX(getWidth() - 75);
tt.setFromY(0);
tt.setToY(getHeight() - 75);
tt.setCycleCount(Animation.INDEFINITE);
tt.setAutoReverse(true);
FillTransition ft = new FillTransition(Duration.millis(750), rectangle);
ft.setFromValue(Color.ORANGERED);
ft.setToValue(Color.CADETBLUE);
ft.setCycleCount(Animation.INDEFINITE);
ft.setAutoReverse(true);
tt.play();
ft.play();
}));
}
};
});
}
@Override
public void postInit(Scene scene) {
Swatch.TEAL.assignTo(scene);
scene.getStylesheets().add(HelloGluon.class.getResource("styles.css").toExternalForm());
if (Platform.isDesktop()) {
Dimension2D dimension2D = DisplayService.create()
.map(DisplayService::getDefaultDimensions)
.orElse(new Dimension2D(640, 480));
scene.getWindow().setWidth(dimension2D.getWidth());
scene.getWindow().setHeight(dimension2D.getHeight());
}
}
public static void main(String[] args) {
launch();
}
}
You can now run with your regular JDK on your machine:
mvn clean javafx:run
and verify that works fine.
If that is the case, you can now create a native image with GraalVM, also on your machine:
mvn clean client:build
This is a lengthy process, that requires usually 16 GB RAM, and a few minutes.
Once finished successfully, you can run it:
mvn client:run
It should work as expected:
Finally, you can try to build an Android native image:
mvn -Pandroid client:build
When finished, create the apk:
mvn -Pandroid client:package
It will create an apk under target/client/aarch64-android/gvm/apk/bin/hellogluon.apk
.
Plug a device, to install and run:
mvn -Pandroid client:install client:run
Note: by default, icon assets and AndroidManifest are generated at target/client/aarch64-android/gensrc/android
. If you want to modify either of them, you have to copy the content of this folder to src/android
.