Animation “ghosting” on xxhdpi Android javafxports application

江枫思渺然 提交于 2021-01-28 22:15:36

问题


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();
    }
}

回答1:


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:

  • Use a Linux machine
  • Download and extract this version of GraalVM.
  • Set GRAALVM_HOME environment variable to the GraalVM installation directory, like:

    export GRAALVM_HOME=/opt/graalvm-svm-linux-20.1.0-ea+28
    
  • Use Java 11, or set 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.



来源:https://stackoverflow.com/questions/61334909/animation-ghosting-on-xxhdpi-android-javafxports-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!